共计 2690 个字符,预计需要花费 7 分钟才能阅读完成。
背景介绍
神经网络训练的核心在于通过调整权重参数,使得模型的预测输出尽可能接近真实值。这个过程分为两个主要步骤:前向传播和误差反向传播。前向传播负责计算网络的输出,而反向传播则根据输出误差来调整网络参数,使得误差逐渐减小。

数学推导
前向传播
给定一个神经网络,假设我们有一个输入层、一个隐藏层和一个输出层。前向传播的过程可以表示为:
-
输入层到隐藏层:
$$h = \sigma(W_1 x + b_1)$$
其中,$W_1$ 是权重矩阵,$b_1$ 是偏置向量,$\sigma$ 是激活函数(如 Sigmoid 或 ReLU)。 -
隐藏层到输出层:
$$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$ 是真实值。
反向传播
反向传播的核心是通过链式法则计算损失函数对权重和偏置的梯度。具体步骤如下:
-
计算输出层的误差:
$$\delta_2 = (y – t) \odot \sigma'(z_2)$$
其中,$z_2 = W_2 h + b_2$,$\odot$ 表示逐元素乘法。 -
计算隐藏层的误差:
$$\delta_1 = (W_2^T \delta_2) \odot \sigma'(z_1)$$
其中,$z_1 = W_1 x + b_1$。 -
更新权重和偏置:
$$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)
思考题
- 如何修改代码以支持更多隐藏层?
- 除了均方误差,还有哪些损失函数适用于分类问题?
- 如何利用 GPU 加速反向传播过程?
希望这篇文章能帮助你理解 BP 神经网络的反向传播机制,并能在实际项目中灵活应用。如有任何问题,欢迎留言讨论!
