共计 2113 个字符,预计需要花费 6 分钟才能阅读完成。
BP 神经网络实现手写数字识别:从数学原理到 Python 实战
背景介绍
手写数字识别是计算机视觉领域的经典问题,广泛应用于邮政编码识别、银行支票处理等场景。BP 神经网络因其强大的非线性拟合能力,成为解决这类问题的有效工具。相比传统算法,BP 神经网络能够自动学习特征,无需手动设计复杂的特征提取规则。

数学原理
前向传播
前向传播是神经网络预测的核心过程。对于一个三层网络(输入层、隐藏层、输出层),其计算过程如下:
$$
\begin{aligned}
z^{(2)} &= W^{(1)}x + b^{(1)} \
a^{(2)} &= \sigma(z^{(2)}) \
z^{(3)} &= W^{(2)}a^{(2)} + b^{(2)} \
a^{(3)} &= \text{softmax}(z^{(3)})
\end{aligned}
$$
其中,$\sigma$ 表示激活函数,常用 Sigmoid 或 ReLU。
反向传播
反向传播通过链式法则计算梯度:
$$
\begin{aligned}
\delta^{(3)} &= a^{(3)} – y \
\delta^{(2)} &= (W^{(2)})^T\delta^{(3)} \odot \sigma'(z^{(2)}) \
\frac{\partial J}{\partial W^{(2)}}} &= \delta^{(3)}(a^{(2)})^T \
\frac{\partial J}{\partial b^{(2)}}} &= \delta^{(3)} \
\frac{\partial J}{\partial W^{(1)}}} &= \delta^{(2)}x^T \
\frac{\partial J}{\partial b^{(1)}}} &= \delta^{(2)}
\end{aligned}
$$
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) * 0.01
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * 0.01
self.b2 = np.zeros((1, output_size))
def sigmoid(self, z):
return 1 / (1 + np.exp(-z))
def softmax(self, z):
exp_z = np.exp(z - np.max(z, axis=1, keepdims=True))
return exp_z / np.sum(exp_z, axis=1, keepdims=True)
def forward(self, X):
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.sigmoid(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
self.a2 = self.softmax(self.z2)
return self.a2
def backward(self, X, y, learning_rate):
m = X.shape[0]
delta3 = self.a2 - y
dW2 = np.dot(self.a1.T, delta3) / m
db2 = np.sum(delta3, axis=0, keepdims=True) / m
delta2 = np.dot(delta3, self.W2.T) * self.a1 * (1 - self.a1)
dW1 = np.dot(X.T, delta2) / m
db1 = np.sum(delta2, axis=0, keepdims=True) / m
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
模型训练
- 加载 MNIST 数据集并预处理
- 初始化网络参数
- 设置超参数:学习率 =0.1,batch_size=100,epochs=20
- 迭代训练并计算准确率
关键观察:
– 学习率过大导致震荡,过小收敛缓慢
– batch_size 影响梯度估计的准确性
– 适当增加 epochs 可提高准确率,但要注意过拟合
性能优化
- 隐藏层节点数实验:
- 32 节点:准确率~85%
- 64 节点:准确率~90%
- 128 节点:准确率~92%
- 增加隐藏层数量:
- 2 层 64 节点:准确率提升至~94%
避坑指南
- 梯度消失问题:使用 ReLU 激活函数替代 Sigmoid
- 过拟合:添加 L2 正则化或 Dropout
- 参数初始化:使用 Xavier 或 He 初始化
- 学习率衰减:随着训练逐步降低学习率
延伸思考
- 引入卷积层处理空间信息
- 使用 Adam 等自适应优化器
- 尝试 Batch Normalization 加速训练
- 探索更深的网络结构
通过这个项目,我深刻理解了 BP 神经网络的工作原理。虽然实现比调用框架复杂,但对理解深度学习底层机制很有帮助。建议读者尝试调整网络结构和参数,观察性能变化,这是掌握神经网络的最佳方式。
