05 - 反向传播算法¶
反向传播原理¶
图注:反向传播算法流程图,展示前向传播和反向传播的完整过程
重要性: ⭐⭐⭐⭐⭐ 实用度: ⭐⭐⭐⭐⭐ 学习时间: 4小时 必须掌握: 是
为什么学这一章?¶
反向传播是训练神经网络的核心算法。理解它能帮助你: - 真正理解神经网络是如何学习的 - 调试训练过程中的梯度问题 - 实现自定义的层和损失函数 - 理解自动微分的原理
学完这一章,你将能够: - ✅ 理解链式法则在神经网络中的应用 - ✅ 手动推导简单网络的反向传播 - ✅ 实现完整的反向传播算法 - ✅ 进行梯度检查验证正确性 - ✅ 理解自动微分的基本原理
📖 计算图与链式法则¶
图注:计算图可视化,展示前向传播(数据流动)和反向传播(梯度流动)的对比
什么是计算图?¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 计算图:可视化计算过程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 示例:计算 z = (x + y) × w │
│ │
│ x ──┐ │
│ ├──→ [+] ──→ a ──┐ │
│ y ──┘ ├──→ [×] ──→ z │
│ w ─────────────────────┘ │
│ │
│ 节点:变量或操作 │
│ 边:数据流动方向 │
│ │
│ 前向传播:从左到右计算 │
│ 反向传播:从右到左计算梯度 │
│ │
└─────────────────────────────────────────────────────────────────────┘
链式法则¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 链式法则 - 反向传播的核心 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 单变量链式法则: │
│ dz/dx = dz/dy · dy/dx │
│ │
│ 多变量链式法则: │
│ 如果 z = f(x,y),x = g(t),y = h(t) │
│ 则 dz/dt = ∂z/∂x · dx/dt + ∂z/∂y · dy/dt │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 直观理解: │ │
│ │ │ │
│ │ 想象一条河流: │ │
│ │ 上游流量变化 = 中游流量变化 × 下游流量变化 │ │
│ │ │ │
│ │ 梯度就是流量的变化率 │ │
│ │ 反向传播就是逐层传递这个变化率 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 示例: │
│ f(x) = (x + 1)² │
│ 令 u = x + 1,则 f = u² │
│ df/dx = df/du · du/dx = 2u · 1 = 2(x + 1) │
│ │
└─────────────────────────────────────────────────────────────────────┘
🔧 反向传播详解¶
图注:神经网络中的梯度流动,蓝色箭头表示前向传播,橙色箭头表示反向传播(梯度流动)
简单示例¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 反向传播示例:z = x × y + w │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 计算图: │
│ │
│ x(2) ──┐ 前向传播 │
│ ├──→ [×] ──→ a(6) ──┐ a = x × y = 2 × 3 = 6 │
│ y(3) ──┘ ├──→ [+] ──→ z(10) z = a + w = 6+4=10 │
│ w(4) ───────────────────────┘ │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ 反向传播(假设 ∂L/∂z = 1): │
│ │
│ 1. ∂L/∂z = 1 │
│ │
│ 2. z = a + w │
│ ∂L/∂a = ∂L/∂z · ∂z/∂a = 1 × 1 = 1 │
│ ∂L/∂w = ∂L/∂z · ∂z/∂w = 1 × 1 = 1 │
│ │
│ 3. a = x × y │
│ ∂L/∂x = ∂L/∂a · ∂a/∂x = 1 × y = 3 │
│ ∂L/∂y = ∂L/∂a · ∂a/∂y = 1 × x = 2 │
│ │
│ 结果: │
│ ∂L/∂x = 3, ∂L/∂y = 2, ∂L/∂w = 1 │
│ │
└─────────────────────────────────────────────────────────────────────┘
神经网络中的反向传播¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 神经网络的反向传播 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 网络结构:输入 → 线性 → ReLU → 线性 → Softmax → 输出 │
│ │
│ 前向传播: │
│ z₁ = X·W₁ + b₁ │
│ h = ReLU(z₁) │
│ z₂ = h·W₂ + b₂ │
│ ŷ = Softmax(z₂) │
│ L = CrossEntropy(ŷ, y) │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ 反向传播(从后往前): │
│ │
│ 1. 输出层梯度(Softmax + CrossEntropy): │
│ ∂L/∂z₂ = ŷ - y (简洁形式!) │
│ │
│ 2. 第二层参数梯度: │
│ ∂L/∂W₂ = hᵀ · ∂L/∂z₂ │
│ ∂L/∂b₂ = Σ ∂L/∂z₂ │
│ │
│ 3. 隐藏层梯度: │
│ ∂L/∂h = ∂L/∂z₂ · W₂ᵀ │
│ ∂L/∂z₁ = ∂L/∂h ⊙ ReLU'(z₁) (逐点乘,ReLU导数) │
│ │
│ 4. 第一层参数梯度: │
│ ∂L/∂W₁ = Xᵀ · ∂L/∂z₁ │
│ ∂L/∂b₁ = Σ ∂L/∂z₁ │
│ │
│ 关键:梯度从输出层向输入层逐层传播! │
│ │
└─────────────────────────────────────────────────────────────────────┘
🧮 手动推导示例¶
图注:反向传播的具体示例,展示如何通过链式法则计算梯度
两层网络完整推导¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 两层网络反向传播完整推导 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 网络: │
│ 输入 X (1×2) → W₁ (2×3) → ReLU → W₂ (3×2) → Softmax → 输出 │
│ │
│ 给定: │
│ X = [1, 2] │
│ W₁ = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]] │
│ W₂ = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]] │
│ y = [1, 0] (类别0) │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ 前向传播: │
│ │
│ z₁ = X·W₁ = [1, 2] · [[0.1, 0.2, 0.3], │
│ [0.4, 0.5, 0.6]] │
│ = [0.9, 1.2, 1.5] │
│ │
│ h = ReLU(z₁) = [0.9, 1.2, 1.5] │
│ │
│ z₂ = h·W₂ = [0.9, 1.2, 1.5] · [[0.1, 0.2], │
│ [0.3, 0.4], │
│ [0.5, 0.6]] │
│ = [1.20, 1.56] │
│ │
│ ŷ = Softmax(z₂) = [0.41, 0.59] │
│ │
│ L = -log(0.41) = 0.89 │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ 反向传播: │
│ │
│ 1. ∂L/∂z₂ = ŷ - y = [0.41-1, 0.59-0] = [-0.59, 0.59] │
│ │
│ 2. ∂L/∂W₂ = hᵀ · ∂L/∂z₂ │
│ = [[0.9], · [-0.59, 0.59] │
│ [1.2], │
│ [1.5]] │
│ = [[-0.53, 0.53], │
│ [-0.71, 0.71], │
│ [-0.89, 0.89]] │
│ │
│ 3. ∂L/∂h = ∂L/∂z₂ · W₂ᵀ │
│ = [-0.59, 0.59] · [[0.1, 0.3, 0.5], │
│ [0.2, 0.4, 0.6]] │
│ = [0.059, 0.059, 0.059] │
│ │
│ 4. ∂L/∂z₁ = ∂L/∂h ⊙ ReLU'(z₁) │
│ = [0.059, 0.059, 0.059] ⊙ [1, 1, 1] │
│ = [0.059, 0.059, 0.059] (z₁都>0) │
│ │
│ 5. ∂L/∂W₁ = Xᵀ · ∂L/∂z₁ │
│ = [[1], · [0.059, 0.059, 0.059] │
│ [2]] │
│ = [[0.059, 0.059, 0.059], │
│ [0.118, 0.118, 0.118]] │
│ │
│ ✅ 完成!现在可以用这些梯度更新参数了! │
│ │
└─────────────────────────────────────────────────────────────────────┘
✅ 梯度检查¶
数值梯度验证¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 梯度检查:验证反向传播正确性 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 原理:用数值梯度近似解析梯度,对比两者是否接近 │
│ │
│ 数值梯度公式(中心差分): │
│ ∂f/∂x ≈ [f(x+ε) - f(x-ε)] / (2ε) │
│ │
│ Python实现: │
│ │
│ def numerical_gradient(f, x, epsilon=1e-5): │
│ """ │
│ 计算函数f在点x处的数值梯度 │
│ f: 函数 │
│ x: numpy数组,计算梯度的位置 │
│ """ │
│ grad = np.zeros_like(x) │
│ it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])│
│ │
│ while not it.finished: │
│ idx = it.multi_index │
│ old_val = x[idx] │
│ │
│ # f(x + epsilon) │
│ x[idx] = old_val + epsilon │
│ fx_plus = f(x) │
│ │
│ # f(x - epsilon) │
│ x[idx] = old_val - epsilon │
│ fx_minus = f(x) │
│ │
│ # 中心差分 │
│ grad[idx] = (fx_plus - fx_minus) / (2 * epsilon) │
│ │
│ # 恢复原始值 │
│ x[idx] = old_val │
│ it.iternext() │
│ │
│ return grad │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ 使用示例: │
│ │
│ # 定义损失函数(关于W的函数) │
│ def loss_fn(W): │
│ model.W1 = W │
│ y_pred = model.forward(X) │
│ return model.compute_loss(y_pred, y) │
│ │
│ # 计算数值梯度 │
│ num_grad = numerical_gradient(loss_fn, model.W1) │
│ │
│ # 计算解析梯度(反向传播) │
│ y_pred = model.forward(X) │
│ grads = model.backward(y) │
│ ana_grad = grads['W1'] │
│ │
│ # 比较两者 │
│ diff = np.linalg.norm(num_grad - ana_grad) / │
│ (np.linalg.norm(num_grad) + np.linalg.norm(ana_grad)) │
│ │
│ print(f"相对误差: {diff}") │
│ # 如果 diff < 1e-7,说明反向传播实现正确! │
│ │
└─────────────────────────────────────────────────────────────────────┘
🤖 自动微分简介¶
什么是自动微分?¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 自动微分(Automatic Differentiation) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 自动微分 = 计算机自动计算梯度 │
│ │
│ 三种计算导数的方法: │
│ │
│ 1. 符号微分(Symbolic Diff) │
│ • 用代数规则推导导数公式 │
│ • 优点:精确 │
│ • 缺点:表达式膨胀,难以处理控制流 │
│ │
│ 2. 数值微分(Numerical Diff) │
│ • 用差分近似(f(x+h)-f(x))/h │
│ • 优点:简单 │
│ • 缺点:精度低,计算量大 │
│ │
│ 3. 自动微分(Automatic Diff)⭐ │
│ • 基于链式法则,在计算图中反向传播 │
│ • 优点:精确、高效、通用 │
│ • 缺点:需要记录中间结果 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 深度学习框架的自动微分: │ │
│ │ │ │
│ │ PyTorch: 动态计算图,反向传播时自动计算梯度 │ │
│ │ TensorFlow: 静态/动态计算图,tf.GradientTape │ │
│ │ JAX: 函数变换,jit、grad、vmap │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
PyTorch自动微分示例¶
Python
import torch
# 创建需要梯度的张量
x = torch.tensor([2.0, 3.0], requires_grad=True)
w = torch.tensor([1.0, 2.0], requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
# 前向传播
y = torch.sum(x * w) + b # y = 2*1 + 3*2 + 1 = 9
# 反向传播
y.backward()
# 查看梯度
print(f"x.grad = {x.grad}") # tensor([1., 2.])
print(f"w.grad = {w.grad}") # tensor([2., 3.])
print(f"b.grad = {b.grad}") # tensor(1.)
# 验证:
# y = x₁w₁ + x₂w₂ + b
# ∂y/∂x₁ = w₁ = 1
# ∂y/∂x₂ = w₂ = 2
# ∂y/∂w₁ = x₁ = 2
# ∂y/∂w₂ = x₂ = 3
# ∂y/∂b = 1
💡 核心要点总结¶
反向传播关键步骤¶
- 前向传播:计算每一层的输出,保存中间结果
- 计算输出层梯度:根据损失函数计算
- 反向传播梯度:从后向前,用链式法则计算每层梯度
- 计算参数梯度:用上游梯度和本地梯度相乘
- 更新参数:用梯度下降更新权重
调试技巧¶
- 梯度检查:用数值梯度验证解析梯度
- 打印梯度:检查梯度大小和分布
- 可视化:画出计算图帮助理解
- 简化网络:先用小网络验证正确性
❓ 常见问题¶
Q1:为什么反向传播比前向传播慢?
A:反向传播需要: - 保存前向传播的中间结果(占用内存) - 计算并传播梯度(计算量类似前向) - 实际中两者时间相近
Q2:梯度消失怎么解决?
A: - 使用ReLU等激活函数 - 残差连接(ResNet) - 批归一化(BatchNorm) - 更好的初始化
Q3:梯度爆炸怎么解决?
A: - 梯度裁剪(Gradient Clipping) - 更小的学习率 - 权重正则化 - 批归一化
Q4:为什么要用链式法则?
A:神经网络是复合函数,链式法则是计算复合函数导数的唯一有效方法。
📝 自测问题¶
- 什么是链式法则?为什么在神经网络中需要它?
- 手动推导一个两层网络的反向传播过程。
- 为什么要进行梯度检查?
- 自动微分相比数值微分有什么优势?
- 如果反向传播实现错误,可能出现什么问题?
📚 扩展阅读¶
- 《Learning representations by back-propagating errors》 - Rumelhart et al. (1986) - 反向传播原始论文
- PyTorch Autograd文档 - 理解自动微分实现
- CS231n Lecture 4 - Backpropagation
🎯 下一步¶
恭喜完成第一阶段!现在你可以: 1. 完成 实践-手写神经网络 项目 2. 开始第二阶段 02-CNN 的学习
你已经掌握了深度学习的基础,继续加油!🎉



