共计 2114 个字符,预计需要花费 6 分钟才能阅读完成。
计算图视角下的反向传播原理
BP 神经网络的核心在于通过误差反向传播调整权重。我们先从计算图角度理解这个过程:

- 前向传播计算
- 输入层到隐藏层:$h = \sigma(W_1 x + b_1)$
- 隐藏层到输出层:$\hat{y} = \sigma(W_2 h + b_2)$
-
其中 $\sigma$ 是 sigmoid 激活函数
-
损失函数计算
-
采用均方误差:$L = \frac{1}{2}(y – \hat{y})^2$
-
反向传播关键步骤
- 输出层梯度:$\frac{\partial L}{\partial W_2} = (\hat{y} – y) \cdot \sigma'(z_2) \cdot h^T$
- 隐藏层梯度:$\frac{\partial L}{\partial W_1} = \delta_{hidden} \cdot x^T$
- 其中 $\delta_{hidden} = (W_2^T \delta_{output}) \odot \sigma'(z_1)$
Python 实现核心代码
import numpy as np
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
# 初始化权重
self.W1 = np.random.randn(input_size, hidden_size)
self.b1 = np.zeros(hidden_size)
self.W2 = np.random.randn(hidden_size, output_size)
self.b2 = np.zeros(output_size)
def sigmoid(self, x):
return 1 / (1 + np.exp(-x))
def forward(self, x):
self.z1 = np.dot(x, self.W1) + self.b1
self.h = self.sigmoid(self.z1)
self.z2 = np.dot(self.h, self.W2) + self.b2
return self.sigmoid(self.z2)
def backward(self, x, y, lr=0.1):
# 前向传播
y_pred = self.forward(x)
# 计算输出层梯度
dL_dz2 = (y_pred - y) * y_pred * (1 - y_pred)
dW2 = np.dot(self.h.T, dL_dz2)
db2 = np.sum(dL_dz2, axis=0)
# 计算隐藏层梯度
dL_dh = np.dot(dL_dz2, self.W2.T)
dL_dz1 = dL_dh * self.h * (1 - self.h)
dW1 = np.dot(x.T, dL_dz1)
db1 = np.sum(dL_dz1, axis=0)
# 更新权重
self.W2 -= lr * dW2
self.b2 -= lr * db2
self.W1 -= lr * dW1
self.b1 -= lr * db1
可视化梯度流动
使用 matplotlib 可以绘制梯度流动示意图:
- 正向传播时数据从左向右流动
- 反向传播时红色箭头表示梯度传播方向
- 线宽代表梯度大小,越宽表示该连接权重更新幅度越大
常见问题与解决方案
- 梯度消失问题
- 现象:深层网络训练时梯度逐渐变小
-
解决方案:
- 使用 ReLU 等非饱和激活函数
- 采用 Batch Normalization
- 残差连接结构
-
学习率设置
- 太大:可能导致震荡无法收敛
- 太小:训练速度过慢
-
自适应方法:Adam、RMSprop 等优化器
-
过拟合处理
- Dropout 随机失活
- L2 正则化
- 早停法 (Early Stopping)
MNIST 实战案例
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
# 加载数据
mnist = fetch_openml('mnist_784')
X = mnist.data / 255.0
y = np.eye(10)[mnist.target.astype(int)]
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 训练网络
nn = NeuralNetwork(784, 128, 10)
for epoch in range(10):
for i in range(0, len(X_train), 32):
batch_x = X_train[i:i+32]
batch_y = y_train[i:i+32]
nn.backward(batch_x, batch_y)
# 评估
test_pred = nn.forward(X_test)
accuracy = np.mean(np.argmax(test_pred, axis=1) == np.argmax(y_test, axis=1))
print(f"Epoch {epoch}, Accuracy: {accuracy:.4f}")
调参建议
- 尝试不同隐藏层大小(如 64、256)
- 调整学习率(0.01-0.5 范围)
- 增加隐藏层数量(需注意梯度消失)
- 尝试不同的激活函数
通过本文的数学推导和代码实现,相信你对 BP 神经网络的反向传播机制有了清晰理解。建议动手调整网络参数,观察对模型性能的影响,这是掌握神经网络的最佳方式。
正文完
