共计 2175 个字符,预计需要花费 6 分钟才能阅读完成。
反向传播(Backpropagation)是神经网络训练的基石,它通过高效计算梯度来指导参数更新。理解反向传播的关键在于掌握链式求导法则和矩阵运算的维度对齐。本文将从数学推导到代码实现,带你一步步攻克这个核心算法。

数学原理拆解
链式求导示例(Sigmoid 函数)
假设神经网络使用 Sigmoid 激活函数:
$$\sigma(z) = \frac{1}{1+e^{-z}}$$
其导数为:
$$\sigma'(z) = \sigma(z)(1-\sigma(z))$$
当计算损失函数 $L$ 对权重 $w$ 的梯度时,需要逐层反向求导:
$$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \times \frac{\partial a}{\partial z} \times \frac{\partial z}{\partial w}$$
其中 $a=\sigma(z)$ 是激活值,$z=w^Tx+b$ 是线性变换结果。
矩阵求导维度对齐
在矩阵运算中,梯度维度必须与参数维度一致。例如:
- 权重矩阵 $W$ 的维度为 $(n_{in}, n_{out})$
- 梯度 $\frac{\partial L}{\partial W}$ 必须保持相同维度
- 通过 $\delta^{l} = (W^{l+1})^T \delta^{l+1} \odot \sigma'(z^l)$ 反向传播误差
Python 实现详解
核心代码结构
import numpy as np
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size):
self.W1 = np.random.randn(input_size, hidden_size) * 0.01
self.b1 = np.zeros(hidden_size)
self.W2 = np.random.randn(hidden_size, output_size) * 0.01
self.b2 = np.zeros(output_size)
def sigmoid(self, x):
return 1 / (1 + np.exp(-x))
def forward(self, X):
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.sigmoid(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
return self.z2
def backward(self, X, y, output, learning_rate):
m = X.shape[0]
# 输出层梯度
dz2 = output - y
dW2 = np.dot(self.a1.T, dz2) / m
db2 = np.sum(dz2, axis=0) / m
# 隐藏层梯度
dz1 = np.dot(dz2, self.W2.T) * self.a1 * (1 - self.a1)
dW1 = np.dot(X.T, dz1) / m
db1 = np.sum(dz1, axis=0) / m
# 参数更新
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
梯度检查实现
def gradient_check(net, X, y, epsilon=1e-7):
params = {'W1':net.W1, 'b1':net.b1, 'W2':net.W2, 'b2':net.b2}
grads = {}
# 计算数值梯度
for key in params:
param = params[key]
grad = np.zeros_like(param)
for i in range(param.shape[0]):
for j in range(param.shape[1]):
# 正向扰动
param[i,j] += epsilon
loss_plus = compute_loss(net.forward(X), y)
# 负向扰动
param[i,j] -= 2*epsilon
loss_minus = compute_loss(net.forward(X), y)
# 恢复原值
param[i,j] += epsilon
# 计算梯度
grad[i,j] = (loss_plus - loss_minus) / (2*epsilon)
grads[f'd{key}'] = grad
return grads
避坑指南
梯度消失 / 爆炸处理
- 现象:梯度指数级缩小 / 增大
- 解决方案:
- 使用 ReLU 等改进的激活函数
- 初始化时控制权重范围(如 Xavier 初始化)
- 添加梯度裁剪(Gradient Clipping)
学习率设置经验
- 常用基准值:0.001~0.1
- 监控训练损失曲线:
- 如果损失波动大,适当降低学习率
- 如果下降过慢,可尝试增大学习率
- 进阶技巧:
- 学习率衰减(Learning Rate Decay)
- 自适应优化器(Adam 等)
思考与延伸
- 如何修改代码实现批量归一化(BatchNorm)?需要考虑哪些额外参数?
- 比较 ReLU、LeakyReLU、Tanh 等激活函数的梯度计算差异
- 如果使用二阶优化算法(如 L -BFGS),反向传播的实现需要做哪些调整?
通过这个完整的实现案例,相信你已经掌握了反向传播的核心要义。接下来可以尝试扩展到更复杂的网络结构,或者探索不同的优化策略。
正文完
