共计 1508 个字符,预计需要花费 4 分钟才能阅读完成。
数学基础与链式法则
反向传播算法的核心是链式求导法则。对于复合函数 $f(g(x))$,其导数为 $\frac{df}{dx} = \frac{df}{dg} \cdot \frac{dg}{dx}$。在神经网络中,损失函数 $L$ 对参数 $w$ 的梯度计算可以表示为:

$$
\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w}
$$
其中 $z$ 是线性变换结果,$a$ 是激活函数输出。这个链条可以一直延伸到网络的输出层。
反向传播流程
- 前向传播 :计算每层的激活值
- 计算损失 :比较预测值与真实值
- 反向传播 :
- 计算输出层误差
- 逐层回传误差信号
- 计算各层参数梯度
- 参数更新 :使用优化器调整权重
优化器对比
-
SGD:直接使用梯度更新,简单但容易震荡
$$
w_{t+1} = w_t – \eta \nabla L(w_t)
$$ -
Momentum:引入速度项缓解震荡
$$
v_{t+1} = \gamma v_t + \eta \nabla L(w_t)
$$
$$
w_{t+1} = w_t – v_{t+1}
$$ -
Adam:自适应调整学习率
$$
m_t = \beta_1 m_{t-1} + (1-\beta_1)g_t
$$
$$
v_t = \beta_2 v_{t-1} + (1-\beta_2)g_t^2
$$
Python 实现
import numpy as np
class NeuralNetwork:
def __init__(self, layers):
self.weights = [np.random.randn(y, x)*0.01
for x, y in zip(layers[:-1], layers[1:])]
def forward(self, x):
for w in self.weights:
x = np.dot(w, x)
x = 1/(1+np.exp(-x)) # Sigmoid
return x
def backward(self, x, y_true):
# 前向传播缓存
activations = [x]
zs = []
# 反向传播
delta = (y_pred - y_true) * y_pred * (1 - y_pred)
grads = []
# 计算各层梯度
for i in reversed(range(len(self.weights))):
grads.append(np.outer(delta, activations[i]))
if i > 0: # 不计算输入层梯度
delta = np.dot(self.weights[i].T, delta) * \
activations[i] * (1 - activations[i])
return grads[::-1] # 反转梯度顺序
训练参数影响
- 学习率 :过大导致震荡,过小收敛慢
- 经验值:0.001-0.1
-
学习率衰减策略:step decay, cosine
-
批量大小 :
- 小批量(32-256)适合非凸优化
- 大批量需要调整学习率
梯度问题解决方案
- 梯度消失 :
- 使用 ReLU 激活函数
-
残差连接
-
梯度爆炸 :
- 梯度裁剪
- 权重归一化
生产实践建议
- 权重初始化 :
- Xavier 初始化:$\text{Var}(w) = 1/n_{in}$
-
He 初始化:$\text{Var}(w) = 2/n_{in}$
-
正则化 :
- L2 正则:$L = L_0 + \lambda\sum w^2$
-
Dropout:训练时随机失活神经元
-
批量归一化 :
- 加速训练
- 提高模型鲁棒性
开放问题
- 如何设计自适应网络结构,使反向传播路径可动态调整?
- 在联邦学习场景下,如何实现安全高效的分布式反向传播?
通过系统实现和调优,bp 算法可以支撑复杂的深度网络训练。建议读者从简单网络开始,逐步增加复杂度观察训练动态。
