BP梯度下降算法详解:从数学原理到Python实现

1次阅读
没有评论

共计 1902 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

1. 反向传播的数学原理

反向传播(Backpropagation)的核心是链式法则。假设有一个三层的神经网络,其前向传播过程可以表示为:

BP 梯度下降算法详解:从数学原理到 Python 实现

$$
\begin{aligned}
z^{[1]} &= W^{[1]}x + b^{[1]} \
a^{[1]} &= \sigma(z^{[1]}) \
z^{[2]} &= W^{[2]}a^{[1]} + b^{[2]} \
a^{[2]} &= \sigma(z^{[2]})
\end{aligned}
$$

反向传播时,我们需要计算损失函数 $L$ 对各个参数的梯度。以 $W^{[2]}$ 为例:

$$
\frac{\partial L}{\partial W^{[2]}} = \frac{\partial L}{\partial a^{[2]}} \cdot \frac{\partial a^{[2]}}{\partial z^{[2]}} \cdot \frac{\partial z^{[2]}}{\partial W^{[2]}}
$$

这就是链式法则的具体应用。

2. 梯度消失问题与解决方案

梯度消失常发生在深层网络中使用 sigmoid/tanh 激活函数时。因为:

  • sigmoid 导数最大值为 0.25
  • 多层连乘后梯度指数级减小

解决方案:

  1. 使用 ReLU 激活函数:$f(x)=max(0,x)$
  2. 残差连接(ResNet)
  3. 批量归一化(BatchNorm)

3. Python 实现核心代码

网络层基类

class Layer:
    def __init__(self):
        self.params = {}

    def forward(self, x):
        raise NotImplementedError

    def backward(self, grad):
        raise NotImplementedError

全连接层实现

class Dense(Layer):
    def __init__(self, input_dim, output_dim):
        super().__init__()
        # Xavier 初始化
        self.params['W'] = np.random.randn(input_dim, output_dim) * np.sqrt(2./input_dim)
        self.params['b'] = np.zeros((1, output_dim))

    def forward(self, x):
        self.x = x  # 保存输入用于反向传播
        return x @ self.params['W'] + self.params['b']

    def backward(self, grad):
        # 计算本层梯度
        dW = self.x.T @ grad
        db = np.sum(grad, axis=0, keepdims=True)
        # 返回传给前一层的梯度
        return grad @ self.params['W'].T, dW, db

4. 训练过程可视化

使用 Matplotlib 绘制训练曲线:

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(losses, label='训练损失')
plt.xlabel('迭代次数')
plt.ylabel('损失值')

plt.subplot(1,2,2)
plt.plot(accuracies, label='测试准确率')
plt.xlabel('迭代次数')
plt.ylabel('准确率')
plt.show()

5. 关键优化技巧

学习率调整策略

  1. 线性预热:前 1000 步从 0 线性增加到目标学习率
  2. 余弦退火:$\eta_t = \eta_{min} + \frac{1}{2}(\eta_{max}-\eta_{min})(1+cos(\frac{t}{T}\pi))$

梯度裁剪实现

def clip_grads(grads, max_norm):
    total_norm = 0
    for grad in grads:
        total_norm += np.sum(grad**2)
    total_norm = np.sqrt(total_norm)

    clip_coef = max_norm / (total_norm + 1e-6)
    if clip_coef < 1:
        for grad in grads:
            grad *= clip_coef

6. 实践经验总结

  • 输入数据归一化到 [0,1] 或标准正态分布
  • 参数初始化推荐:
  • ReLU 网络使用 He 初始化
  • Tanh 网络使用 Xavier 初始化
  • 调试时可以先在小数据集上过拟合

7. 扩展思考

  1. Adam 优化器在 SGD 基础上增加了:
  2. 动量项(一阶矩估计)
  3. 自适应学习率(二阶矩估计)
  4. 不同优化器对比:
  5. SGD 收敛慢但最终精度可能更高
  6. Adam 收敛快但可能陷入局部最优

完整的实现代码已上传到 GitHub 仓库,包含更多细节和注释。建议读者尝试修改网络结构、调整超参数来观察训练效果的变化。

正文完
 0
评论(没有评论)