共计 2298 个字符,预计需要花费 6 分钟才能阅读完成。
背景介绍
BP(Backpropagation)神经网络是一种多层前馈神经网络,通过误差反向传播算法进行训练。反向传播算法是深度学习中最核心的优化方法之一,它通过计算损失函数对网络参数的梯度,利用梯度下降法来更新权重和偏置。理解反向传播的数学原理对于掌握深度学习至关重要。

数学推导
前向传播过程
对于一个简单的三层神经网络(输入层、隐藏层、输出层),前向传播过程可以表示为:
$$
\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
$$
反向传播推导
反向传播的核心是链式法则。我们需要计算损失函数对各层参数的梯度:
- 输出层误差:
$$
\delta^{(3)} = -(y – a^{(3)}) \odot f'(z^{(3)})
$$
- 隐藏层误差:
$$
\delta^{(2)} = (W^{(2)T}\delta^{(3)}) \odot f'(z^{(2)})
$$
- 参数梯度:
$$
\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 神经网络:
- 加载 MNIST 数据集
- 预处理数据(归一化等)
- 设计网络结构(输入层 784 维,隐藏层 128 维,输出层 10 维)
- 实现反向传播训练
- 评估模型性能
通过这个实战任务,可以加深对反向传播算法的理解,并掌握神经网络的实际应用技巧。
