读到这里,你已经懂:
- 矩阵乘法(L1-02)
- 导数与梯度下降(L1-03)
- 概率与交叉熵(L1-04)
- 信息论(L1-05)
但还没解决一个关键问题:深度网络几十层、几亿参数,怎么算梯度?
答案:链式法则 + 反向传播。这一篇我们手算一遍一个 3 层网络的完整梯度——读完,你”懂”反向传播了。
第一站:复习链式法则
高中数学:
dxdf(g(x))=f′(g(x))⋅g′(x)
翻译:嵌套函数的导数 = 外层导数(在内层值上算的)× 内层导数。
多层嵌套也一样,导数是连乘:
dxdf3(f2(f1(x)))=f3′(f2(f1(x)))⋅f2′(f1(x))⋅f1′(x)
就这一条规则。所有神经网络的梯度都靠它。
第二站:定义一个最小神经网络
让我们造一个最简单的网络:
输入 x ──[W₁]── h ──[ReLU]── a ──[W₂]── y ──[loss]── L
- 输入 x 是个数(标量)
- 第一层:h=W1⋅x
- 激活:a=ReLU(h)=max(0,h)
- 第二层:y=W2⋅a
- 损失:L=(y−t)2(其中 t 是目标值)
这是一个完整的 2 层网络,足够展示反向传播。
具体数值:x=2,W1=3,W2=−1,t=5。
前向计算
h=W1⋅x=3⋅2=6
a=ReLU(h)=ReLU(6)=6
y=W2⋅a=−1⋅6=−6
L=(y−t)2=(−6−5)2=121
损失 121,挺大。我们要调 W1 和 W2 让它变小。
第三站:求 ∂L/∂W2
按链式法则:
∂W2∂L=∂y∂L⋅∂W2∂y
逐项算:
∂y∂L=2(y−t)=2(−6−5)=−22
∂W2∂y=a=6
所以:
∂W2∂L=−22⋅6=−132
这就是 W2 的梯度。
第四站:求 ∂L/∂W1 —— 链更长
∂W1∂L=∂y∂L⋅∂a∂y⋅∂h∂a⋅∂W1∂h
四个因子,逐项算:
∂y∂L=−22 (已经算过)
∂a∂y=W2=−1
∂h∂a={1,0,h>0h≤0
我们的 h=6>0,所以 ∂a/∂h=1。
∂W1∂h=x=2
把它们乘起来:
∂W1∂L=−22⋅(−1)⋅1⋅2=44
这就是 W1 的梯度。
第五站:参数更新
学习率 α=0.01,梯度下降:
W2new=W2−α⋅∂W2∂L=−1−0.01⋅(−132)=0.32
W1new=W1−α⋅∂W1∂L=3−0.01⋅44=2.56
让我们用新参数再跑一遍前向:
h=2.56⋅2=5.12
a=5.12y=0.32⋅5.12=1.64
L=(1.64−5)2=11.29
损失从 121 降到 11.29——一步迭代就改善了 10 倍!
继续迭代会让损失越来越小,最终找到能使 y=5 的 W1,W2。
💡 这就是反向传播的全部
- 前向:从输入算到损失
- 反向:从损失反着算每个参数的梯度(用链式法则)
- 更新:参数 ← 参数 − lr × 梯度
- 重复
就这三件事。 GPT-3 用 1750 亿参数训练时,做的也是这三件事。
第六站:为什么”反向”
观察上面 ∂L/∂W1 的计算,它包含了 ∂L/∂W2 计算过的项——具体是 ∂L/∂y=−22。
如果我们从后往前算:
- 先算 ∂L/∂y(用一次)
- 用它算 ∂L/∂W2(直接乘 a)
- 用它算 ∂L/∂a(乘 W2)
- 用它算 ∂L/∂h(乘 ReLU 导数)
- 用它算 ∂L/∂W1(乘 x)
每一步只多算一次乘法,重复利用之前的结果。
这就是反向传播的精髓:从后往前传梯度,重复利用中间结果。如果从前往后算,会重复计算很多东西——效率低 N 倍。
第七站:扩展到大网络
让我们看一个更现实的设定。前向:
h1=W1x+b1
a1=ReLU(h1)
h2=W2a1+b2
a2=ReLU(h2)
y=W3a2+b3
L=cross_entropy(y,t)
反向:
# 从后往前
dL_dy = (y - t) / batch # 假设 softmax + 交叉熵
dL_dW3 = a_2 ⊗ dL_dy # ⊗ 是外积
dL_db3 = dL_dy
dL_da2 = W_3.T @ dL_dy
dL_dh2 = dL_da2 * (h_2 > 0) # ReLU 导数
dL_dW2 = a_1 ⊗ dL_dh2
dL_db2 = dL_dh2
dL_da1 = W_2.T @ dL_dh2
dL_dh1 = dL_da1 * (h_1 > 0)
dL_dW1 = x ⊗ dL_dh1
dL_db1 = dL_dh1
每一行都是链式法则的一次应用。整个网络的梯度,就是这样从后往前传出来的。
第八站:PyTorch 自动做这事
我们手算了一遍,但实际工程中没人手算。PyTorch 的 autograd 自动追踪每个运算的导数。
import torch
# 定义参数(requires_grad=True 让 PyTorch 追踪它们)
W1 = torch.tensor(3.0, requires_grad=True)
W2 = torch.tensor(-1.0, requires_grad=True)
# 前向
x = torch.tensor(2.0)
t = torch.tensor(5.0)
h = W1 * x
a = torch.relu(h)
y = W2 * a
loss = (y - t) ** 2
# 反向(一行搞定!)
loss.backward()
print(f"W1.grad = {W1.grad}") # 44.0
print(f"W2.grad = {W2.grad}") # -132.0
和我们手算一模一样——PyTorch 用的就是同一套链式法则。
autograd 是怎么做的
PyTorch 在前向时构建一个计算图——记录每个张量是怎么从其它张量算出来的。backward() 时它沿着图反向遍历,按链式法则累积梯度。
这就是为什么深度学习能”训”得起来:人不用手算梯度,框架自动做。否则 100 层的网络谁也算不出。
第九站:常见问题
梯度消失
如果链条很长(100 层),每层导数都 < 1,连乘后梯度 ≈ 0——参数几乎不更新。这是早期深网络”训不动”的核心原因。
解决:
- ReLU(导数为 0 或 1,不衰减)
- 残差连接(ResNet 的发明,让梯度有”快速通道”回流)
- BatchNorm / LayerNorm
梯度爆炸
反过来,连乘 > 1 的导数会指数爆炸。
解决:
- 梯度裁剪(grad clipping)
- 适当的初始化
计算图太大
对于 LLM 来说,前向时存的中间值会占爆显存。
解决:
- Gradient checkpointing(重算代替存储)
- 混合精度训练
这些都是 L7 系统工程会深入讲的。
一句话总结
反向传播 = 链式法则 + 重复利用中间结果。
它让任意深的神经网络都”训得动”,是深度学习时代的核心算法之一。
你今天看到的所有大模型,每次迭代都在做这件事。
🔬 L1 数学块完结
读完到这里,你已经掌握了 ML 数学的所有核心:
- 线性代数 ✓
- 微积分 + 梯度 ✓
- 概率与最大似然 ✓
- 信息论 ✓
- 反向传播 ✓
后面 L1 几篇是 Python 工具链——让你能把数学写成代码跑起来。
下一篇:《Python 速成(一):30 分钟从零到能写函数》