BP神经网络实现手写数字识别:从数学原理到Python实战

1次阅读
没有评论

共计 2113 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

BP 神经网络实现手写数字识别:从数学原理到 Python 实战

背景介绍

手写数字识别是计算机视觉领域的经典问题,广泛应用于邮政编码识别、银行支票处理等场景。BP 神经网络因其强大的非线性拟合能力,成为解决这类问题的有效工具。相比传统算法,BP 神经网络能够自动学习特征,无需手动设计复杂的特征提取规则。

BP 神经网络实现手写数字识别:从数学原理到 Python 实战

数学原理

前向传播

前向传播是神经网络预测的核心过程。对于一个三层网络(输入层、隐藏层、输出层),其计算过程如下:

$$
\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

模型训练

  1. 加载 MNIST 数据集并预处理
  2. 初始化网络参数
  3. 设置超参数:学习率 =0.1,batch_size=100,epochs=20
  4. 迭代训练并计算准确率

关键观察:
– 学习率过大导致震荡,过小收敛缓慢
– batch_size 影响梯度估计的准确性
– 适当增加 epochs 可提高准确率,但要注意过拟合

性能优化

  • 隐藏层节点数实验:
  • 32 节点:准确率~85%
  • 64 节点:准确率~90%
  • 128 节点:准确率~92%
  • 增加隐藏层数量:
  • 2 层 64 节点:准确率提升至~94%

避坑指南

  1. 梯度消失问题:使用 ReLU 激活函数替代 Sigmoid
  2. 过拟合:添加 L2 正则化或 Dropout
  3. 参数初始化:使用 Xavier 或 He 初始化
  4. 学习率衰减:随着训练逐步降低学习率

延伸思考

  1. 引入卷积层处理空间信息
  2. 使用 Adam 等自适应优化器
  3. 尝试 Batch Normalization 加速训练
  4. 探索更深的网络结构

通过这个项目,我深刻理解了 BP 神经网络的工作原理。虽然实现比调用框架复杂,但对理解深度学习底层机制很有帮助。建议读者尝试调整网络结构和参数,观察性能变化,这是掌握神经网络的最佳方式。

正文完
 0
评论(没有评论)