BP梯度下降公式详解:从数学推导到Python实现

1次阅读
没有评论

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

image.webp

链式法则与权重更新原理

BP 算法的核心是链式法则。假设我们有 3 层网络(输入层、隐藏层、输出层),以输出层权重更新为例:

BP 梯度下降公式详解:从数学推导到 Python 实现

  1. 计算损失函数 L 对输出层权重 w 的偏导:
    $$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w}$$
  2. a 是激活值,z 是加权输入
  3. 这个偏导反映了权重微小变化对最终损失的影响程度

  4. 对于 MSE 损失函数和 Sigmoid 激活的组合:

  5. $$\frac{\partial L}{\partial a} = 2(a-y)$$
  6. $$\frac{\partial a}{\partial z} = \sigma(z)(1-\sigma(z))$$
  7. $$\frac{\partial z}{\partial w} = a_{prev}$$

梯度下降的两种实现方式

  • 标准梯度下降(Batch GD):
  • 使用全部样本计算梯度
  • 更新稳定但计算量大
  • 适合小规模数据集

  • 随机梯度下降(SGD):

  • 每次随机选取单个样本
  • 更新波动大但计算快
  • 适合在线学习场景

实际常用的是 小批量梯度下降(Mini-batch GD),平衡了计算效率和稳定性。

Python 完整实现

import numpy as np

class NeuralNetwork:
    def __init__(self, input_size, hidden_size):
        # 初始化权重(注意尺度)self.W1 = np.random.randn(input_size, hidden_size) * 0.01
        self.W2 = np.random.randn(hidden_size, 1) * 0.01

    def sigmoid(self, z):
        return 1/(1+np.exp(-z))

    def sigmoid_derivative(self, z):
        s = self.sigmoid(z)
        return s * (1-s)

    def forward(self, X):
        self.z1 = np.dot(X, self.W1)
        self.a1 = self.sigmoid(self.z1)
        self.z2 = np.dot(self.a1, self.W2)
        self.a2 = self.sigmoid(self.z2)
        return self.a2

    def backward(self, X, y, learning_rate):
        m = X.shape[0]  # 样本数量

        # 输出层梯度
        dL_da2 = 2*(self.a2 - y)
        da2_dz2 = self.sigmoid_derivative(self.z2)
        dz2_dW2 = self.a1

        # 梯度裁剪(防止爆炸)grad_W2 = np.clip(dz2_dW2.T.dot(dL_da2 * da2_dz2), -5, 5)

        # 隐藏层梯度
        dz2_da1 = self.W2
        dL_da1 = (dL_da2 * da2_dz2).dot(dz2_da1.T)
        da1_dz1 = self.sigmoid_derivative(self.z1)
        dz1_dW1 = X

        grad_W1 = np.clip(dz1_dW1.T.dot(dL_da1 * da1_dz1), -5, 5)

        # 权重更新(带学习率衰减)self.W2 -= learning_rate * grad_W2 / m
        self.W1 -= learning_rate * grad_W1 / m

避坑指南

  1. 权重初始化
  2. 过大权重会导致 Sigmoid 饱和(梯度消失)
  3. 推荐使用 Xavier 初始化:w = np.random.randn(n) * sqrt(1/n)

  4. 学习率选择

  5. 常用基准值:0.01~0.1
  6. 可配合指数衰减:lr = initial_lr * (0.95 ** epoch)

  7. 批量大小影响

  8. 大批量(如 256)需要更大学习率
  9. 小批量(如 32)更新更频繁但波动大

思考题

如何修改代码实现带动量的梯度下降?提示:需要维护速度变量 v,更新规则变为:

v = momentum * v - learning_rate * grad
w += v

通过这个实现,我们完整走通了 BP 算法的数学原理到代码落地的全过程。建议读者尝试用不同激活函数(如 ReLU)和优化器进行扩展实验,这将帮助深入理解深度学习训练的动态过程。

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