共计 1978 个字符,预计需要花费 5 分钟才能阅读完成。
为什么需要反向传播算法
在神经网络中,我们需要通过调整权重来最小化损失函数。这个过程类似于教一个孩子学习:每次犯错后,我们需要告诉孩子错在哪里以及如何改正。反向传播算法就是神经网络中的这个 ” 教学 ” 过程。

- 前向传播:输入数据通过网络层层传递,最终产生预测结果
- 反向传播:根据预测结果与真实值的差异,从输出层反向计算各层参数的梯度
- 权重更新:根据梯度下降算法调整网络参数
BF 反向传播算法数学推导
让我们从一个简单的 3 层神经网络 (输入层、隐藏层、输出层) 开始推导。假设我们使用均方误差 (MSE) 作为损失函数:
$$ L = \frac{1}{2}(y – \hat{y})^2 $$
1. 输出层梯度计算
对于输出层的权重 $w_{ij}$,我们需要计算 $\frac{\partial L}{\partial w_{ij}}$:
- 计算损失对输出的偏导:$\frac{\partial L}{\partial \hat{y}} = \hat{y} – y$
- 计算输出对激活的偏导:$\frac{\partial \hat{y}}{\partial z} = \sigma'(z)$
- 计算激活对权重的偏导:$\frac{\partial z}{\partial w_{ij}} = a_j$
根据链式法则,最终梯度为:
$$ \frac{\partial L}{\partial w_{ij}} = (\hat{y} – y) \cdot \sigma'(z) \cdot a_j $$
2. 隐藏层梯度计算
对于隐藏层的权重 $w_{jk}$,我们需要计算 $\frac{\partial L}{\partial w_{jk}}$。这需要反向传播误差:
- 计算隐藏层误差 $\delta_j$:$\delta_j = \sum_i \delta_i w_{ij} \cdot \sigma'(z_j)$
- 然后计算权重梯度:$\frac{\partial L}{\partial w_{jk}} = \delta_j \cdot a_k$
Python 实现
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.W2 = np.random.randn(hidden_size, 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.a1 = self.sigmoid(self.z1)
self.z2 = np.dot(self.a1, self.W2)
self.a2 = self.sigmoid(self.z2)
return self.a2
def backward(self, X, y, output, learning_rate):
# 反向传播
error = output - y
# 输出层梯度
delta2 = error * self.sigmoid_derivative(output)
dW2 = np.dot(self.a1.T, delta2)
# 隐藏层梯度
error_hidden = np.dot(delta2, self.W2.T)
delta1 = error_hidden * self.sigmoid_derivative(self.a1)
dW1 = np.dot(X.T, delta1)
# 更新权重
self.W2 -= learning_rate * dW2
self.W1 -= learning_rate * dW1
复杂度分析
- 时间复杂度:O(W),其中 W 是网络中权重的总数
- 空间复杂度:O(W),需要存储所有权重和中间激活值
避坑指南
- 梯度消失问题:当使用 sigmoid 激活函数时,深层网络容易出现梯度消失
- 解决方案:使用 ReLU 等激活函数
- 学习率选择不当:太大导致震荡,太小导致收敛慢
- 解决方案:使用学习率衰减或自适应优化器
- 权重初始化问题:全部初始化为 0 会导致对称性问题
- 解决方案:使用随机初始化
- 数值不稳定:中间计算结果可能溢出
- 解决方案:使用数值稳定的实现方式
思考题
- 如果我们将激活函数从 sigmoid 改为 ReLU,反向传播的推导会发生什么变化?
- 对于分类问题,为什么我们通常不使用 MSE 而使用交叉熵损失函数?
- 当网络很深时,除了梯度消失问题,还可能遇到什么问题?如何解决?
总结
通过本文的推导和实现,你应该对反向传播算法有了更深入的理解。理解这些数学原理不仅可以帮助你更好地调试神经网络,还能为后续学习更复杂的模型打下坚实基础。建议你尝试扩展这个简单网络,比如增加更多隐藏层或使用不同的激活函数,观察训练效果的变化。
正文完
