苦猿的大模型日记 · Day03 · 从神经元到反向传播的数学直觉-帮普通人把AI学进简历系列

前言:把神经元想象成一个"小委员"

先做个思维实验。

把一个神经元想象成你公司里一个小委员

每天他要做一道二选一的决策——批准 or 驳回一份提案。

提案送进来,带着一堆"特征":预算、紧急度、风险评分、老板脸色……

每一项都是一票,但票的权重不一样:

  • "老板脸色"是老员工,说话重,权重 0.8
  • "紧急度"是实习生,说话轻,权重 0.2
  • "预算"中等,权重 0.5

委员把所有票加权求和,再叠一个个人偏好(偏置 b)。

总分过阈值?点火!批准! 没过?按住,驳回。

这就是一个神经元——神经网络里最小的决策单元。

但一提到"神经网络",大部分人的第一反应是:黑盒、高深、看不懂。更别提反向传播那串 ∂L/∂w 的偏导公式,劝退率 90%。

但我想告诉你一个反常识的事实:神经网络根本不是黑盒。剥到最底层,它就是初中数学——加权求和、激活函数、链式法则。

这一篇,我把"深度学习"四个字拆到你能讲给小学生听。不堆公式,只讲直觉。读完你能:

  • 一句话讲清神经元在干啥
  • 知道为啥激活函数不能省
  • 看懂前向传播就是一连串矩阵乘
  • 明白反向传播就是链式法则的工业化
  • 知道 Adam 优化器为啥是默认
  • 搞懂 Dropout、BatchNorm、LayerNorm 在干啥

我们开始。


PART 01:神经元——一个最小的决策单元

先给上面那位"小委员"起个学名:MP 神经元(McCulloch-Pitts,1943 年提出,比深度学习这个词早了 70 年)。

它的数学公式长这样:

y = f(w · x + b)

拆开看四个角色:

  • x 是输入向量(那堆特征)
  • w 是权重向量(每张票的权重)
  • b 是偏置(委员的个人偏好)
  • f 是激活函数(点火规则)

用一个具体数字算一遍,你就懂了。

委员要决策"今天要不要跑 AI 模型",三个输入:

  • x1 = GPU 利用率(0~1)
  • x2 = 紧急程度(0~1)
  • x3 = 老板催没催(0 或 1)

权重 w = [0.5, 0.3, 0.8],偏置 b = -0.4。

来一组真实数据 x = [0.2, 0.9, 1](GPU 闲、事急、老板催)。

加权求和:

z = 0.5×0.2 + 0.3×0.9 + 0.8×1 + (-0.4)
  = 0.1 + 0.27 + 0.8 - 0.4
  = 0.77

激活函数 f 暂时设为"z > 0 就点火"。

0.77 > 0,点火!结论:跑模型

这就是一个神经元做的事。一句话:加权求和 + 一个判定

一个神经元能做的事有限。但当你把成千上万个这样的小委员叠成网络——奇迹就出现了。

MP 神经元结构

但在叠网络之前,有个关键问题:那个激活函数 f,凭啥不能直接用 z 本身?


PART 02:激活函数——为啥不能线性到底

最直觉的想法:激活函数省了好不好?直接把 z 输出,多简单?

不行。这是新手最容易踩的坑。

原因一句话:线性叠线性,叠完还是线性

假设有一个 3 层网络,每层都是 y = w·x + b,没有激活函数:

第1层:  y1 = w1·x + b1
第2层:  y2 = w2·y1 + b2
       = w2·(w1·x + b1) + b2
       = (w2·w1)·x + (w2·b1 + b2)
第3层:  y3 = ... 还是 x 的线性组合

三层叠完,等价于一层。叠多少层都没用,网络能学的东西和一层没区别。

激活函数的存在,就是把线性掰成非线性,让网络能学"弯弯绕"的复杂模式。

三大主流激活函数:

Sigmoid:把任意数压到 (0, 1)

σ(x) = 1 / (1 + e^(-x))

输入越大越接近 1,越小越接近 0。早期神经网络主力,现在主要用在二分类输出层(预测概率)。

缺点:两端梯度几乎为 0(饱和),深网络反向传播传不动——这就是著名的"梯度消失"。

Tanh:把任意数压到 (-1, 1)

形状和 Sigmoid 像,但有正有负,收敛比 Sigmoid 快。早期 RNN 常用。

ReLU:最简单粗暴,但赢了

ReLU(x) = max(0, x)

负数全部砍成 0,正数原样输出。一个 max 操作,几乎不耗算力。

为啥 ReLU 赢了?

  1. 算得快:一个 max,比 Sigmoid 的 exp 快几十倍
  2. 不饱和:正数区域梯度恒为 1,深网络不会梯度消失
  3. 稀疏:负数全变 0,网络天然稀疏,自带正则化效果

现代深度学习,隐藏层 90% 用 ReLU,Transformer 里会用它的升级版 GELU。

一句话:隐藏层默认 ReLU,二分类输出层用 Sigmoid

三大激活函数曲线对比


PART 03:前向传播——一层一层往上传

把单个神经元讲清楚了,现在把它们串起来

最经典的结构——两层 MLP(多层感知机):

输入层(784 维)  →  隐藏层(128 个神经元)  →  输出层(10 维)

这是 MNIST 手写数字识别的标准结构:输入是 28×28=784 个像素,输出是 0~9 十个数字的得分。

前向传播(Forward)就是:输入信号一层一层往前传,直到输出预测。

关键操作就是——矩阵乘法

一个神经元要做 w·x + b。128 个神经元呢? 就是 128 套权重,堆起来变成一个矩阵 W。

隐藏层输出 = W1 · x + b1

形状对一下:

  • x 是 [784]
  • W1 是 [128, 784](128 个神经元,每个 784 个权重)
  • b1 是 [128]
  • 输出是 [128]

一句话:矩阵乘 = 一层神经元并行算。这就是 GPU 加速的甜点——矩阵乘 GPU 最在行。

PyTorch 代码长这样:

import torch
import torch.nn as nn

x = torch.randn(1, 784)              # 输入:1 张图,784 个像素

W1 = nn.Linear(784, 128)             # 第一层:784 → 128
h = torch.relu(W1(x))                # 矩阵乘 + 激活

W2 = nn.Linear(128, 10)              # 第二层:128 → 10
y = W2(h)                            # 矩阵乘

print(y.shape)  # torch.Size([1, 10])  ← 10 个数字的得分

这就是前向传播的全部。一堆矩阵乘法串起来,中间夹几个激活函数。

输出 y 就是模型的"预测"——10 个数字里得分最高的那个,就是模型认为这张图是几。

两层 MLP 全连接网络结构

但问题来了:这个预测一开始是错的。随机初始化的 W1、W2,准确率 10%(瞎猜)。

怎么让它变对?这就是反向传播的事。


PART 04:反向传播——链式法则的工业化

反向传播(backpropagation)是整个深度学习的灵魂。它解决一个问题:

网络预测错了,怎么调权重才能让它下次别错?

分三步。

第一步:算"错多少"——损失函数

预测 [0.1, 0.7, 0.05, ...] vs 真实标签 [0, 1, 0, ...](这张图是数字 1)。

差距多大?用一个数表示。最常用交叉熵,简化版:

L = -log(预测[正确类别])

预测越准,L 越小。L = 0 就是完美。

第二步:算"该往哪调"——梯度

我现在的 loss 是 0.8,我想知道每个权重 w 对这个 0.8 贡献了多少。贡献大的多调,贡献小的少调。

这就是导数——loss 对每个 w 的偏导数 ∂L/∂w。

但 w 在网络最前面,loss 在最后面,中间隔了一堆层。怎么算?链式法则

举个例子:三层网络 y = f3(f2(f1(x))),要算 ∂L/∂w1,链式法则告诉你:

∂L/∂w1 = ∂L/∂y · ∂y/∂f2 · ∂f2/∂f1 · ∂f1/∂w1

一连串导数相乘,从输出层一路传到输入层。这就是"反向传播"——前向往前传信号,反向往回传梯度。

PyTorch 的 autograd 就是把这玩意儿自动化:loss.backward() 一行搞定

第三步:更新权重——梯度下降

拿到梯度了,怎么调?往梯度的反方向走一小步:

w = w - lr · ∂L/∂w

lr 是学习率(learning rate),通常 0.001 ~ 0.01。

为啥反方向?梯度指向 loss 增大的方向,你想要 loss 减小,自然反向走。

这三步合起来,就是训练一个 batch 的完整流程:

pred = model(x)              # 1. 前向传播
loss = loss_fn(pred, y)      # 2. 算 loss
optimizer.zero_grad()        # 3. 清空旧梯度(必加)
loss.backward()              # 4. 反向传播算新梯度
optimizer.step()             # 5. 优化器更新权重

注意第 5 步写的是 optimizer.step(),不是手动 w -= lr * grad。为什么?因为现实中没人用裸的梯度下降——大家用的都是"优化器"。

前向反向计算图与链式法则


PART 05:优化器——从 SGD 到 Adam,谁才是默认

裸梯度下降(也叫 SGD)有个致命问题:它在山谷里疯狂震荡

想象 loss 是一个二维地形,你想走到最低点。如果地形是个狭长的山谷:

  • 横向陡,梯度大 → 步子大
  • 纵向缓,梯度小 → 步子小

结果就是你在山谷两侧来回横跳,纵向却走得慢。这就是 SGD 的震荡问题。

怎么解决?三代优化器迭代:

第一代:SGD + Momentum(加惯性)

v = 0.9 · v + ∂L/∂w       # 累积历史梯度
w = w - lr · v             # 用累积梯度更新

直觉:给优化器一个"惯性",让它冲过震荡方向,持续往纵向走。就像滚下山的石头,有惯性就不会被小沟卡住

第二代:RMSProp(自适应学习率)

每个参数单独有自己的学习率
梯度大的参数 → 学习率自动调小
梯度小的参数 → 学习率自动调大

直觉:不同参数用不同步长,狭长山谷里横向步子自动变小,纵向步子自动变大。震荡消失。

第三代:Adam = Momentum + RMSProp

Adam 同时用了两个机制:

  • 动量(Momentum):看历史梯度的累积方向
  • 自适应(RMSProp):每个参数自己调学习率

一句话:Adam 是当前深度学习的默认选项。LLM、CV、推荐系统,90% 的任务直接上 Adam。

为啥 Adam 这么强?

  1. 默认超参就能跑(lr=1e-3, β1=0.9, β2=0.999),新手友好
  2. 收敛快,对学习率不敏感
  3. 自适应,不用手动给不同层调不同 lr

PyTorch 里用 Adam 就一行:

from torch import optim

optimizer = optim.Adam(model.parameters(), lr=1e-3)

新手建议:永远从 Adam + lr=1e-3 开始。不收敛再换 SGD,别反过来。

SGD/Momentum/Adam 在山谷中的轨迹对比


PART 06:正则化与归一化——让训练别"飘"

到这里你已经能训出一个网络了。但真训起来,你会遇到两个新问题:

  1. 训练集上神勇,测试集上一塌糊涂 → 过拟合
  2. 训练 loss 直接 NaN,模型炸了 → 数值不稳定

这两类问题,深度学习准备了两个标准解法:正则化归一化

正则化:治过拟合

过拟合的本质是网络死记硬背训练数据,没学到通用规律。两个主流解法:

Dropout(随机失活)

每次前向传播,随机让一部分神经元"罢工"——输出强制变 0

nn.Dropout(p=0.5)   # 每次训练时 50% 神经元随机失活

直觉:网络不敢依赖任何单个神经元(因为它可能被随机关掉),被迫让所有神经元都学会做事。就像公司不能让一个核心员工独占知识,否则他请假就崩盘——Dropout 强制"知识分散"。

注意:Dropout 只在训练时生效,推理时全部神经元都开。model.train() / model.eval() 切换。

Dropout 训练 vs 推理

L2 正则(权重衰减)

在 loss 里加一项,惩罚权重过大:

L_total = L_original + λ · Σw²

直觉:让权重保持小一点,网络就不会过度拟合训练集里的噪声。PyTorch 里一句话启用:optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)

归一化:治数值不稳

深层网络训练时,每一层的输入分布会随着前面层的权重变化而变化——这叫 Internal Covariate Shift。后果是:梯度爆炸或消失,训练直接崩。

两个主流解法:

BatchNorm(批归一化) —— CV 经典

把每个 batch 的数据强制拉回均值 0、方差 1

nn.BatchNorm1d(128)    # 全连接层用
nn.BatchNorm2d(64)    # 卷积层用

直觉:每一层都对到达的数据做一次"标准化",让数值始终落在温和的区间,梯度就能稳定传。

BatchNorm 有个坑:batch 太小(比如 1 或 2)效果会很差,因为统计量不准。这就是为啥推理时要切 model.eval()——切换成用训练时累计的全局统计量,而不是当前 batch 的。

LayerNorm(层归一化) —— NLP / Transformer 标配

不看 batch 维,对单个样本自己的特征维做归一化

nn.LayerNorm(768)     # Transformer 每个 token 都过一遍

直觉:BatchNorm 是"横向归一化"(看 batch 内不同样本),LayerNorm 是"纵向归一化"(看一个样本自己的特征)。

为啥 NLP/LLM 都用 LayerNorm 不用 BatchNorm?

  • NLP 里序列长度不一,batch 内对不同样本做统计会乱
  • 推理时 batch_size 经常是 1,BatchNorm 直接失效
  • LayerNorm 不依赖 batch,推理时和训练时行为一致

所以你看 Transformer、BERT、GPT、Llama 全都用 LayerNorm。这是从 CV 跨到 NLP 时最容易踩的"反直觉"点。

BatchNorm 横向 vs LayerNorm 纵向

一句话记住:CV 用 BatchNorm,NLP 用 LayerNorm


结尾:把"训练"两个字完整拆开

回头看,深度学习的全部数学,其实就是这几样东西:

  • 神经元 = 加权求和 + 激活
  • 激活函数 = 把线性掰成非线性,ReLU 是默认
  • 前向传播 = 矩阵乘法一层层串起来
  • 反向传播 = 链式法则算每个权重的梯度
  • 优化器 = Adam 默认,带惯性 + 自适应学习率
  • 正则化 = Dropout 防过拟合,L2 控权重
  • 归一化 = CV 用 BatchNorm,NLP/LLM 用 LayerNorm

完整训练循环:

model.train()                                # 切训练模式(启用 Dropout/BatchNorm)
for x, y in dataloader:
    pred = model(x)                          # 1. 前向
    loss = loss_fn(pred, y)                  # 2. 算 loss
    optimizer.zero_grad()                    # 3. 清梯度
    loss.backward()                          # 4. 反向传播
    optimizer.step()                         # 5. 优化器更新

就这 5 行,反复跑几万次,网络就学会了。

没有黑盒,没有玄学。

很多人卡在"深度学习"这四个字上,是因为被那一串偏导公式吓到了。但你拆开看,它就是中学数学的链式法则,加了个"反向遍历"的工业化流程,再配几个让训练别"飘"的小技巧。

下一篇 Day04,我们用 PyTorch 把这套理论落地——从 0 手写一个真实能跑的训练循环,在 MNIST 上训练一个能认数字的网络。

到那时候,你回头看这篇文章,会发现"深度学习"这四个字,其实就 5 行代码。

最后送你一句话:

所有"深度学习"的玄学,拆开都是中学数学。区别只在于——你有没有把它拆开看过。

互动时间:看完这篇,你觉得最难懂的是哪一块?是反向传播的链式法则,还是 Adam 优化器的自适应学习率,还是 BatchNorm 和 LayerNorm 怎么选?评论区告诉我,下一篇我挑高频问题专门补图补例子讲透。


下一篇预告:Day04 | 深度学习基础(2):用 PyTorch 从 0 训练 MNIST 数字识别

关注「苦猿」,和我一起从 0 走到大模型。

— END —

苦猿 · 帮普通人把 AI 学进简历

更多推荐