共计 1747 个字符,预计需要花费 5 分钟才能阅读完成。
为什么需要从零实现 BP 神经网络?
作为机器学习初学者,直接调用框架虽然方便,但容易成为调包侠。我在第一次实现 BP 网络时遇到过:

- 梯度消失导致深层权重无法更新(输出层误差传不到第一层)
- 学习率设大了震荡,设小了收敛慢
- 全零初始化导致神经元对称性问题
通过手写实现,才能真正理解:
- 反向传播如何通过链式法则逐层传递误差
- 激活函数导数对梯度的影响
- 参数初始化与学习率的关系
核心实现:三层网络架构
网络结构定义
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
# Xavier 初始化(后续会详细解释)self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(1/input_size)
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * np.sqrt(1/hidden_size)
self.b2 = np.zeros((1, output_size))
正向传播实现
关键点在于矩阵运算的维度匹配:
def forward(self, X):
self.z1 = np.dot(X, self.W1) + self.b1 # (batch, hidden)
self.a1 = self.sigmoid(self.z1) # 激活层
self.z2 = np.dot(self.a1, self.W2) + self.b2
return self.softmax(self.z2) # 多分类用 softmax
反向传播四步曲
- 计算输出层误差:
output_error = y_pred - y_true # (batch, output) - 隐藏层误差(链式法则核心):
hidden_error = np.dot(output_error, self.W2.T) * self.sigmoid_derivative(self.a1) - 梯度计算(注意 batch 求平均):
dW2 = np.dot(self.a1.T, output_error) / batch_size db2 = np.sum(output_error, axis=0) / batch_size - 参数更新(带学习率):
self.W2 -= learning_rate * dW2
那些年我踩过的坑
梯度消失实战案例
当使用 sigmoid 激活且网络较深时,曾出现前几层的梯度值接近于 0。解决方法:
- 改用 ReLU 激活函数
- 批规范化(BatchNorm)
- 残差连接(ResNet 思路)
学习率调优实验
在 MNIST 数据集上的对比结果:
| 策略 | 最终准确率 | 收敛速度 |
|---|---|---|
| 固定 0.1 | 87.2% | 快但震荡 |
| 固定 0.01 | 92.1% | 慢但稳定 |
| AdaGrad | 94.3% | 先快后慢 |
生产级优化技巧
Xavier 初始化的数学原理
权重初始化标准差应满足:
\sigma = \sqrt{\frac{2}{n_{in} + n_{out}}}
对应代码实现:
# 输入层到隐藏层
std = np.sqrt(2 / (input_size + hidden_size))
self.W1 = np.random.randn(input_size, hidden_size) * std
Early Stopping 实现
best_val_acc = 0
for epoch in range(epochs):
# ... 训练代码...
val_acc = evaluate(val_data)
if val_acc > best_val_acc:
best_weights = self.get_weights() # 保存当前最佳
patience = 3 # 重置耐心值
else:
patience -= 1
if patience == 0: break
self.set_weights(best_weights) # 恢复最佳
留给读者的思考题
- 当前实现是全批量梯度下降,如何改造为 Mini-batch 训练?
- 如果想增加 Dropout 层防止过拟合,应该在正向 / 反向传播中如何处理?
- 为什么说 ReLU 比 Sigmoid 更适合深层网络?
建议尝试用不同激活函数和优化器组合,观察训练曲线的差异。完整代码已上传 GitHub(见文末链接)。在实际项目中,当准确率遇到瓶颈时,不妨回头看看这些基础实现,往往会有新的启发。
正文完
