BP神经网络反向传播算法公式推导:从数学原理到Python实现

1次阅读
没有评论

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

image.webp

反向传播算法是神经网络训练的基石,它通过高效计算每个参数的梯度,指导网络权重的迭代更新。理解其数学本质对掌握深度学习至关重要。

BP 神经网络反向传播算法公式推导:从数学原理到 Python 实现

原理推导

网络结构与符号定义

假设一个单隐藏层网络结构如下:

  • 输入层:$\mathbf{x} = [x_1,…,x_n]^T$
  • 隐藏层:$\mathbf{h} = sigmoid(\mathbf{W}_1\mathbf{x}+\mathbf{b}_1)$
  • 输出层:$\mathbf{\hat{y}} = softmax(\mathbf{W}_2\mathbf{h}+\mathbf{b}_2)$
  • 损失函数:交叉熵 $L=-\sum y_i\log\hat{y}_i$

链式求导过程

  1. 输出层梯度计算:

$$\frac{\partial L}{\partial \mathbf{W}_2} = \frac{\partial L}{\partial \mathbf{\hat{y}}}\n\frac{\partial \mathbf{\hat{y}}}{\partial \mathbf{z}_2}\n\frac{\partial \mathbf{z}_2}{\partial \mathbf{W}_2} = (\mathbf{\hat{y}}-\mathbf{y})\mathbf{h}^T$$

  1. 隐藏层梯度传播:

$$\frac{\partial L}{\partial \mathbf{W}_1} = \frac{\partial L}{\partial \mathbf{h}}\n\frac{\partial \mathbf{h}}{\partial \mathbf{z}_1}\n\frac{\partial \mathbf{z}_1}{\partial \mathbf{W}_1} = \mathbf{W}_2^T(\mathbf{\hat{y}}-\mathbf{y}) \odot \mathbf{h} \odot (1-\mathbf{h}) \mathbf{x}^T$$

其中 $\odot$ 表示逐元素相乘,sigmoid 导数特性被巧妙利用。

代码实现

import numpy as np

def sigmoid(x):
    return 1/(1+np.exp(-x))

# 前向传播
h = sigmoid(np.dot(W1, x) + b1)
y_hat = softmax(np.dot(W2, h) + b2)

# 反向传播
def backward(x, y, h, y_hat, W2):
    # 输出层梯度
    grad_z2 = y_hat - y
    grad_W2 = np.outer(grad_z2, h)  # 向量外积替代矩阵乘法

    # 隐藏层梯度
    grad_h = np.dot(W2.T, grad_z2)
    grad_z1 = grad_h * h * (1-h)    # 激活函数导数
    grad_W1 = np.outer(grad_z1, x)

    return grad_W1, grad_W2

关键实现技巧:

  • 使用 np.outer 实现向量外积
  • 利用广播机制避免显式循环
  • 保持矩阵维度一致性检查

调试技巧

学习率选择

  1. 初始建议值:0.001-0.1 范围尝试
  2. 观察损失曲线:震荡需调小,下降过慢需调大
  3. 自适应方法:AdaGrad/RMSProp 自动调整

梯度检验

def grad_check(x, y, W, f):
    eps = 1e-5
    numeric_grad = np.zeros_like(W)
    for i in range(W.size):
        old_val = W.flat[i]
        W.flat[i] = old_val + eps
        fxh1 = f(x, y)
        W.flat[i] = old_val - eps
        fxh2 = f(x, y)
        numeric_grad.flat[i] = (fxh1 - fxh2) / (2*eps)
    return numeric_grad

批量训练实现

batch_grad_W1 = np.zeros_like(W1)
for x_batch, y_batch in dataloader:
    grad_W1, _ = backward(x_batch, y_batch)
    batch_grad_W1 += grad_W1
batch_grad_W1 /= batch_size

总结与思考

通过本文的公式推导和代码实现,我们揭示了反向传播如何通过链式法则将误差从输出层传递到隐藏层。建议尝试以下改进:

  1. 添加 ReLU 激活函数版本
  2. 实现带动量的梯度下降
  3. 扩展为多层隐藏层结构

思考题答案提示:动量更新需要维护历史梯度变量:
v = beta*v + (1-beta)*grad
W -= lr*v

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