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

1次阅读
没有评论

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

image.webp

背景介绍

神经网络训练的核心在于通过调整权重参数,使得模型的预测输出尽可能接近真实值。这个过程分为两个主要步骤:前向传播和误差反向传播。前向传播负责计算网络的输出,而反向传播则根据输出误差来调整网络参数,使得误差逐渐减小。

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

数学推导

前向传播

给定一个神经网络,假设我们有一个输入层、一个隐藏层和一个输出层。前向传播的过程可以表示为:

  1. 输入层到隐藏层:
    $$h = \sigma(W_1 x + b_1)$$
    其中,$W_1$ 是权重矩阵,$b_1$ 是偏置向量,$\sigma$ 是激活函数(如 Sigmoid 或 ReLU)。

  2. 隐藏层到输出层:
    $$y = \sigma(W_2 h + b_2)$$
    其中,$W_2$ 是权重矩阵,$b_2$ 是偏置向量。

误差函数

为了衡量模型的预测误差,我们使用均方误差(MSE)作为损失函数:
$$E = \frac{1}{2} \sum_{i=1}^{n} (y_i – t_i)^2$$
其中,$y_i$ 是预测值,$t_i$ 是真实值。

反向传播

反向传播的核心是通过链式法则计算损失函数对权重和偏置的梯度。具体步骤如下:

  1. 计算输出层的误差:
    $$\delta_2 = (y – t) \odot \sigma'(z_2)$$
    其中,$z_2 = W_2 h + b_2$,$\odot$ 表示逐元素乘法。

  2. 计算隐藏层的误差:
    $$\delta_1 = (W_2^T \delta_2) \odot \sigma'(z_1)$$
    其中,$z_1 = W_1 x + b_1$。

  3. 更新权重和偏置:
    $$W_2 = W_2 – \eta \delta_2 h^T$$
    $$b_2 = b_2 – \eta \delta_2$$
    $$W_1 = W_1 – \eta \delta_1 x^T$$
    $$b_1 = b_1 – \eta \delta_1$$
    其中,$\eta$ 是学习率。

代码实现

以下是一个简单的 Python 实现,展示了如何实现 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(hidden_size)
        self.W2 = np.random.randn(hidden_size, output_size)
        self.b2 = np.zeros(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.h = self.sigmoid(np.dot(x, self.W1) + self.b1)
        self.y = self.sigmoid(np.dot(self.h, self.W2) + self.b2)
        return self.y

    def backward(self, x, t, learning_rate):
        # Output layer error
        delta2 = (self.y - t) * self.sigmoid_derivative(self.y)
        # Hidden layer error
        delta1 = np.dot(delta2, self.W2.T) * self.sigmoid_derivative(self.h)
        # Update weights and biases
        self.W2 -= learning_rate * np.dot(self.h.T, delta2)
        self.b2 -= learning_rate * np.sum(delta2, axis=0)
        self.W1 -= learning_rate * np.dot(x.T, delta1)
        self.b1 -= learning_rate * np.sum(delta1, axis=0)

优化技巧

学习率调整

学习率的选择对训练效果至关重要。太大可能导致震荡,太小则收敛缓慢。可以尝试动态调整学习率,如随着训练轮次逐渐减小。

动量法

动量法通过引入一个动量项,加速收敛并减少震荡:
$$v = \beta v + (1 – \beta) \nabla W$$
$$W = W – \eta v$$
其中,$\beta$ 是动量系数,通常取 0.9。

实战示例:手写数字识别

我们可以使用 MNIST 数据集来验证我们的实现。以下是简单的训练流程:

from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split

# Load MNIST data
mnist = fetch_openml('mnist_784')
X, y = mnist.data / 255.0, mnist.target.astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# One-hot encode labels
y_train_encoded = np.eye(10)[y_train]
y_test_encoded = np.eye(10)[y_test]

# Initialize network
nn = NeuralNetwork(784, 128, 10)

# Training loop
for epoch in range(10):
    for i in range(0, len(X_train), 32):
        batch_X = X_train[i:i+32]
        batch_y = y_train_encoded[i:i+32]
        nn.forward(batch_X)
        nn.backward(batch_X, batch_y, 0.01)
    # Evaluate
    pred = nn.forward(X_test)
    accuracy = np.mean(np.argmax(pred, axis=1) == y_test)
    print(f'Epoch {epoch}, Accuracy: {accuracy:.4f}')

常见问题

梯度消失 / 爆炸

当网络层数较深时,梯度可能在传播过程中变得非常小(消失)或非常大(爆炸)。解决方案包括:

  • 使用 ReLU 等激活函数
  • 批归一化(Batch Normalization)
  • 残差连接(Residual Connections)

思考题

  1. 如何修改代码以支持更多隐藏层?
  2. 除了均方误差,还有哪些损失函数适用于分类问题?
  3. 如何利用 GPU 加速反向传播过程?

希望这篇文章能帮助你理解 BP 神经网络的反向传播机制,并能在实际项目中灵活应用。如有任何问题,欢迎留言讨论!

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