BP神经网络误差反向传播推导:从数学原理到代码实现

1次阅读
没有评论

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

image.webp

背景介绍

BP(Backpropagation)神经网络是一种多层前馈神经网络,通过误差反向传播算法进行训练。反向传播算法是深度学习中最核心的优化方法之一,它通过计算损失函数对网络参数的梯度,利用梯度下降法来更新权重和偏置。理解反向传播的数学原理对于掌握深度学习至关重要。

BP 神经网络误差反向传播推导:从数学原理到代码实现

数学推导

前向传播过程

对于一个简单的三层神经网络(输入层、隐藏层、输出层),前向传播过程可以表示为:

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

其中 $f(\cdot)$ 是激活函数。

损失函数定义

我们使用均方误差(MSE)作为损失函数:

$$
J(W,b) = \frac{1}{2} \sum_{i=1}^m (y_i – a_i^{(3)})^2
$$

反向传播推导

反向传播的核心是链式法则。我们需要计算损失函数对各层参数的梯度:

  1. 输出层误差:

$$
\delta^{(3)} = -(y – a^{(3)}) \odot f'(z^{(3)})
$$

  1. 隐藏层误差:

$$
\delta^{(2)} = (W^{(2)T}\delta^{(3)}) \odot f'(z^{(2)})
$$

  1. 参数梯度:

$$
\begin{aligned}
\frac{\partial J}{\partial W^{(2)}} &= \delta^{(3)} a^{(2)T} \
\frac{\partial J}{\partial b^{(2)}} &= \delta^{(3)} \
\frac{\partial J}{\partial W^{(1)}} &= \delta^{(2)} x^T \
\frac{\partial J}{\partial b^{(1)}} &= \delta^{(2)}
\end{aligned}
$$

代码实现

下面是一个简单的 BP 神经网络实现,包含完整的反向传播过程:

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.b1 = np.zeros((1, hidden_size))
        self.W2 = np.random.randn(hidden_size, output_size)
        self.b2 = np.zeros((1, output_size))

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

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

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

    def backward(self, X, y, output, learning_rate):
        # 计算输出层误差
        self.error = y - output
        self.delta2 = self.error * self.sigmoid_derivative(output)

        # 计算隐藏层误差
        self.delta1 = np.dot(self.delta2, self.W2.T) * self.sigmoid_derivative(self.a1)

        # 更新权重和偏置
        self.W2 += learning_rate * np.dot(self.a1.T, self.delta2)
        self.b2 += learning_rate * np.sum(self.delta2, axis=0, keepdims=True)
        self.W1 += learning_rate * np.dot(X.T, self.delta1)
        self.b1 += learning_rate * np.sum(self.delta1, axis=0)

    def train(self, X, y, epochs, learning_rate):
        for i in range(epochs):
            output = self.forward(X)
            self.backward(X, y, output, learning_rate)

常见问题

梯度消失 / 爆炸

梯度消失发生在深层网络中,当梯度通过多个激活函数传递时,如果激活函数的导数小于 1,梯度会指数级减小。梯度爆炸则相反,当导数大于 1 时梯度会指数级增大。解决方案包括:

  • 使用 ReLU 等激活函数
  • 使用批归一化
  • 合理的权重初始化

激活函数选择

常用的激活函数包括:

  • Sigmoid:输出在 0 - 1 之间,但容易引起梯度消失
  • Tanh:输出在 - 1 到 1 之间
  • ReLU:计算简单,能缓解梯度消失问题

优化建议

学习率调整

  • 使用学习率衰减策略
  • 尝试自适应优化器如 Adam

权重初始化

  • Xavier 初始化:适用于 Sigmoid/Tanh
  • He 初始化:适用于 ReLU

正则化技术

  • L1/L2 正则化
  • Dropout
  • 早停法

实践建议

尝试在 MNIST 手写数字识别任务上应用 BP 神经网络:

  1. 加载 MNIST 数据集
  2. 预处理数据(归一化等)
  3. 设计网络结构(输入层 784 维,隐藏层 128 维,输出层 10 维)
  4. 实现反向传播训练
  5. 评估模型性能

通过这个实战任务,可以加深对反向传播算法的理解,并掌握神经网络的实际应用技巧。

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