HelloAI
L1 第 6 篇 🐥 难度 🕒 10 分钟

链式法则与反向传播:手算一个 3 层网络

把 L1 前 5 篇的所有数学武器组合起来——让我们手算一遍真实神经网络的梯度。

阿
阿莱
2026/6/19

读到这里,你已经懂:

  • 矩阵乘法(L1-02)
  • 导数与梯度下降(L1-03)
  • 概率与交叉熵(L1-04)
  • 信息论(L1-05)

但还没解决一个关键问题:深度网络几十层、几亿参数,怎么算梯度?

答案:链式法则 + 反向传播。这一篇我们手算一遍一个 3 层网络的完整梯度——读完,你”懂”反向传播了。

第一站:复习链式法则

高中数学:

ddxf(g(x))=f′(g(x))⋅g′(x)\frac{d}{dx} f(g(x)) = f'(g(x)) \cdot g'(x)

翻译:嵌套函数的导数 = 外层导数(在内层值上算的)× 内层导数。

多层嵌套也一样,导数是连乘:

ddxf3(f2(f1(x)))=f3′(f2(f1(x)))⋅f2′(f1(x))⋅f1′(x)\frac{d}{dx} f_3(f_2(f_1(x))) = f_3'(f_2(f_1(x))) \cdot f_2'(f_1(x)) \cdot f_1'(x)

就这一条规则。所有神经网络的梯度都靠它。

第二站:定义一个最小神经网络

让我们造一个最简单的网络:

输入 x ──[W₁]── h ──[ReLU]── a ──[W₂]── y ──[loss]── L
  • 输入 xx 是个数(标量)
  • 第一层:h=W1⋅xh = W_1 \cdot x
  • 激活:a=ReLU(h)=max⁡(0,h)a = \text{ReLU}(h) = \max(0, h)
  • 第二层:y=W2⋅ay = W_2 \cdot a
  • 损失:L=(y−t)2L = (y - t)^2(其中 tt 是目标值)

这是一个完整的 2 层网络,足够展示反向传播。

具体数值:x=2,W1=3,W2=−1,t=5x = 2, W_1 = 3, W_2 = -1, t = 5。

前向计算

h=W1⋅x=3⋅2=6h = W_1 \cdot x = 3 \cdot 2 = 6 a=ReLU(h)=ReLU(6)=6a = \text{ReLU}(h) = \text{ReLU}(6) = 6 y=W2⋅a=−1⋅6=−6y = W_2 \cdot a = -1 \cdot 6 = -6 L=(y−t)2=(−6−5)2=121L = (y - t)^2 = (-6 - 5)^2 = 121

损失 121,挺大。我们要调 W1W_1 和 W2W_2 让它变小。

第三站:求 ∂L/∂W2\partial L / \partial W_2

按链式法则:

∂L∂W2=∂L∂y⋅∂y∂W2\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial W_2}

逐项算:

∂L∂y=2(y−t)=2(−6−5)=−22\frac{\partial L}{\partial y} = 2(y - t) = 2(-6 - 5) = -22 ∂y∂W2=a=6\frac{\partial y}{\partial W_2} = a = 6

所以:

∂L∂W2=−22⋅6=−132\frac{\partial L}{\partial W_2} = -22 \cdot 6 = -132

这就是 W2W_2 的梯度。

第四站:求 ∂L/∂W1\partial L / \partial W_1 —— 链更长

∂L∂W1=∂L∂y⋅∂y∂a⋅∂a∂h⋅∂h∂W1\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial a} \cdot \frac{\partial a}{\partial h} \cdot \frac{\partial h}{\partial W_1}

四个因子,逐项算:

∂L∂y=−22 (已经算过)\frac{\partial L}{\partial y} = -22 \text{ (已经算过)} ∂y∂a=W2=−1\frac{\partial y}{\partial a} = W_2 = -1 ∂a∂h={1,h>00,h≤0\frac{\partial a}{\partial h} = \begin{cases} 1, & h > 0 \\ 0, & h \le 0 \end{cases}

我们的 h=6>0h = 6 > 0,所以 ∂a/∂h=1\partial a / \partial h = 1。

∂h∂W1=x=2\frac{\partial h}{\partial W_1} = x = 2

把它们乘起来:

∂L∂W1=−22⋅(−1)⋅1⋅2=44\frac{\partial L}{\partial W_1} = -22 \cdot (-1) \cdot 1 \cdot 2 = 44

这就是 W1W_1 的梯度。

第五站:参数更新

学习率 α=0.01\alpha = 0.01,梯度下降:

W2new=W2−α⋅∂L∂W2=−1−0.01⋅(−132)=0.32W_2^{new} = W_2 - \alpha \cdot \frac{\partial L}{\partial W_2} = -1 - 0.01 \cdot (-132) = 0.32 W1new=W1−α⋅∂L∂W1=3−0.01⋅44=2.56W_1^{new} = W_1 - \alpha \cdot \frac{\partial L}{\partial W_1} = 3 - 0.01 \cdot 44 = 2.56

让我们用新参数再跑一遍前向:

h=2.56⋅2=5.12h = 2.56 \cdot 2 = 5.12 a=5.12y=0.32⋅5.12=1.64a = 5.12 \quad y = 0.32 \cdot 5.12 = 1.64 L=(1.64−5)2=11.29L = (1.64 - 5)^2 = 11.29

损失从 121 降到 11.29——一步迭代就改善了 10 倍!

继续迭代会让损失越来越小,最终找到能使 y=5y = 5 的 W1,W2W_1, W_2。

💡 这就是反向传播的全部
  1. 前向:从输入算到损失
  2. 反向:从损失反着算每个参数的梯度(用链式法则)
  3. 更新:参数 ← 参数 − lr × 梯度
  4. 重复

就这三件事。 GPT-3 用 1750 亿参数训练时,做的也是这三件事。

第六站:为什么”反向”

观察上面 ∂L/∂W1\partial L / \partial W_1 的计算,它包含了 ∂L/∂W2\partial L / \partial W_2 计算过的项——具体是 ∂L/∂y=−22\partial L / \partial y = -22。

如果我们从后往前算:

  1. 先算 ∂L/∂y\partial L / \partial y(用一次)
  2. 用它算 ∂L/∂W2\partial L / \partial W_2(直接乘 aa)
  3. 用它算 ∂L/∂a\partial L / \partial a(乘 W2W_2)
  4. 用它算 ∂L/∂h\partial L / \partial h(乘 ReLU 导数)
  5. 用它算 ∂L/∂W1\partial L / \partial W_1(乘 xx)

每一步只多算一次乘法,重复利用之前的结果。

这就是反向传播的精髓:从后往前传梯度,重复利用中间结果。如果从前往后算,会重复计算很多东西——效率低 N 倍。

第七站:扩展到大网络

让我们看一个更现实的设定。前向:

h1=W1x+b1h_1 = W_1 x + b_1 a1=ReLU(h1)a_1 = \text{ReLU}(h_1) h2=W2a1+b2h_2 = W_2 a_1 + b_2 a2=ReLU(h2)a_2 = \text{ReLU}(h_2) y=W3a2+b3y = W_3 a_2 + b_3 L=cross_entropy(y,t)L = \text{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 分钟从零到能写函数》

📐 完整推导(可点开)

📐 推导:MSE loss 对线性层权重的梯度 点击展开

设线性层:y^=w⋅x+b\hat{y} = w \cdot x + b,MSE loss L=12(y^−y)2L = \frac{1}{2}(\hat{y} - y)^2。

  1. 应用链式法则: ∂L∂w=∂L∂y^⋅∂y^∂w\frac{\partial L}{\partial w} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial w}
  2. 算两项: ∂L∂y^=y^−y;∂y^∂w=x\frac{\partial L}{\partial \hat{y}} = \hat{y} - y \quad ; \quad \frac{\partial \hat{y}}{\partial w} = x
  3. 乘起来: ∂L∂w=(y^−y)⋅x□\frac{\partial L}{\partial w} = (\hat{y} - y) \cdot x \quad \square

SGD 更新规则: w←w−η⋅(y^−y)⋅xw \leftarrow w - \eta \cdot (\hat{y} - y) \cdot x

为什么 deep learning 跑得动: 即使 100 层网络,每层都只需要「上游梯度 × 本地导数」—— 每层计算量是 O(params)O(\text{params}),不是 O(layers2)O(\text{layers}^2)。

📐 推导:反向传播的矩阵形式(3 个核心公式) 点击展开

神经网络第 ℓ\ell 层:hℓ=σ(Wℓhℓ−1+bℓ)h^{\ell} = \sigma(W^{\ell} h^{\ell-1} + b^{\ell})。

设 zℓ=Wℓhℓ−1+bℓz^{\ell} = W^{\ell} h^{\ell-1} + b^{\ell},上游传来的梯度 δℓ=∂L∂zℓ\delta^{\ell} = \frac{\partial L}{\partial z^{\ell}}。

  1. 本层权重梯度: ∂L∂Wℓ=δℓ(hℓ−1)T\frac{\partial L}{\partial W^{\ell}} = \delta^{\ell} (h^{\ell-1})^T
  2. 本层 bias 梯度: ∂L∂bℓ=δℓ\frac{\partial L}{\partial b^{\ell}} = \delta^{\ell}
  3. 传给前一层的梯度: δℓ−1=(Wℓ)Tδℓ⊙σ′(zℓ−1)\delta^{\ell-1} = (W^{\ell})^T \delta^{\ell} \odot \sigma'(z^{\ell-1})

其中 ⊙\odot 是逐元素乘积。

这 3 个公式就是 PyTorch 内部所有 backward() 的来源—— 所有 deep learning 框架都在自动化这套递推。

🔗 被以下 5 篇文章引用
📬

读到这里说明你认真在学 🎯

订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。

💬

讨论区

· 用 GitHub 账号登录评论
⚠️ Giscus 评论未配置 —— 在 src/components/Comments.astro 顶部填入 仓库 ID 和分类 ID(见组件注释里的配置步骤)。