Skip to content

第2章 反向传播 — 神经网络的学习算法

Chapter 2: Backpropagation — The Learning Algorithm of Neural Networks

反向传播(backpropagation /ˌbækprəpəˈɡeɪʃən/) (Backpropagation) 是深度学习最核(kernel /ˈkɜːrnl/)心的训练算法。 它将微积分中的链式法则应用于多层神经网络,优雅地解决了"如何计算每个参数(parameter /pəˈræmɪtər/)对最终误差的贡献"这一根本问题。本章从一个简单的计算图出发,逐步推导出 2 层神经网络的反向传播公式,并通过 NumPy 实现和数值梯度(gradient /ˈɡreɪdiənt/)验证来确保推导的正确性。

时间线:

  • 1986: Rumelhart, Hinton & Williams 在 Nature 发表反向传播算法

Backpropagation is the core training algorithm of deep learning. It applies the chain rule from calculus to multi-layer neural networks, elegantly solving the fundamental problem of "how to compute each parameter's contribution to the final error." This chapter starts from a simple computation graph, derives backpropagation for a 2-layer network step by step, and verifies correctness through NumPy implementation and numerical gradient checking.

前置知识 (Prerequisites): 微积分(链式法则),线性代数(矩阵乘法),第 1 章(感知机与 MLP)

依赖库 (Dependencies): numpy, matplotlib

Code companion: code/backpropagation.py


目录 (Table of Contents)

  1. 计算图 (Computation Graph) 📐
  2. 链式法则递归应用 📐
  3. 数值梯度验证 (Gradient Check)
  4. 2 层网络的完整实现
  5. 关键总结 (Key Summary)

1. 计算图 (Computation Graph)

1.1 前向传播的图形化表示 (Forward Pass Visualization)

计算图是理解反向传播最直观的工具。考虑一个最简单的网络:一个神经元 + 一个 Sigmoid(/ˈsɪɡmɔɪd/) + 平方误差

对于单个样本 (x,y),前向计算为:

z=wx+b,a=σ(z),L=12(ay)2

这个前向过程可以用计算图直观表示:

    x ──┐
        ├──→ (+) ── z ──→ [σ] ── a ──→ [½(·-y)²] ── L
    w ──┤         ↑
    b ──┘      偏置

从左到右: 输入 x 和权重 w 做线性组合,加上偏置 bz,Sigmoid 激活得 a,最后与目标 y 比较得损失 L

关键洞察: 要更新 w,我们需要 L/w。计算图告诉我们:从 Lw 的路径是 Lazw,所以根据链式法则:

Lw=Laazzw

这正是反向传播的本质——从输出端往输入端逐层传递梯度

1.2 计算图上的梯度传播 (Gradient Flow on Computation Graph)

让我们实际计算每个局部梯度:

节点局部梯度表达式
LaL/aay
aza/zσ(z)(1σ(z))
zwz/wx

因此:

Lw=(ay)σ(z)(1σ(z))x

这虽然只是一个神经元的梯度,但它揭示了反向传播的核心模式

梯度 = 上游梯度 × 局部梯度 (Gradient = upstream gradient × local gradient)

当我们堆叠多层时,这个模式会递归应用,形成误差反向传播(errors backpropagating from output to input)。

🔍 完整演算:单神经元梯度计算 — w=2,x=3,y=0

📐 公式

对于单个神经元,前向计算:

z=wx+b,a=σ(z),L=12(ay)2

反向传播的链式法则:

Lw=Laazzw

其中 σ(z)=11+ez 为 Sigmoid 函数。


📖 参数含义

符号名称含义
w权重神经元的连接权重,控制输入信号的缩放
b偏置神经元的偏置项,控制激活阈值
x输入单个样本的特征值
y目标值样本的真实标签
z线性输出z=wx+b,激活前的线性组合
a激活输出a=σ(z),神经元经过 Sigmoid 后的输出
L损失均方误差 L=12(ay)2
δ误差信号δ=L/z,上游传来的梯度
L/w权重梯度损失对权重的导数,用于梯度下降更新

📝 公式来源

链式法则的逐层拆解:

Lw=Laazzw

各局部梯度:

La=ay,az=σ(z)(1σ(z)),zw=x

合并得到完整梯度公式:

Lw=(ay)σ(z)(1σ(z))x

✏️ 手算演示

给定参数:w=2,b=1,x=3,y=0

Step 1: 前向传播

z=wx+b=23+1=7a=σ(z)=11+e70.999L=12(ay)2=12(0.9990)20.499

Step 2: 反向传播 — 逐层求导

La=ay=0.99900.999az=σ(z)(1σ(z))=a(1a)0.999×0.0010.0009zw=x=3

Step 3: 链式法则合成

Lw=Laazzw0.999×0.0009×30.0027

验证:上游梯度 × 局部梯度

梯度流动路径:L0.999a0.0009z3w

最终梯度 L/w0.0027,表示权重 w 每增加 1,损失 L 约增加 0.0027。


🌍 实际意义

  • 上游梯度 × 局部梯度 = 反向传播的核心模式:这个简单例子揭示了整个深度学习的梯度计算本质。每一层只负责计算自己的局部梯度,上游传来的梯度与局部梯度相乘后继续向前传播。
  • Sigmoid 的梯度消失现象:当 z 很大(7)时,σ(z)0.0009 非常小。深层网络中多个小梯度连乘会导致梯度消失(Vanishing Gradient)——这就是为什么现代网络更倾向使用 ReLU 的原因。
  • δ(误差信号)的概念δ=L/z 衡量了该神经元对最终误差的"责任",是反向传播中最核心的中间量。

2. 链式法则递归应用 (Recursive Chain Rule)

现在考虑一个 2 层神经网络(1 个隐藏层 + 1 个输出层),使用 Tanh 隐藏激活和 Sigmoid 输出激活。

符号定义 (Notation)

符号含义维度
X输入矩阵(N,din)
W1,b1第一层权重、偏置(din,dh), (1,dh)
z1,h1第一层线性输出、隐藏激活(N,dh)
W2,b2第二层权重、偏置(dh,1), (1,1)
z2,y^第二层线性输出、预测(N,1), (N,1)
y真实标签(N,1)
L二元交叉熵(entropy /ˈentrəpi/)损失标量(scalar /ˈskeɪlər/)

前向传播 (Forward Pass)

z1=XW1+b1,h1=tanh(z1)z2=h1W2+b2,y^=σ(z2)L=1Ni=1N[yilogy^i+(1yi)log(1y^i)]

2.1 输出层 (Layer 2 — Output Layer)

梯度 1: L/z2

对于二元交叉熵 + Sigmoid,有一个非常简洁的合并梯度形式:

Lz2=y^yRN×1

δ2=L/z2=y^y,我们称 δ2输出层的误差信号 (error signal)

梯度 2: L/W2L/b2

应用链式法则:

LW2=Lz2z2W2=h1δ2Rdh×1

注意 z2/W2=h1,这是因为 z2=h1W2+b2,维度为 (N,1)=(N,dh)×(dh,1)

Lb2=1Ni=1Nδ2(i)R1×1

矩阵形式的最终公式:

δ2=y^y,LW2=1Nh1δ2,Lb2=1Nδ2

2.2 隐藏层 (Layer 1 — Hidden Layer)

误差传播到第一层

现在我们要把 δ2 "传播" 回第一层。关键问题是:L/h1 是多少?

Lh1=Lz2z2h1=δ2W2RN×dh

通过激活函数传播

接着通过 Tanh 激活函数:

Lz1=Lh1tanh(z1)=(δ2W2)(1h12)

其中 表示逐元素乘法 (Hadamard product)。

W1,b1 的梯度

LW1=Xδ1,Lb1=1Nδ1

矩阵形式的最终公式:

δ1=(δ2W2)(1h12),LW1=1NXδ1,Lb1=1Nδ1

2.3 梯度汇总 (Gradient Summary)

      Layer 2 (Output)                   Layer 1 (Hidden)
  ┌──────────────────────┐         ┌──────────────────────┐
  │ δ₂ = ŷ - y           │         │ δ₁ = (δ₂W₂ᵀ) ⊙ t'(z₁)│
  │ dW₂ = (h₁ᵀ δ₂) / N   │    ←───│ dW₁ = (Xᵀ δ₁) / N    │
  │ db₂ = mean(δ₂)       │         │ db₁ = mean(δ₁)       │
  └──────────────────────┘         └──────────────────────┘
           │                                │
           │         δ₂ "flows back"        │
           └────────────────────────────────┘

核心模式 (The Core Pattern):

每一层的反向传播遵循统一的 三部曲

δ()=(W(+1))δ(+1)σ(z())LW()=1N(a(1))δ()Lb()=1Ni=1Nδ()

其中 σ 是当前层激活函数的导数。

🔍 完整演算:2 层网络反向传播手算 — 2→2→1

📐 公式

2 层神经网络(Tanh 隐藏层 + Sigmoid 输出层 + 二元交叉熵损失):

z1=XW1+b1,h1=tanh(z1)z2=h1W2+b2,y^=σ(z2)L=[ylogy^+(1y)log(1y^)]

输出层梯度:

δ2=y^y,LW2=h1δ2,Lb2=δ2

隐藏层梯度:

δ1=(δ2W2)tanh(z1),LW1=Xδ1,Lb1=δ1

📖 参数含义

符号名称含义
X,y输入、真实标签单个样本:X=[0.5,0.3],y=1
W1,b1隐藏层权重、偏置2×2 矩阵和 2 维偏置
W2,b2输出层权重、偏置2×1 向量和标量偏置
δ2输出层误差信号L/z2,衡量预测偏差
δ1隐藏层误差信号L/z1,经权重回传的误差
Hadamard 积逐元素乘法

📝 公式来源

反向传播 = 递归链式法则:

输出层:Ly^z2{W2,b2h1z1{W1,b1

δ2 的推导利用了 Sigmoid + 交叉熵的"巧合"——两者求导后抵消 σ(z) 的非线性,得到简洁形式 y^y

δ1 的推导:L/h1=δ2W2(误差回传),再经 Tanh 导数 tanh(z1)=1tanh(z1)2 门控。


✏️ 手算演示

网络结构与参数:

2 输入W1,b12 神经元隐藏层 (Tanh)W2,b21 神经元输出层 (Sigmoid)W1=[0.50.30.20.4],b1=[0.10.2],W2=[0.60.5],b2=0.3

单个样本: X=[0.50.3],y=1

Step 1: 前向传播

z1=XW1+b1=[0.50.3][0.50.30.20.4]+[0.10.2]z1,1=0.5×0.5+(0.3)×0.2+0.1=0.29z1,2=0.5×(0.3)+(0.3)×0.4+(0.2)=0.47z1=[0.290.47]h1=tanh(z1)=[tanh(0.29)tanh(0.47)][0.2820.438]z2=h1W2+b2=[0.2820.438][0.60.5]+0.3=0.282×0.6+(0.438)×(0.5)+0.3=0.688y^=σ(z2)=11+e0.6880.666L=[ylogy^+(1y)log(1y^)]=log(0.666)0.407

Step 2: 输出层反向传播

δ2=y^y=0.6661=0.334LW2=h1δ2=[0.2820.438]×(0.334)=[0.0940.146]Lb2=δ2=0.334

Step 3: 误差传播到隐藏层

Lh1=δ2W2=0.334×[0.60.5]=[0.2000.167]tanh(z1)=1h12=[10.28221(0.438)2]=[0.9200.808]δ1=Lh1tanh(z1)=[0.200×0.9200.167×0.808]=[0.1840.135]

Step 4: 隐藏层梯度

LW1=Xδ1=[0.50.3][0.1840.135]=[0.0920.0680.0550.041]Lb1=δ1=[0.1840.135]

Step 5: 梯度流动表

传播方向变量数值/表达式维度含义
前向z1[0.29,0.47]1×2隐藏层线性输出
h1[0.282,0.438]1×2Tanh 激活
z20.6881×1输出层线性输出
y^0.6661×1Sigmoid 预测
反向 ↑δ20.3341×1输出误差信号
L/W2[0.094,0.146]2×1输出层权重梯度
L/h1[0.200,0.167]1×2对隐藏层的梯度
δ1[0.184,0.135]1×2隐藏层误差信号
L/W1[0.0920.0680.0550.041]2×2隐藏层权重梯度

🌍 实际意义

  • 误差信号 δ 的物理含义δ2=0.334 表示预测 0.666 低于真实值 1,因此梯度会推动权重增加预测值。δ1 中第一个神经元为负(0.184)、第二个为正(0.135),反映了它们对输出误差的不同"责任"。
  • 梯度流动的可视化:从 y^W1 方向,误差信号 δ 经历"输出层 → 权重回传 → 激活函数门控"的标准三步曲,每一步都乘以一个局部梯度。
  • 现实训练中的应用:实际训练时(如第 4 节的代码实现),上述手算过程以矩阵形式对批量数据一次性完成。梯度检查(第 3 节)本质上就是用数值近似验证上述每一组梯度的正确性。

3. 数值梯度验证 (Gradient Check)

反向传播的推导容易出错。数值梯度验证 (Gradient Check) 是确保推导正确的黄金标准。

有限差分法 (Finite Differences)

使用中心差分 (central difference) 近似导数:

f(θ)θf(θ+ε)f(θε)2ε,ε=105

为什么不用前向差分 [f(θ+ε)f(θ)]/ε 中心差分的误差为 O(ε2),而前向差分的误差为 O(ε)。在 ε=105 下,中心差分的精度远高于前向差分。

验证标准 (Verification Criteria)

如果反向传播实现正确,对于每个参数 θ

|analytical_gradnumerical_grad|<106

代码实现 (Code Implementation)

python
def gradient_check(model, X, y, param_name="W2", num_tests=5):
    """
    数值梯度验证: |analytical - numerical| < 1e-6
    使用中心差分: ∂f/∂θ ≈ (f(θ+ε) - f(θ-ε)) / (2ε)
    """
    param = model.params[param_name]
    analytical_grad = model.grads[param_name].copy()
    flat_analytical = analytical_grad.ravel()
    total_dims = flat_analytical.size

    # 如果参数小, 测试所有维度; 否则随机采样
    if total_dims <= 20:
        test_indices = list(range(total_dims))
    else:
        test_indices = RNG.choice(
            total_dims, min(num_tests, total_dims), replace=False
        ).tolist()

    max_diff = 0.0
    for idx in test_indices:
        orig_val = param.ravel()[idx]

        # f(θ + ε)
        param_plus = param.copy()
        param_plus.ravel()[idx] += EPS
        loss_plus = model.compute_loss_with_params(
            {param_name: param_plus}, X, y
        )

        # f(θ - ε)
        param_minus = param.copy()
        param_minus.ravel()[idx] -= EPS
        loss_minus = model.compute_loss_with_params(
            {param_name: param_minus}, X, y
        )

        num_grad = (loss_plus - loss_minus) / (2 * EPS)
        ana_grad = flat_analytical[idx]
        diff = abs(ana_grad - num_grad)
        max_diff = max(max_diff, diff)

    return max_diff

运行输出 (Actual Output)

========================================================================
【数值梯度验证 / Numerical Gradient Check】
  有限差分步长 (Finite diff step): ε = 1e-05
  每个参数测试 (Tests per param): 5 个随机位置
========================================================================

  参数   W1  ✓ PASS  |  shape=(2, 10)  |  max|diff|=1.30e-11  |  rel_err=1.24e-10
    [0,0]  analytical=-1.04903863e-01  numerical=-1.04903863e-01  |diff|=3.55e-12
    [0,1]  analytical=+2.28319969e-02  numerical=+2.28319969e-02  |diff|=8.81e-12
    ...
    [1,9]  analytical=-1.18234050e-02  numerical=-1.18234050e-02  |diff|=2.18e-12

  参数   b1  ✓ PASS  |  shape=(1, 10)  |  max|diff|=7.66e-12  |  rel_err=3.41e-10

  参数   W2  ✓ PASS  |  shape=(10, 1)  |  max|diff|=1.43e-11  |  rel_err=3.69e-11

  参数   b2  ✓ PASS  |  shape=(1, 1)   |  max|diff|=1.62e-12  |  rel_err=7.74e-11

  ✓ 所有梯度检查通过! |analytical - numerical| < 1e-6

所有 4 组参数的梯度检查全部通过,最大绝对差值 1.43×1011,远小于 106 的阈值。这以数值方式证明了我们推导的反向传播公式是正确的。


4. 2 层网络的完整实现 (Complete 2-Layer Implementation)

4.1 前向与反向传播 (Forward & Backward)

python
class TwoLayerNet:
    """2 层神经网络: 输入 → 隐藏层(tanh) → 输出层(sigmoid)"""

    def __init__(self, input_dim=2, hidden_dim=10):
        scale1 = np.sqrt(1.0 / input_dim)
        scale2 = np.sqrt(1.0 / hidden_dim)
        self.params = {
            "W1": np.random.randn(input_dim, hidden_dim) * scale1,
            "b1": np.zeros((1, hidden_dim)),
            "W2": np.random.randn(hidden_dim, 1) * scale2,
            "b2": np.zeros((1, 1)),
        }
        self.cache = {}

    def forward(self, X):
        """前向传播: X → z1 → h1 → z2 → y_hat"""
        z1 = X @ self.params["W1"] + self.params["b1"]
        h1 = np.tanh(z1)
        z2 = h1 @ self.params["W2"] + self.params["b2"]
        y_hat = 1.0 / (1.0 + np.exp(-np.clip(z2, -500, 500)))

        self.cache = {"X": X, "z1": z1, "h1": h1, "z2": z2, "y_hat": y_hat}
        return y_hat

    def backward(self, y):
        """反向传播 — 计算所有参数的梯度"""
        X = self.cache["X"]
        h1 = self.cache["h1"]
        y_hat = self.cache["y_hat"]
        N = X.shape[0]

        # 输出层: δ₂ = ŷ - y
        delta2 = y_hat - y
        dW2 = (h1.T @ delta2) / N
        db2 = np.mean(delta2, axis=0, keepdims=True)

        # 隐藏层: δ₁ = (δ₂W₂ᵀ) ⊙ (1 - h₁²)
        delta1 = (delta2 @ self.params["W2"].T) * (1.0 - h1 ** 2)
        dW1 = (X.T @ delta1) / N
        db1 = np.mean(delta1, axis=0, keepdims=True)

        self.grads = {"W1": dW1, "b1": db1, "W2": dW2, "b2": db2}
        return self.grads

4.2 梯度检查结果 (Gradient Check Results)

我们用 backpropagation.py 中的 full_gradient_check() 对所有 4 组参数验证。结果:

  ✓ W1: max|diff| = 1.30e-11  (through 20 dimensions)
  ✓ b1: max|diff| = 7.66e-12  (through 10 dimensions)
  ✓ W2: max|diff| = 1.43e-11  (through 10 dimensions)
  ✓ b2: max|diff| = 1.62e-12  (through 1 dimension)

所有 |diff| 在 10111012 量级,远低于 106 阈值。反向传播公式正确!

4.3 训练与决策边界 (Training & Decision Boundary)

在 sklearn 风格的 moons 二分类(classification /ˌklæsɪfɪˈkeɪʃən/)数据集上训练(200 样本,noise=0.1):

Step 5: Training
  Epoch    1 | Loss = 0.927862 | Acc = 0.3600
  Epoch  100 | Loss = 0.290621 | Acc = 0.8600
  Epoch  200 | Loss = 0.285902 | Acc = 0.8600
  Epoch  300 | Loss = 0.278738 | Acc = 0.8750
  Epoch  400 | Loss = 0.237570 | Acc = 0.8850
  Epoch  500 | Loss = 0.151477 | Acc = 0.9500

  Final loss: 0.151477
  Final acc:  0.9500

训练曲线

损失从 0.928 降至 0.151,准确率从 36% 提升至 95%。500 个 epoch 后的决策边界清晰地分离了两个半月形:

决策边界


本章演算盒索引

位置演算盒跳转
§1🔍 单神经元梯度计算 — w=2,x=3,y=0跳转
§2🔍 2 层网络反向传播手算 — 2→2→1跳转

5. 关键总结 (Key Summary)

核心公式

概念公式含义
误差信号 δ()δ()=(W(+1))δ(+1)σ(z())上层误差通过权重回传,经激活函数导数的"门控"
权重梯度L/W()=1N(a(1))δ()下层激活 × 本层误差信号
偏置梯度L/b()=1Nδ()误差信号的平均值
数值梯度验证f(θ+ε)f(θε)2ε中心差分,ε=105,要求 $

关键洞察

  1. 链式法则是唯一需要的数学工具 — 反向传播就是递归地应用链式法则,从输出层逐层向输入层传播梯度
  2. 局部梯度 × 传播梯度 = 参数梯度 — 每个节点的梯度 = 上游传播来的梯度 × 该节点自身的局部梯度
  3. 梯度检查是调试的必备技能 — 在实现新的网络结构时,永远先用数值梯度验证你的解析梯度
  4. "误差反向传播"这个名字很贴切δ() 就是"误差信号",它从输出层开始,逐层向后"传播"

与后续章节的联系

后续章节联系
第 3 章 (训练技巧)反向传播的改进:动量(momentum /məˈmentəm/)、Adam、梯度裁剪
第 4 章 (CNN)反向传播在卷积(convolution /ˌkɒnvəˈluːʃən/)层中的适配(卷积转置)
第 5 章 (RNN)通过时间的反向传播 (BPTT)
Transformer(/trænsˈfɔːrmər/)自注意力(attention /əˈtenʃən/)的反向传播 — 更复杂的计算图

动手练习

  1. 推导练习: 为 3 层网络(2 个隐藏层 + 1 个输出层)推导反向传播公式
  2. 代码练习: 将隐藏层激活从 Tanh 改为 ReLU,修改 backward() 中的对应行,验证梯度检查仍通过
  3. 实验练习:backpropagation.py 中改变隐藏层神经元数(5, 10, 20, 50),观察最终准确率的变化
  4. 深入练习: 添加 L2 正则化(regularization /ˌreɡjələraɪˈzeɪʃən/),推导修改后的梯度公式,验证梯度检查

下章预告: 第 3 章将介绍训练技巧 (Training Techniques) — 权重初始化、批归一化(normalization /ˌnɔːrmələˈzeɪʃən/) (Batch Normalization)、Dropout(/ˈdrɒpaʊt/)、学习率调度等让神经网络训练更稳定的实用技术。


Last updated: 2026-06-02

参考文献 (References)

  1. Rumelhart, D. E., Hinton, G. E. & Williams, R. J. (1986). Learning representations by back-propagating errors. Nature, 323(6088), 533–536.
  2. LeCun, Y., Bottou, L., Bengio, Y. & Haffner, P. (1998). Gradient-based learning applied to document recognition. Proc. IEEE, 86(11), 2278–2324.