反向传播详解:链式法则的实战
Backpropagation 让神经网络真的能学。这一篇拆解它的数学、直觉、和实现。
L3-01 我们看到 MLP 怎么把多层神经元堆起来。 这一篇讲:堆起来之后,它怎么”学”。
没有反向传播 —— 神经网络只是个昂贵的随机函数。
一句话定位
反向传播 = 用链式法则把 loss 的梯度从输出层一路传回每一层参数。
每层都拿到自己”应该往哪个方向、走多大步”才能让 loss 下降一点点。 然后跟着梯度下降走 —— 模型变好了。
数学回忆:链式法则
L1-06 讲过链式法则。一句话回顾:
复合函数的导数 = 外层在内层值处的导数 × 内层的导数。
把这个推广到多个变量、多个层 —— 就是反向传播。
一个最小神经网络
考虑 1 个输入、1 个隐藏层(2 个神经元)、1 个输出的小网络:
x ──W1──> h ──W2──> y_pred ──Loss──> L
↑
y_true
参数:(这里忽略 bias 简化) 激活:, 是 sigmoid 预测: 损失:
问题:怎么算 ?
链式法则一层层算
从最外层开始往内传:
每一项是局部导数:
- (loss 对预测的导)
- (线性层对输入的导)
- (激活后对权重的导)
代回去:
就这么简单——每一项都是局部的微分。
“反向”在哪里反向
正向传播(forward):从 走到 ,每层算输出。 反向传播(backward):从 走回 ,每层算梯度。
正向: x → h → y_pred → L
反向: L ← h ← y_pred ← (gradient 累乘回去)
反向这个词来自这个回流方向。
为什么不直接数值微分
数值微分(有限差分)也能算梯度:
为啥不用?
| 数值微分 | 反向传播 | |
|---|---|---|
| 精度 | 取决于 | 解析精确 |
| 每个参数代价 | 一次完整 forward | 共享一次 forward |
| 百万参数模型 | 一百万次 forward 才一个 gradient | 一次 forward + 一次 backward 就够了 |
数值微分理论上对、实际算不起。反向传播让大模型成为可能。
计算图视角
现代深度学习框架(PyTorch、TensorFlow)把模型表示成计算图:
┌──────┐
│ x │
└──┬───┘
│
┌──▼───┐ ┌────┐
│ mul │◄───│ W1 │
└──┬───┘ └────┘
│
┌──▼───┐
│sigma │
└──┬───┘
│
┌──▼───┐ ┌────┐
│ mul │◄───│ W2 │
└──┬───┘ └────┘
│
┌──▼───┐
│ y_p │
└──┬───┘
│
┌──▼───┐ ┌────┐
│ loss │◄───│ y │
└──────┘ └────┘
每个节点知道:
- 它的 forward 公式(用输入算输出)
- 它的 backward 公式(已知 ,算 和 )
整个 backward = 沿图反向遍历,每节点用 chain rule 把梯度往后传。
PyTorch 实战
PyTorch 的 autograd 自动构建计算图、自动反向:
import torch
# 输入和真值
x = torch.tensor(2.0)
y = torch.tensor(1.0)
# 参数 —— requires_grad=True 让 autograd 跟踪
W1 = torch.tensor(0.5, requires_grad=True)
W2 = torch.tensor(0.8, requires_grad=True)
# Forward
h = torch.sigmoid(W1 * x)
y_pred = W2 * h
loss = 0.5 * (y_pred - y) ** 2
print(f"loss = {loss.item():.4f}")
# Backward —— 一行
loss.backward()
# 梯度直接在参数上
print(f"dL/dW1 = {W1.grad:.4f}")
print(f"dL/dW2 = {W2.grad:.4f}")
loss.backward() —— 这一句话内部做了上面整套链式法则。
几个常见坑
1. 梯度累加,不是替换
loss.backward()
# W1.grad 是 0.3
loss.backward()
# W1.grad 是 0.6 —— 累加了!
所以训练循环里一定要:
optimizer.zero_grad() # 清零旧梯度
loss.backward() # 算新梯度
optimizer.step() # 沿梯度更新
忘记 zero_grad() 是新手常犯的 bug。
2. 梯度消失 / 爆炸
链式法则是连乘。如果每层的局部梯度都很小(< 1),层数一多——梯度乘到底就接近 0(消失)。如果都很大(> 1)——梯度爆炸。
历史上:
- Sigmoid + 深层 → 梯度消失 → 训不动深网络
- 解决方案:ReLU 激活、BatchNorm、残差连接(ResNet)、梯度裁剪
L3-03 训练技巧 会详讲。
3. 不可微的操作
argmax、if x > 0、采样等操作没有导数——backprop 走不过。
解决:
- Gumbel-Softmax:连续松弛 argmax
- Straight-Through Estimator:forward 用离散,backward 假装恒等
- Reparameterization(VAE):把随机性外置
反向传播的历史
- 1960s:控制论里就有类似想法
- 1986:Rumelhart、Hinton、Williams 把它系统化用在神经网络
- 2012:AlexNet 用 GPU + backprop 在 ImageNet 杀疯了 —— 深度学习爆发
- 今天:autograd 让每个 PyTorch 用户都”免费”用上
反向传播是 AI 史上最重要的算法之一—— 没有它,“训练神经网络”这件事根本不成立。
反向传播 = 链式法则 + 计算图 + 梯度从后往前传。
学神经网络可以不会推 backward 公式(autograd 帮你算)—— 但得理解它在做什么:每层都拿到一个”你这一步该走多远”的信号。
这个信号的质量(不消失、不爆炸、不被噪声主导)—— 决定了模型能不能学。
下一篇推荐:L3-03 训练技巧(BatchNorm、Dropout、初始化) —— 让 backprop 在深层网络里实际能用。
读到这里说明你认真在学 🎯
订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。
讨论区
· 用 GitHub 账号登录评论src/components/Comments.astro 顶部填入
仓库 ID 和分类 ID(见组件注释里的配置步骤)。