从零实现bp神经网络手写数字识别:原理详解与Python实战

1次阅读
没有评论

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

image.webp

背景介绍

手写数字识别是计算机视觉领域的基础任务,在银行支票识别、快递单号录入等场景有广泛应用。传统方法依赖特征工程,而神经网络能自动学习特征。MNIST 数据集包含 6 万张 28×28 灰度手写数字图,是理想的入门数据集。

从零实现 bp 神经网络手写数字识别:原理详解与 Python 实战

原理详解

前向传播

神经网络通过加权求和与激活函数逐层计算输出:

$$ z^{l} = w^{l}a^{l-1} + b^{l} $$
$$ a^{l} = \sigma(z^{l}) $$

其中 $\sigma$ 常用 ReLU 或 Sigmoid 函数。输出层用 Softmax 得到概率分布。

反向传播

通过链式法则计算梯度:

  1. 输出层误差:
    $$ \delta^{L} = \nabla_a C \odot \sigma'(z^{L}) $$

  2. 隐藏层误差:
    $$ \delta^{l} = ((w^{l+1})^T \delta^{l+1}) \odot \sigma'(z^{l}) $$

  3. 参数梯度:
    $$ \nabla_w C = \delta^{l}(a^{l-1})^T $$
    $$ \nabla_b C = \delta^{l} $$

代码实现

import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.preprocessing import OneHotEncoder

class NeuralNetwork:
    def __init__(self, layer_sizes):
        self.weights = [np.random.randn(y, x)/np.sqrt(x) 
                        for x, y in zip(layer_sizes[:-1], layer_sizes[1:])]
        self.biases = [np.random.randn(y, 1) for y in layer_sizes[1:]]

    def forward(self, x):
        for w, b in zip(self.weights, self.biases):
            x = sigmoid(np.dot(w, x) + b)
        return x

    def train(self, X, y, epochs=10, lr=0.1, batch_size=32):
        for _ in range(epochs):
            for i in range(0, len(X), batch_size):
                X_batch, y_batch = X[i:i+batch_size], y[i:i+batch_size]
                self.update_batch(X_batch, y_batch, lr)

    def update_batch(self, X_batch, y_batch, lr):
        # 反向传播实现代码...
        pass

def sigmoid(x):
    return 1/(1 + np.exp(-x))

模型训练

  1. 数据预处理
  2. 像素值归一化到[0,1]
  3. 标签进行 one-hot 编码

  4. 超参数设置

  5. 网络结构:784(input)-128(hidden)-10(output)
  6. 学习率:初始 0.1,每 5 轮衰减 10%
  7. Batch 大小:64

  8. 训练过程

    mnist = fetch_openml('mnist_784')
    X, y = mnist.data / 255.0, mnist.target
    encoder = OneHotEncoder()
    y_onehot = encoder.fit_transform(y.reshape(-1, 1)).toarray()
    
    nn = NeuralNetwork([784, 128, 10])
    nn.train(X, y_onehot, epochs=20, lr=0.1)

性能优化

  • 学习率:过大导致震荡,过小收敛慢。建议使用学习率衰减
  • Batch 大小:较小 batch 带来更多噪声,可能帮助跳出局部最优
  • 激活函数:隐藏层用 ReLU 比 Sigmoid 训练更快
  • 权重初始化:Xavier 初始化避免梯度消失

避坑指南

  1. 梯度爆炸
  2. 现象:loss 突然变成 NaN
  3. 解决:梯度裁剪 / 权重正则化

  4. 模型不收敛

  5. 检查数据预处理是否正确
  6. 确认反向传播梯度计算无误

  7. 过拟合

  8. 添加 Dropout 层
  9. 使用 L2 正则化

扩展思考

  1. 尝试增加隐藏层数量观察性能变化
  2. 用交叉验证选择最优超参数
  3. 实现卷积神经网络 (CNN) 对比效果
  4. 在自定义手写数字数据集上测试

通过这个项目,我们不仅理解了 BP 神经网络的数学原理,还掌握了从数据预处理到模型调优的完整流程。建议读者尝试修改网络结构,观察不同激活函数的效果,这是深入理解神经网络行为的最佳方式。

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