共计 2473 个字符,预计需要花费 7 分钟才能阅读完成。
神经网络基础与反向传播的必要性
神经网络通过堆叠神经元层实现复杂函数拟合。前向传播计算预测值时,每个神经元的输出为:

$$
a_j^l = \sigma(\sum_k w_{jk}^l a_k^{l-1} + b_j^l)
$$
其中 $\sigma$ 为激活函数,$w$ 和 $b$ 分别代表权重和偏置。训练的核心目标是调整这些参数使得损失函数 $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$$
Python 实现详解
import numpy as np
class NeuralNetwork:
def __init__(self, layers):
# 初始化权重(He 初始化)self.weights = [np.random.randn(y, x)/np.sqrt(x)
for x, y in zip(layers[:-1], layers[1:])]
self.biases = [np.zeros((y, 1)) for y in layers[1:]]
def forward(self, x):
"""前向传播计算"""
for w, b in zip(self.weights, self.biases):
x = sigmoid(np.dot(w, x) + b)
return x
def backprop(self, x, y):
"""反向传播计算梯度"""
# 存储各层激活值和加权输入
activations = [x]
zs = []
# 前向传播
for w, b in zip(self.weights, self.biases):
z = np.dot(w, activations[-1]) + b
zs.append(z)
activations.append(sigmoid(z))
# 反向传播
delta = (activations[-1] - y) * sigmoid_prime(zs[-1])
nabla_w = [np.zeros_like(w) for w in self.weights]
nabla_b = [np.zeros_like(b) 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.weights)+1):
z = zs[-l]
delta = np.dot(self.weights[-l+1].T, delta) * sigmoid_prime(z)
nabla_w[-l] = np.dot(delta, activations[-l-1].T)
nabla_b[-l] = delta
return nabla_w, nabla_b
def sigmoid(z):
return 1.0/(1.0+np.exp(-z))
def sigmoid_prime(z):
return sigmoid(z)*(1-sigmoid(z))
梯度检查实现
数值梯度验证是调试的关键步骤:
def gradient_check(network, x, y, epsilon=1e-7):
# 计算反向传播梯度
nabla_w, nabla_b = network.backprop(x, y)
# 数值梯度计算
for layer in range(len(network.weights)):
for i in range(network.weights[layer].shape[0]):
for j in range(network.weights[layer].shape[1]):
# 正向扰动
network.weights[layer][i,j] += epsilon
loss_plus = np.sum((network.forward(x) - y)**2)
# 负向扰动
network.weights[layer][i,j] -= 2*epsilon
loss_minus = np.sum((network.forward(x) - y)**2)
# 恢复原值
network.weights[layer][i,j] += epsilon
# 数值梯度
numeric_grad = (loss_plus - loss_minus)/(2*epsilon)
# 比较差异
assert abs(nabla_w[layer][i,j] - numeric_grad) < 1e-5
常见问题深度解析
梯度消失 / 爆炸
当网络层数较深时,梯度可能呈现指数级衰减或增长。以 sigmoid 函数为例,其导数最大值为 0.25,经过 $n$ 层传播后梯度最多缩小 $0.25^n$。解决方案包括:
- 使用 ReLU 及其变体激活函数
- 采用残差连接结构
- 合理的权重初始化(如 Xavier、He 初始化)
学习率选择
学习率 $\eta$ 直接影响收敛速度:
- 固定学习率:通常取 $10^{-3}$ 到 $10^{-5}$
- 自适应方法:Adam、RMSprop 等
- 学习率衰减:余弦退火、阶梯式衰减
激活函数对比
| 函数类型 | 优点 | 缺点 |
|---|---|---|
| Sigmoid | 输出平滑 | 容易饱和导致梯度消失 |
| ReLU | 计算高效 | 可能出现神经元死亡 |
| LeakyReLU | 缓解死亡问题 | 需要调参 |
实践避坑指南
- 梯度检查必做 :在复杂网络实现中,先用小样本验证梯度计算正确性
- 监控激活值分布 :使用 TensorBoard 等工具观察各层输出范围
- 谨慎选择初始化 :深层网络避免全零初始化,推荐 He 初始化配合 ReLU
思考与延伸
如何将反向传播扩展到卷积神经网络?关键在于理解:
1. 卷积核的局部连接特性
2. 池化层的梯度传播方式
3. 参数共享机制下的梯度计算
期待读者在掌握全连接网络的基础上,进一步探索 CNN 的反向传播实现。
