共计 2029 个字符,预计需要花费 6 分钟才能阅读完成。
反向传播 (Backpropagation) 算法是深度学习的基石,它让多层神经网络的训练成为可能。通过梯度下降的迭代优化,BP 算法将误差从输出层逐层反向传播至输入层。没有 BP 算法,现代深度学习模型如同失去引擎的汽车,无法自动调整参数。

数学原理:从标量到矩阵的链式法则
-
标量求导示例:设简单函数 $z = (x+y)^2$,求 $\frac{\partial z}{\partial x}$ 时,先计算 $\frac{\partial z}{\partial t}$(其中 $t=x+y$),再乘 $\frac{\partial t}{\partial x}$,这就是链式法则的标量形式:
$$\frac{\partial z}{\partial x} = 2(x+y) \cdot 1$$ -
矩阵求导差异:当处理 $W\mathbf{x}+\mathbf{b}$ 这样的矩阵运算时,需遵循矩阵求导规则。例如全连接层权重 $W$ 的梯度计算:
$$\frac{\partial L}{\partial W} = \frac{\partial L}{\partial \mathbf{z}} \cdot \mathbf{x}^T$$
这里的矩阵维度必须匹配,$\mathbf{x}^T$ 的出现是矩阵乘法规则决定的。
三大核心痛点与解决方案
- 梯度消失:当使用 sigmoid 激活函数时,其导数最大值为 0.25,多层连乘后梯度指数级减小。解决方案:
- 改用 ReLU 及其变体(如 LeakyReLU)
-
添加残差连接(ResNet 思路)
-
学习率选择:
- 太大:震荡无法收敛
- 太小:收敛速度过慢
动态调整策略: - 余弦退火(Cosine Annealing)
- 循环学习率(CLR)
-
Adam 优化器的自适应学习率
-
局部最优:
实际中鞍点比局部极小更常见,动量(Momentum)能帮助逃离:
$$v_t = \gamma v_{t-1} + \eta \nabla_\theta J(\theta)$$
$$\theta = \theta – v_t$$
PyTorch 实战代码解析
# 自定义全连接层(含手动反向传播)class DenseLayer(nn.Module):
def __init__(self, input_dim, output_dim):
super().__init__()
self.w = nn.Parameter(torch.randn(input_dim, output_dim) * 0.1)
self.b = nn.Parameter(torch.zeros(output_dim))
def forward(self, x):
self.x = x # 缓存输入用于反向传播
return x @ self.w + self.b
def manual_backward(self, grad_output):
grad_w = self.x.T @ grad_output # 矩阵求导关键步骤
grad_b = grad_output.sum(0)
grad_input = grad_output @ self.w.T
return grad_input, grad_w, grad_b
# 手动 SGD 优化器(含动量与梯度裁剪)class MySGD:
def __init__(self, params, lr=0.01, momentum=0.9):
self.params = list(params)
self.lr = lr
self.momentum = momentum
self.velocities = [torch.zeros_like(p) for p in self.params]
def step(self, grads):
for p, v, g in zip(self.params, self.velocities, grads):
if self.momentum > 0:
v.mul_(self.momentum).add_(g)
g = v
# 梯度裁剪(防止爆炸)g = torch.clamp(g, -1, 1)
p.data.sub_(self.lr * g)
避坑指南
- 激活函数选择:
- ReLU 家族梯度恒为 1(正区间),缓解消失问题
-
Swish 函数 $\frac{x}{1+e^{-x}}$ 具有平滑梯度特性
-
BatchNorm 的影响:
- 标准化操作会改变梯度幅度
-
反向传播需额外计算均值 / 方差的梯度
-
浮点数精度:
- 混合精度训练(fp16+fp32)
- 梯度累加时用
loss = loss / iters_to_accumulate
性能优化技巧
- 向量化计算 :避免 Python 循环,用
torch.matmul替代逐元素操作 - GPU 并行化:
- 使用
torch.cuda.amp自动混合精度 - DataParallel 多卡训练时注意梯度聚合
延伸思考
- 在 LSTM 中,时间步之间的梯度如何流动?与普通全连接网络有何不同?
- 为什么 Transformer 需要梯度裁剪的频率比 CNN 更高?
- 残差连接是如何改变梯度传播路径的?
通过本文的数学推导和代码实践,希望读者能建立起对 BP 算法和梯度下降的直观理解。建议动手修改示例代码中的超参数(如动量系数、学习率),观察训练曲线的变化,这是掌握算法精髓的最佳途径。
