共计 2191 个字符,预计需要花费 6 分钟才能阅读完成。
一、从单层感知机到多层网络
反向传播 (Backpropagation, BP) 算法是神经网络训练的核心。我们先从单层感知机的梯度计算开始理解基本原理。

对于单个神经元,其输出可以表示为:
$$a = \sigma(w^Tx + b)$$
其中 $\sigma$ 是激活函数,$w$ 是权重(weights),$b$ 是偏置(bias)。
-
单层网络的损失函数对权重的梯度为:
$$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w}$$
这就是链式法则 (Chain Rule) 的体现 -
扩展到多层网络时,误差需要从输出层逐层反向传播:
$$\delta^{(l)} = ((w^{(l+1)})^T \delta^{(l+1)}) \odot \sigma'(z^{(l)})$$
其中 $\odot$ 表示逐元素相乘,$\delta$ 是误差项
二、关键数学推导
以三层网络为例,完整的反向传播过程:
-
输出层误差:
$$\delta^{(3)} = \nabla_a L \odot \sigma'(z^{(3)})$$ -
隐藏层误差:
$$\delta^{(2)} = (w^{(3)})^T \delta^{(3)} \odot \sigma'(z^{(2)})$$ -
参数更新:
$$\Delta w^{(l)} = -\eta \delta^{(l)} (a^{(l-1)})^T$$
$$\Delta b^{(l)} = -\eta \delta^{(l)}$$
三、Python 实现
import numpy as np
# 激活函数实现
def sigmoid(x):
return 1/(1+np.exp(-x))
def sigmoid_derivative(x):
s = sigmoid(x)
return s*(1-s)
# 前向传播
def forward_pass(x, weights, biases):
layer_outputs = []
a = x
for w, b in zip(weights, biases):
z = np.dot(w, a) + b
a = sigmoid(z)
layer_outputs.append((z, a))
return layer_outputs
# 反向传播
def backward_pass(x, y, layer_outputs, weights):
gradients_w = []
gradients_b = []
delta = None
# 从输出层开始反向计算
for l in range(len(weights)-1, -1, -1):
z, a = layer_outputs[l]
if l == len(weights)-1: # 输出层
delta = (a - y) * sigmoid_derivative(z)
else: # 隐藏层
delta = np.dot(weights[l+1].T, delta) * sigmoid_derivative(z)
# 计算梯度
a_prev = x if l == 0 else layer_outputs[l-1][1]
grad_w = np.dot(delta, a_prev.T)
grad_b = delta
gradients_w.insert(0, grad_w)
gradients_b.insert(0, grad_b)
return gradients_w, gradients_b
四、数值稳定性处理
- 梯度消失问题:使用 ReLU 等激活函数替代 Sigmoid
- 梯度爆炸问题:梯度裁剪(Gradient Clipping)
- 权重初始化:Xavier 初始化或 He 初始化
五、避坑指南
- 学习率设置:
- 常见初始值 0.01
-
可以使用学习率衰减策略
-
参数初始化方法对比:
- 全零初始化:导致对称性问题
- 随机初始化:需要合理设置方差
- Xavier 初始化:适合 Sigmoid/tanh
-
He 初始化:适合 ReLU
-
梯度检查实现:
def gradient_check(x, y, weights, biases, epsilon=1e-7): # 计算数值梯度 numerical_grads = [] for w in weights: grad = np.zeros_like(w) it = np.nditer(w, flags=['multi_index']) while not it.finished: idx = it.multi_index original = w[idx] w[idx] = original + epsilon cost_plus = compute_cost(x, y) w[idx] = original - epsilon cost_minus = compute_cost(x, y) grad[idx] = (cost_plus - cost_minus)/(2*epsilon) w[idx] = original it.iternext() numerical_grads.append(grad) return numerical_grads
六、思考与扩展
- 如何扩展为 Mini-batch 训练?
- 计算 batch 内样本梯度的平均值
-
使用矩阵运算优化批量处理
-
自动微分 vs 手动实现:
- PyTorch/TensorFlow 的 autograd
- 手动实现有助于深入理解原理
希望这篇从理论到实践的完整介绍,能帮助你真正理解 BP 算法的精髓。建议尝试修改代码中的网络结构,观察不同激活函数的表现,这是掌握神经网络训练的最佳方式。
