共计 2036 个字符,预计需要花费 6 分钟才能阅读完成。
1. 反向传播的数学基础
反向传播算法的核心是 链式求导法则。设网络有 $L$ 层,第 $l$ 层的权重为 $W^l$,偏置为 $b^l$,激活函数为 $\sigma(\cdot)$。对于单个样本 $(x,y)$,前向传播过程可表示为:

$$
\begin{aligned}
z^l &= W^l a^{l-1} + b^l \
a^l &= \sigma(z^l)
\end{aligned}
$$
定义损失函数 $J$(如交叉熵),反向传播需要计算 $\frac{\partial J}{\partial W^l}$ 和 $\frac{\partial J}{\partial b^l}$。根据链式法则:
$$
\frac{\partial J}{\partial W^l} = \frac{\partial J}{\partial z^l} \cdot \frac{\partial z^l}{\partial W^l} = \delta^l (a^{l-1})^T
$$
$$
\frac{\partial J}{\partial b^l} = \delta^l
$$
其中 $\delta^l = \frac{\partial J}{\partial z^l}$ 称为误差项,其递推公式为:
$$
\delta^l = (W^{l+1})^T \delta^{l+1} \odot \sigma'(z^l)
$$
输出层的 $\delta^L$ 需单独计算,例如使用交叉熵损失 +softmax 时:
$$
\delta^L = a^L – y
$$
2. 优化器对比
| 优化器 | 权重更新公式 | 特点 |
|---|---|---|
| SGD | $W \leftarrow W – \eta \nabla_W J$ | 简单但容易震荡 |
| Momentum | $v \leftarrow \gamma v + \eta \nabla_W J$ | 积累动量减少震荡 |
| $W \leftarrow W – v$ | ||
| Adam | 综合动量与自适应学习率 | 实际应用最广泛的优化器 |
3. Python 实现
import numpy as np
class BPNetwork:
def __init__(self, layers):
"""layers: 每层神经元数量,如[784, 256, 10]"""
self.weights = [np.random.randn(y, x)*0.01
for x,y in zip(layers[:-1], layers[1:])]
self.biases = [np.zeros((y,1)) for y in layers[1:]]
def forward(self, x):
"""前向传播"""
a = x
for w, b in zip(self.weights, self.biases):
z = np.dot(w, a) + b
a = 1/(1+np.exp(-z)) # sigmoid 激活
return a
def backward(self, x, y):
"""反向传播"""
# 前向计算并保存中间结果
activations = [x]
zs = []
a = x
for w, b in zip(self.weights, self.biases):
z = np.dot(w, a) + b
zs.append(z)
a = 1/(1+np.exp(-z))
activations.append(a)
# 反向计算误差
delta = (activations[-1] - y) * activations[-1] * (1-activations[-1])
nabla_w = [np.zeros_like(w) for w in self.weights]
nabla_b = [np.zeros_like(b) for b in self.biases]
nabla_w[-1] = np.dot(delta, activations[-2].T)
nabla_b[-1] = delta
# 逐层反向传播
for l in range(2, len(self.weights)+1):
z = zs[-l]
sp = 1/(1+np.exp(-z)) * (1 - 1/(1+np.exp(-z)))
delta = np.dot(self.weights[-l+1].T, delta) * sp
nabla_w[-l] = np.dot(delta, activations[-l-1].T)
nabla_b[-l] = delta
return nabla_w, nabla_b
4. 避坑指南
- 学习率设置:过大的学习率会导致梯度爆炸(可添加梯度裁剪)
- ReLU 死亡问题:部分神经元可能永远无法激活(可改用 LeakyReLU)
- 批量归一化:应在激活函数前应用,保持均值 0 方差 1
5. 激活函数比较
| 函数 | 导数表达式 | 梯度特性 |
|---|---|---|
| Sigmoid | $\sigma(1-\sigma)$ | 易饱和导致梯度消失 |
| tanh | $1-\tanh^2(z)$ | 梯度比 sigmoid 更稳定 |
| ReLU | $1_{{z>0}}$ | 缓解梯度消失但可能神经元死亡 |
6. 思考题
要实现 Mini-batch 训练,需要:
1. 修改 forward/backward 支持矩阵输入(每列一个样本)
2. 在反向传播时对 batch 内梯度取平均
3. 添加 shuffle 逻辑打乱训练数据
完整的实现应该包括数据加载、分批处理和迭代训练等模块。通过向量化计算可以显著提升 GPU 利用率。
