共计 2295 个字符,预计需要花费 6 分钟才能阅读完成。
前向传播与反向传播的必要性
BP 神经网络通过前向传播计算预测值,再通过反向传播调整权重参数。前向传播的公式为:

$$
z^{(l)} = W^{(l)}a^{(l-1)} + b^{(l)}\
a^{(l)} = \sigma(z^{(l)})
$$
其中 $l$ 表示层数,$\sigma$ 为激活函数。前向传播只能得到预测结果,无法自动优化权重,因此需要通过反向传播计算梯度来更新参数。
链式法则的梯度计算
反向传播的核心是链式法则。以均方误差损失函数为例:
$$
\frac{\partial L}{\partial W^{(l)}} = \frac{\partial L}{\partial a^{(l)}} \frac{\partial a^{(l)}}{\partial z^{(l)}} \frac{\partial z^{(l)}}{\partial W^{(l)}}
$$
具体推导过程:
-
输出层误差:
$$
\delta^{(L)} = \frac{\partial L}{\partial a^{(L)}} \odot \sigma'(z^{(L)})
$$ -
隐藏层误差:
$$
\delta^{(l)} = (W^{(l+1)T}\delta^{(l+1)}) \odot \sigma'(z^{(l)})
$$ -
参数梯度:
$$
\frac{\partial L}{\partial W^{(l)}} = \delta^{(l)}a^{(l-1)T}\
\frac{\partial L}{\partial b^{(l)}} = \delta^{(l)}
$$
激活函数对比
常见激活函数的导数特性:
-
Sigmoid:
$$
\sigma'(z) = \sigma(z)(1-\sigma(z))
$$
容易导致梯度消失 -
ReLU:
$$
\sigma'(z) = \begin{cases}
1 & z > 0\
0 & z \leq 0
\end{cases}
$$
缓解梯度消失但可能导致神经元死亡 -
LeakyReLU:
$$
\sigma'(z) = \begin{cases}
1 & z > 0\
\alpha & z \leq 0
\end{cases}
$$
解决了神经元死亡问题
Python 实现
import numpy as np
class NeuralNetwork:
def __init__(self, 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 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):
# 反向传播
nabla_w = [np.zeros(w.shape) for w in self.weights]
nabla_b = [np.zeros(b.shape) for b in self.biases]
# 前向计算各层激活值
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_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].T)
# 隐藏层误差
for l in range(2, len(self.weights)+1):
z = zs[-l]
sp = self.sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].T, delta) * sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].T)
return nabla_w, nabla_b
def sigmoid(self, z):
return 1.0/(1.0+np.exp(-z))
def sigmoid_prime(self, z):
return self.sigmoid(z)*(1-self.sigmoid(z))
训练技巧
- 学习率选择:
- 太大导致震荡不收敛
- 太小导致训练缓慢
-
建议初始值为 0.01,使用学习率衰减
-
权重初始化:
- Xavier 初始化:$W \sim N(0, \sqrt{\frac{2}{n_{in}+n_{out}}})$
-
He 初始化:$W \sim N(0, \sqrt{\frac{2}{n_{in}}})$
-
批量归一化:
$$
\hat{x} = \frac{x – E[x]}{\sqrt{Var[x] + \epsilon}}\
y = \gamma \hat{x} + \beta
$$
有效缓解梯度消失 / 爆炸
梯度问题分析
梯度消失的原因:
– 深度网络中层间梯度连续相乘
– 使用 Sigmoid 等导数小于 1 的函数
梯度爆炸的原因:
– 权重初始化值过大
– 网络层间梯度连续相乘
解决方案:
1. 使用 ReLU 等激活函数
2. 合理的权重初始化
3. 梯度裁剪
4. 残差连接
思考题
如何将反向传播算法扩展到卷积神经网络?考虑:
1. 卷积核权重的梯度计算
2. 池化层的反向传播
3. 参数共享机制的影响
4. 感受野与梯度传播的关系
