共计 2269 个字符,预计需要花费 6 分钟才能阅读完成。
1. 神经网络前向传播基础
在开始反向传播之前,我们需要先明确前向传播的计算过程。假设我们有一个 L 层的神经网络,第 l 层的权重矩阵为 $W^{(l)}$,偏置向量为 $b^{(l)}$,激活函数为 $\sigma(\cdot)$。

对于第 l 层的输出 $a^{(l)}$,其计算过程为:
$$
z^{(l)} = W^{(l)}a^{(l-1)} + b^{(l)}
$$
$$
a^{(l)} = \sigma(z^{(l)})
$$
其中 $a^{(0)}$ 就是网络的输入 x。
2. 反向传播的数学推导
2.1 损失函数定义
假设我们使用均方误差 (MSE) 作为损失函数:
$$
J = \frac{1}{2}||y – a^{(L)}||^2
$$
其中 y 是真实标签,$a^{(L)}$ 是网络输出。
2.2 输出层误差计算
首先计算输出层的误差 $\delta^{(L)}$:
$$
\delta^{(L)} = \frac{\partial J}{\partial z^{(L)}} = (a^{(L)} – y) \odot \sigma'(z^{(L)})
$$
这里 $\odot$ 表示逐元素相乘。
2.3 隐藏层误差反向传播
对于第 l 层 (l < L) 的误差 $\delta^{(l)}$:
$$
\delta^{(l)} = ((W^{(l+1)})^T \delta^{(l+1)}) \odot \sigma'(z^{(l)})
$$
2.4 参数梯度计算
得到各层的误差后,就可以计算权重和偏置的梯度:
$$
\frac{\partial J}{\partial W^{(l)}} = \delta^{(l)}(a^{(l-1)})^T
$$
$$
\frac{\partial J}{\partial b^{(l)}} = \delta^{(l)}
$$
3. Python 实现示例
import numpy as np
class NeuralNetwork:
def __init__(self, layers):
self.layers = layers
self.weights = [np.random.randn(y, x)/np.sqrt(x)
for x, y in zip(layers[:-1], layers[1:])]
self.biases = [np.random.randn(y, 1) for y in layers[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 backward(self, x, y):
# 前向传播
activation = x
activations = [x]
zs = []
for w, b in zip(self.weights, self.biases):
z = np.dot(w, activation) + b
zs.append(z)
activation = self.sigmoid(z)
activations.append(activation)
# 反向传播
delta = (activations[-1] - y) * self.sigmoid_prime(zs[-1])
nabla_w = [np.zeros(w.shape) for w in self.weights]
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w[-1] = np.dot(delta, activations[-2].T)
nabla_b[-1] = delta
for l in range(2, len(self.layers)):
z = zs[-l]
sp = self.sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].T, delta) * sp
nabla_w[-l] = np.dot(delta, activations[-l-1].T)
nabla_b[-l] = delta
return (nabla_w, nabla_b)
4. 数值稳定性问题与解决方案
4.1 梯度消失 / 爆炸
在深层网络中,梯度可能在反向传播过程中指数级减小 (消失) 或增大(爆炸)。主要原因:
- 权重初始化不当
- 激活函数选择不当(如 sigmoid)
- 网络过深
解决方案:
- 使用 ReLU 等非饱和激活函数
- 采用 Xavier 或 He 权重初始化
- 使用批量归一化(BatchNorm)
- 引入残差连接
4.2 学习率选择
学习率过大可能导致震荡不收敛,过小则收敛缓慢。建议:
- 初始学习率设为 0.01 或 0.001
- 使用学习率衰减策略
- 尝试自适应优化器(Adam 等)
5. 超参数影响分析
- 批量大小:
- 大批量训练更稳定但可能陷入局部最优
- 小批量噪声更大但可能找到更好解
-
建议 32-256 之间
-
学习率:
- 需要与批量大小协调
- 大批量可用更大学习率
6. 最佳实践总结
- 使用 ReLU 激活函数避免梯度消失
- 采用 Xavier/He 初始化权重
- 实现梯度检查验证反向传播正确性
- 监控训练过程中的梯度范数
- 使用 Adam 等自适应优化器
- 添加批量归一化层
- 实现早停 (early stopping) 防止过拟合
7. 思考题
- 当使用交叉熵损失函数时,反向传播公式会有哪些变化?
- 如何修改反向传播算法实现 dropout 正则化?
- 在 CNN 中,反向传播算法需要做哪些调整?
希望通过本文的详细推导和实现示例,能够帮助读者深入理解反向传播算法的核心原理,并能够在实际项目中实现高效稳定的神经网络训练。
