共计 2402 个字符,预计需要花费 7 分钟才能阅读完成。
神经网络基础回顾
BP 神经网络是一种典型的前馈神经网络,它由输入层、隐藏层和输出层组成。训练神经网络的核心目标是通过调整权重和偏置,使得网络的输出尽可能接近真实值。而反向传播算法就是实现这一目标的关键技术。

反向传播的必要性在于它提供了一种高效计算梯度的方法。通过链式法则,我们可以从输出层开始,逐层反向计算误差对各个参数的梯度,然后使用梯度下降法更新参数。
反向传播数学推导
前向传播
首先我们定义前向传播的过程。对于一个 L 层的神经网络,第 l 层的输出可以表示为:
$$a^l = \sigma(z^l) = \sigma(W^l a^{l-1} + b^l)$$
其中 $\sigma$ 是激活函数,$W^l$ 是权重矩阵,$b^l$ 是偏置向量。
损失函数
我们使用平方误差作为损失函数:
$$J = \frac{1}{2} \sum_{i=1}^n (y_i – a^L_i)^2$$
反向传播
反向传播的核心是计算损失函数对各层参数的梯度。我们首先定义误差项 $\delta^l$:
$$\delta^l = \frac{\partial J}{\partial z^l}$$
对于输出层 (L 层):
$$\delta^L = \frac{\partial J}{\partial a^L} \odot \sigma'(z^L) = (a^L – y) \odot \sigma'(z^L)$$
对于隐藏层 (l 层):
$$\delta^l = ((W^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l)$$
然后可以计算各参数的梯度:
$$\frac{\partial J}{\partial W^l} = \delta^l (a^{l-1})^T$$
$$\frac{\partial J}{\partial b^l} = \delta^l$$
Python 代码实现
import numpy as np
class NeuralNetwork:
def __init__(self, layer_sizes):
self.layer_sizes = layer_sizes
self.weights = [np.random.randn(y, x) * 0.1
for x, y in zip(layer_sizes[:-1], layer_sizes[1:])]
self.biases = [np.random.randn(y, 1) * 0.1
for y in layer_sizes[1:]]
def sigmoid(self, z):
return 1 / (1 + np.exp(-z))
def sigmoid_prime(self, z):
return self.sigmoid(z) * (1 - self.sigmoid(z))
def forward(self, x):
a = x
for w, b in zip(self.weights, self.biases):
z = np.dot(w, a) + b
a = self.sigmoid(z)
return a
def train(self, X, y, epochs=1000, learning_rate=0.1):
for epoch in range(epochs):
# 初始化梯度
weight_gradients = [np.zeros(w.shape) for w in self.weights]
bias_gradients = [np.zeros(b.shape) for b in self.biases]
# 对每个样本进行训练
for x, target in zip(X, y):
# 前向传播
activations = [x]
zs = []
for w, b in zip(self.weights, self.biases):
z = np.dot(w, activations[-1]) + b
zs.append(z)
activations.append(self.sigmoid(z))
# 反向传播
delta = (activations[-1] - target) * self.sigmoid_prime(zs[-1])
bias_gradients[-1] += delta
weight_gradients[-1] += np.dot(delta, activations[-2].T)
for l in range(2, len(self.layer_sizes)):
z = zs[-l]
sp = self.sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].T, delta) * sp
bias_gradients[-l] += delta
weight_gradients[-l] += np.dot(delta, activations[-l-1].T)
# 更新参数
for i in range(len(self.weights)):
self.weights[i] -= learning_rate * weight_gradients[i] / len(X)
self.biases[i] -= learning_rate * bias_gradients[i] / len(X)
具体计算例题演示
假设我们有一个简单的 2 -2- 1 神经网络,输入为 [0.5, 0.8],目标输出为 [0.7]。我们随机初始化权重和偏置:
- 前向传播计算:
- 输入层到隐藏层计算
-
隐藏层到输出层计算
-
计算输出误差
-
反向传播:
- 计算输出层梯度
-
计算隐藏层梯度
-
更新参数
常见问题与优化建议
梯度消失 / 爆炸
梯度消失和爆炸是训练深层网络时的常见问题。解决方案包括:
- 使用 ReLU 等激活函数
- 权重初始化技巧
- Batch Normalization
学习率选择
学习率太大可能导致震荡,太小则收敛慢。可以尝试:
- 学习率衰减
- 自适应优化器 (Adam, RMSprop)
性能优化
- 使用向量化计算
- 利用 GPU 加速
- 小批量训练
总结与思考
通过本文,我们详细探讨了 BP 神经网络的反向传播算法,从数学推导到 Python 实现。在实际应用中,还需要考虑很多工程细节和调参技巧。
思考题:如何将反向传播算法应用到卷积神经网络中?关键在于理解卷积层和池化层的反向传播计算方式。
