共计 2211 个字符,预计需要花费 6 分钟才能阅读完成。
神经网络前向传播基础
在开始反向传播推导前,我们先明确神经网络前向传播的计算过程。假设一个简单的 3 层网络(输入层、隐藏层、输出层),其前向传播可表示为:

-
输入层到隐藏层:
$$z_h = W_{xh}X + b_h$$
$$a_h = \sigma(z_h)$$ -
隐藏层到输出层:
$$z_o = W_{ho}a_h + b_o$$
$$\hat{y} = \sigma(z_o)$$
其中 $\sigma$ 表示激活函数(如 Sigmoid),$W$ 为权重矩阵,$b$ 为偏置项。
反向传播公式推导
1. 损失函数定义
采用均方误差损失函数:
$$L = \frac{1}{2}(y – \hat{y})^2$$
2. 输出层梯度计算
-
计算损失对输出的偏导:
$$\frac{\partial L}{\partial \hat{y}} = \hat{y} – y$$ -
经过激活函数的梯度:
$$\frac{\partial L}{\partial z_o} = \frac{\partial L}{\partial \hat{y}} \cdot \sigma'(z_o)$$ -
权重和偏置的梯度:
$$\frac{\partial L}{\partial W_{ho}} = \frac{\partial L}{\partial z_o} \cdot a_h^T$$
$$\frac{\partial L}{\partial b_o} = \frac{\partial L}{\partial z_o}$$
3. 隐藏层梯度计算
-
反向传播误差:
$$\delta_h = W_{ho}^T \delta_o \odot \sigma'(z_h)$$ -
计算隐藏层参数梯度:
$$\frac{\partial L}{\partial W_{xh}} = \delta_h \cdot X^T$$
$$\frac{\partial L}{\partial b_h} = \delta_h$$
Python 实现示例
import numpy as np
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
# 初始化权重
self.Wxh = np.random.randn(hidden_size, input_size) * 0.01
self.Who = np.random.randn(output_size, hidden_size) * 0.01
self.bh = np.zeros((hidden_size, 1))
self.bo = np.zeros((output_size, 1))
def sigmoid(self, z):
return 1 / (1 + np.exp(-z))
def forward(self, X):
# 前向传播
self.z_h = np.dot(self.Wxh, X) + self.bh
self.a_h = self.sigmoid(self.z_h)
self.z_o = np.dot(self.Who, self.a_h) + self.bo
self.a_o = self.sigmoid(self.z_o)
return self.a_o
def backward(self, X, y, learning_rate=0.1):
# 反向传播
m = X.shape[1] # 样本数量
# 输出层误差
dL_dao = self.a_o - y
dao_dzo = self.a_o * (1 - self.a_o) # sigmoid 导数
dL_dzo = dL_dao * dao_dzo
# 隐藏层误差
dL_dah = np.dot(self.Who.T, dL_dzo)
dah_dzh = self.a_h * (1 - self.a_h)
dL_dzh = dL_dah * dah_dzh
# 参数更新
self.Who -= learning_rate * np.dot(dL_dzo, self.a_h.T) / m
self.bo -= learning_rate * np.mean(dL_dzo, axis=1, keepdims=True)
self.Wxh -= learning_rate * np.dot(dL_dzh, X.T) / m
self.bh -= learning_rate * np.mean(dL_dzh, axis=1, keepdims=True)
数值稳定性问题与解决方案
1. 梯度消失问题
当使用 Sigmoid 激活函数时,其导数最大值为 0.25,多层叠加会导致梯度指数级减小。解决方案:
- 使用 ReLU 及其变体作为激活函数
- 采用残差连接(ResNet)
- 使用 Batch Normalization
2. 梯度爆炸问题
权重初始化过大或网络过深时可能出现。解决方案:
- 梯度裁剪(Gradient Clipping)
- 合理的权重初始化(如 Xavier 初始化)
- 使用权重正则化
超参数调优建议
- 学习率选择:
- 常用范围:0.001 到 0.1
-
可采用学习率衰减策略
-
批量大小影响:
- 小批量(32-256)通常效果较好
-
大批量训练速度更快但可能降低泛化能力
-
其他技巧:
- 使用动量(Momentum)加速收敛
- 尝试自适应优化器(Adam, RMSprop)
最佳实践要点
- 始终监控训练 / 验证损失曲线
- 实现梯度检查(Gradient Checking)验证反向传播正确性
- 对输入数据进行标准化处理
- 使用早停(Early Stopping)防止过拟合
- 记录完整的超参数配置便于复现
思考题
- 如何修改反向传播算法使其支持 LeakyReLU 激活函数?
- 当网络层数增加到 100 层时,除了梯度问题,还需要考虑哪些因素?
- 对比分析批量梯度下降、随机梯度下降和小批量梯度下降的优缺点?
