BP神经网络反向传播推导过程详解:从数学原理到Python实现

1次阅读
没有评论

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

image.webp

1. 从单层感知机到多层网络

1.1 单层感知机的梯度下降

单层感知机的输出可表示为:

BP 神经网络反向传播推导过程详解:从数学原理到 Python 实现

y = \sigma(w^Tx + b)

其中 σ 为激活函数。采用均方误差损失函数:

L = \frac{1}{2}(y - t)^2

权重更新公式通过梯度下降可得:

w_{new} = w - \eta \frac{\partial L}{\partial w}

1.2 扩展到多层网络的链式法则

对于三层网络(输入层→隐藏层→输出层),需要逐层反向传播误差:

\frac{\partial L}{\partial w_{ij}^{(2)}} = \frac{\partial L}{\partial z_j^{(3)}}\frac{\partial z_j^{(3)}}{\partial w_{ij}^{(2)}}

其中 z 表示未激活的加权和。

2. 完整推导过程

2.1 前向传播计算

定义网络各层计算(以 Sigmoid 激活函数为例):

def forward(x):
    z1 = np.dot(x, W1) + b1
    a1 = sigmoid(z1)
    z2 = np.dot(a1, W2) + b2
    a2 = sigmoid(z2)
    return a2

2.2 损失函数定义

交叉熵损失函数:

L = -\frac{1}{N}\sum_{i=1}^N [t_i\ln(y_i)+(1-t_i)\ln(1-y_i)]

2.3 反向传播梯度计算

输出层梯度:

\delta^{(3)} = (y - t) \odot \sigma'(z^{(3)})

隐藏层梯度:

\delta^{(2)} = (W^{(2)}\delta^{(3)}) \odot \sigma'(z^{(2)})

2.4 权重更新实现

def backward(x, y, learning_rate):
    # 前向传播
    z1, a1, z2, a2 = forward(x)

    # 输出层梯度
    delta2 = (a2 - y) * sigmoid_derivative(z2)
    dW2 = np.dot(a1.T, delta2)

    # 隐藏层梯度
    delta1 = np.dot(delta2, W2.T) * sigmoid_derivative(z1)
    dW1 = np.dot(x.T, delta1)

    # 更新参数
    W1 -= learning_rate * dW1
    W2 -= learning_rate * dW2

3. 避坑指南

3.1 梯度消失 / 爆炸问题

  • 现象:深层网络训练时梯度指数级减小或增大
  • 解决方案:
  • 使用 ReLU 等改良激活函数
  • 采用 Batch Normalization
  • 梯度裁剪(Gradient Clipping)

3.2 学习率设置

  • 推荐初始值:0.001-0.1
  • 可采用学习率衰减策略:
    lr = initial_lr * (1 + decay_rate * epoch)^(-1)

3.3 激活函数选择

函数类型 适用场景 注意事项
Sigmoid 二分类输出层 易梯度饱和
ReLU 隐藏层 可能出现神经元死亡
Tanh 隐藏层 输出零中心化

4. 思考题

  1. 如何通过数值梯度检验验证反向传播实现的正确性?
  2. 批量训练时梯度计算与在线训练有何本质区别?
  3. Adam 等二阶优化算法在哪些场景下优于 SGD?会带来哪些额外计算开销?

5. 完整代码实现

import numpy as np

class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        self.W1 = np.random.randn(input_size, hidden_size)
        self.W2 = np.random.randn(hidden_size, output_size)

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

    def sigmoid_derivative(self, x):
        return self.sigmoid(x)*(1-self.sigmoid(x))

    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]

        # 输出层误差
        delta2 = (self.a2 - y) * self.sigmoid_derivative(self.z2)
        dW2 = np.dot(self.a1.T, delta2)

        # 隐藏层误差
        delta1 = np.dot(delta2, self.W2.T) * self.sigmoid_derivative(self.z1)
        dW1 = np.dot(x.T, delta1)

        # 更新参数
        self.W1 -= learning_rate * dW1
        self.W2 -= learning_rate * dW2

理解反向传播的关键在于掌握链式法则的应用,建议读者通过手动计算一个 3 层网络的导数来加深理解。在实践中,使用自动微分框架(如 PyTorch)可以避免手动推导的复杂性,但理解底层原理对于调试网络和设计新架构至关重要。

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