共计 2224 个字符,预计需要花费 6 分钟才能阅读完成。
为什么需要 BP 神经网络?
以经典的 MNIST 手写数字分类为例,当输入是一张 28×28 像素的图像时,我们需要处理 784 维的输入特征。传统线性模型无法捕捉像素间的复杂非线性关系,而 BP 神经网络通过隐藏层和激活函数,能够自动学习特征组合。比如识别数字 ”8″ 时,它可能先检测局部弧线,再组合成闭合环状结构。

数学推导:反向传播的本质
计算图视角下的链式法则
假设网络有三层结构:输入层 $z^{(1)}=x$,隐藏层 $a^{(2)}=f(W^{(1)}z^{(1)}+b^{(1)})$,输出层 $y=f(W^{(2)}z^{(2)}+b^{(2)})$。定义损失函数为均方误差 $E=\frac{1}{2}|y-t|^2$,则权重更新需要计算:
$$
\frac{\partial E}{\partial W^{(2)}} = \underbrace{(y-t)}_{\delta^{(3)}} \circ f'(z^{(3)}) \cdot a^{(2)T}
$$
$$
\frac{\partial E}{\partial W^{(1)}} = \underbrace{(W^{(2)T}\delta^{(3)})}_{\delta^{(2)}} \circ f'(z^{(2)}) \cdot x^T
$$
其中 $\circ$ 表示 Hadamard 积,关键点在于误差信号 $\delta$ 从输出层向输入层的逐层反向传播。
Python 实现核心代码
import numpy as np
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
# 初始化权重,使用 He 初始化策略
self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(2./input_size)
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * np.sqrt(2./hidden_size)
self.b2 = np.zeros((1, output_size))
def forward(self, X):
# 前向传播维度变化示例:[batch,784] -> [batch,256] -> [batch,10]
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = relu(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
return softmax(self.z2)
def backward(self, X, y, lr=0.01):
# 反向传播梯度缓存
m = X.shape[0]
delta2 = (self.probs - y) / m # 交叉熵损失的特殊性质
dW2 = np.dot(self.a1.T, delta2)
db2 = np.sum(delta2, axis=0, keepdims=True)
delta1 = np.dot(delta2, self.W2.T) * relu_derivative(self.z1)
dW1 = np.dot(X.T, delta1)
db1 = np.sum(delta1, axis=0)
# 学习率衰减实现
lr *= 0.95 ** (self.epoch // 10)
self.W2 -= lr * dW2
self.b2 -= lr * db2
self.W1 -= lr * dW1
self.b1 -= lr * db1
优化技巧与避坑指南
激活函数选择
- Sigmoid 问题 :当输入绝对值较大时梯度接近 0,导致梯度消失。例如 $\sigma(5)\approx0.993$,此时导数 $\sigma'(5)\approx0.006$
- ReLU 优势 :正向区间梯度恒为 1,避免梯度消失。但要注意 Dead ReLU 问题,可用 LeakyReLU 缓解
批量归一化实现
def batch_norm(x, gamma, beta, eps=1e-5):
mu = np.mean(x, axis=0)
var = np.var(x, axis=0)
x_hat = (x - mu) / np.sqrt(var + eps)
return gamma * x_hat + beta
超参数设置经验
- 隐藏层神经元数量 :介于输入层和输出层大小之间,常用公式 $\sqrt{n_{input} \cdot n_{output}}$
- 梯度检查 :用数值梯度验证解析梯度,误差应小于 1e-7
numeric_grad = (loss(W+eps) - loss(W-eps)) / (2*eps) - 学习率与 batch size:大 batch 需增大学习率,但非线性关系。建议 batch size 在 32-256 之间
思考题解析
当使用交叉熵损失 +Softmax 时,输出层梯度简化为 $\hat{y}-y$,这是因为:
$$
\frac{\partial L}{\partial z_i} = \sum_j \frac{\partial L}{\partial \hat{y}_j} \frac{\partial \hat{y}_j}{\partial z_i} = \hat{y}_i – y_i
$$
其中利用了 Softmax 的求导特性 $\frac{\partial \hat{y}_j}{\partial z_i} = \hat{y}_i(1-\hat{y}_i)$ 当 $i=j$,否则 $-\hat{y}_i\hat{y}_j$。这个数学巧合使得实现时无需显式计算 Softmax 导数。
