共计 2167 个字符,预计需要花费 6 分钟才能阅读完成。
数学原理拆解
BP 神经网络的核心是链式求导。假设网络有 L 层,第 l 层的输出为:

$$
a^{(l)} = f(z^{(l)}), \quad z^{(l)} = W^{(l)}a^{(l-1)} + b^{(l)}
$$
对于平方误差损失函数 $E = \frac{1}{2}|y – a^{(L)}|^2$,输出层的误差项为:
$$
\delta^{(L)} = (a^{(L)} – y) \odot f'(z^{(L)})
$$
反向传播时,第 l 层的误差项计算如下($\odot$ 表示逐元素乘):
$$
\delta^{(l)} = (W^{(l+1)T}\delta^{(l+1)}) \odot f'(z^{(l)})
$$
激活函数对比
-
Sigmoid:
$$
\sigma(z) = \frac{1}{1+e^{-z}} \quad \sigma'(z) = \sigma(z)(1-\sigma(z))
$$
易导致梯度消失(导数最大仅 0.25) -
Tanh:
$$
\tanh(z) = \frac{e^z – e^{-z}}{e^z + e^{-z}} \quad \tanh'(z) = 1 – \tanh^2(z)
$$
梯度消失问题稍缓和(导数最大为 1) -
ReLU:
$$
ReLU(z) = max(0,z) \quad ReLU'(z) = \begin{cases}
1 & z>0 \
0 & z\leq0
\end{cases}
$$
缓解梯度消失但可能导致神经元死亡
Python 实现核心代码
import numpy as np
class NeuralNetwork:
def __init__(self, layer_dims, activation='relu'):
self.params = {}
# He 初始化(ReLU 适用)for l in range(1, len(layer_dims)):
self.params['W'+str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1]) * np.sqrt(2/layer_dims[l-1])
self.params['b'+str(l)] = np.zeros((layer_dims[l], 1))
def forward(self, X):
cache = {'A0': X}
for l in range(1, len(self.layer_dims)):
Z = np.dot(self.params['W'+str(l)], cache['A'+str(l-1)]) + self.params['b'+str(l)]
cache['Z'+str(l)] = Z
cache['A'+str(l)] = self._activate(Z)
return cache
def backward(self, cache, y):
grads = {}
m = y.shape[1]
# 输出层误差
dZ = cache['A'+str(L)] - y # 假设使用线性输出层
grads['W'+str(L)] = np.dot(dZ, cache['A'+str(L-1)].T) / m
grads['b'+str(L)] = np.sum(dZ, axis=1, keepdims=True) / m
# 反向传播
for l in reversed(range(1, L)):
dA = np.dot(self.params['W'+str(l+1)].T, dZ)
dZ = dA * self._activate_deriv(cache['Z'+str(l)])
grads['W'+str(l)] = np.dot(dZ, cache['A'+str(l-1)].T) / m
grads['b'+str(l)] = np.sum(dZ, axis=1, keepdims=True) / m
return grads
关键参数设置
- 学习率选择 :
- 初始建议 0.001-0.1
-
观察损失曲线:持续震荡需调小,下降过慢可适当增大
-
批量大小 :
- 小批量(32-256)通常优于全批量
-
显存受限时可尝试梯度累计
-
权重初始化 :
- Xavier 初始化(tanh):$Var(W) = \frac{2}{n_{in} + n_{out}}$
- He 初始化(ReLU):$Var(W) = \frac{2}{n_{in}}$
典型问题解决方案
-
梯度爆炸 :
添加梯度裁剪max_norm = 5 for grad in grads.values(): norm = np.linalg.norm(grad) if norm > max_norm: grad *= max_norm / norm -
神经元死亡 (ReLU):
使用 LeakyReLU:$\alpha=0.01$ -
过拟合 :
- 添加 L2 正则化:
loss += 0.5 * lambda * np.sum(W**2) - 早停法(early stopping)
效果可视化
plt.plot(loss_history)
plt.xlabel('Iterations')
plt.ylabel('Loss')
plt.title(f'Final Accuracy: {accuracy:.2%}')
plt.grid()
进阶技巧
-
学习率衰减 :
lr = initial_lr * (1. / (1. + decay_rate * epoch)) -
批归一化 :
- 在激活函数前插入 BN 层
-
需调整反向传播公式
-
模型保存 :
np.savez('model.npz', **self.params)
通过这个实现,你应该能完整理解 BP 算法的运作机制。建议尝试用不同激活函数组合,观察训练过程的差异。
