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

链式法则与反向传播:手算一个 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=W1xh = W_1 \cdot x
  • 激活:a=ReLU(h)=max(0,h)a = \text{ReLU}(h) = \max(0, h)
  • 第二层:y=W2ay = W_2 \cdot a
  • 损失:L=(yt)2L = (y - t)^2(其中 tt 是目标值)

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

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

前向计算

h=W1x=32=6h = W_1 \cdot x = 3 \cdot 2 = 6 a=ReLU(h)=ReLU(6)=6a = \text{ReLU}(h) = \text{ReLU}(6) = 6 y=W2a=16=6y = W_2 \cdot a = -1 \cdot 6 = -6 L=(yt)2=(65)2=121L = (y - t)^2 = (-6 - 5)^2 = 121

损失 121,挺大。我们要调 W1W_1W2W_2 让它变小

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

按链式法则:

LW2=LyyW2\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial W_2}

逐项算:

Ly=2(yt)=2(65)=22\frac{\partial L}{\partial y} = 2(y - t) = 2(-6 - 5) = -22 yW2=a=6\frac{\partial y}{\partial W_2} = a = 6

所以:

LW2=226=132\frac{\partial L}{\partial W_2} = -22 \cdot 6 = -132

这就是 W2W_2 的梯度

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

LW1=LyyaahhW1\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}

四个因子,逐项算:

Ly=22 (已经算过)\frac{\partial L}{\partial y} = -22 \text{ (已经算过)} ya=W2=1\frac{\partial y}{\partial a} = W_2 = -1 ah={1,h>00,h0\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

hW1=x=2\frac{\partial h}{\partial W_1} = x = 2

把它们乘起来:

LW1=22(1)12=44\frac{\partial L}{\partial W_1} = -22 \cdot (-1) \cdot 1 \cdot 2 = 44

这就是 W1W_1 的梯度

第五站:参数更新

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

W2new=W2αLW2=10.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αLW1=30.0144=2.56W_1^{new} = W_1 - \alpha \cdot \frac{\partial L}{\partial W_1} = 3 - 0.01 \cdot 44 = 2.56

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

h=2.562=5.12h = 2.56 \cdot 2 = 5.12 a=5.12y=0.325.12=1.64a = 5.12 \quad y = 0.32 \cdot 5.12 = 1.64 L=(1.645)2=11.29L = (1.64 - 5)^2 = 11.29

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

继续迭代会让损失越来越小,最终找到能使 y=5y = 5W1,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 分钟从零到能写函数》

📬

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

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

💬

讨论区

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