Files
2026-05-21 15:08:03 +08:00

3.9 KiB
Raw Permalink Blame History

手写数字识别 - 纯NumPy MLP实现

项目简介

使用纯NumPy实现的两层全连接神经网络MLP在MNIST数据集上进行手写数字识别。

零深度学习框架依赖,只需 numpy

网络结构

输入层(784) → 隐藏层(128) + ReLU → 输出层(10) + Softmax
  • 输入: 28×28=784 像素值,归一化到 [0, 1]
  • 隐藏层: 128 神经元ReLU激活函数
  • 输出层: 10 神经元数字0-9Softmax输出概率

文件结构

digit_mlp_class/
├── main.py           # 主程序(训练/评估/对比实验)
├── model_numpy.py    # MLP模型纯NumPy实现
├── dataset.py        # MNIST数据集加载
├── config.py         # 超参数配置
├── data/             # MNIST数据文件
│   ├── train-images-idx3-ubyte.gz
│   ├── train-labels-idx1-ubyte.gz
│   ├── t10k-images-idx3-ubyte.gz
│   └── t10k-labels-idx1-ubyte.gz
└── README.md

依赖

numpy

使用方法

1. 下载MNIST数据集

如果 data/ 目录下没有数据文件,运行:

python dataset.py

或手动下载:

cd data/
curl -LO https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
curl -LO https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
curl -LO https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
curl -LO https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz

2. 训练模型

python main.py

3. 运行对比实验

python main.py --compare

代码设计

model_numpy.py - MLP模型

核心实现:

  • 前向传播: 矩阵乘法 + ReLU + Softmax
  • 反向传播: 手动梯度计算 + 梯度下降
  • 权重初始化: Xavier初始化适合ReLU
class MLP:
    def __init__(self, input_size=784, hidden_size=128, num_classes=10)
    def forward(self, X):      # 前向传播
    def backward(self, X, y):  # 反向传播
    def fit(self, X, y):       # 训练
    def predict(self, X):      # 预测

dataset.py - 数据加载

  • 自动检测 data/ 目录下的MNIST文件
  • 解析IDX格式MNIST标准格式
  • 归一化像素值到 [0, 1]
  • 支持One-Hot编码标签

main.py - 主程序

两种运行模式:

  1. 默认模式: 训练一个模型并评估
  2. 对比模式 (--compare): 对比不同超参数的效果

数学原理

前向传播

z1 = X @ W1 + b1          # 第一层线性变换
a1 = ReLU(z1)             # 第一层激活

z2 = a1 @ W2 + b2         # 第二层线性变换
probs = softmax(z2)       # 输出概率

反向传播

d_z2 = probs - y                          # 输出层梯度
d_W2 = a1.T @ d_z2                        # 第二层权重梯度
d_z1 = d_z2 @ W2.T * relu_derivative(z1)  # 隐藏层梯度
d_W1 = X.T @ d_z1                          # 第一层权重梯度

W1 -= lr * d_W1 / batch_size              # 梯度下降更新
W2 -= lr * d_W2 / batch_size

激活函数

ReLU:

ReLU(x) = max(0, x)
ReLU'(x) = 1 if x > 0 else 0

Softmax:

softmax(x_i) = exp(x_i) / sum(exp(x_j))

超参数

参数 默认值 说明
hidden_size 128 隐藏层神经元数量
learning_rate 0.1 学习率
epochs 50 训练轮数
batch_size 64 批大小
seed 42 随机种子

预期结果

  • 训练准确率: ~98%
  • 测试准确率: ~95-97%

训练时间: 约 5-10 分钟(取决于硬件)

扩展实验

  1. 改变隐藏层大小: 32 / 64 / 128 / 256
  2. 改变学习率: 0.01 / 0.1 / 0.5
  3. 添加Dropout: 防止过拟合
  4. 增加隐藏层数: 784 → 256 → 128 → 10

教学用途

本项目适合用于讲解:

  • 神经网络基本结构
  • 前向传播与反向传播原理
  • 梯度下降优化
  • NumPy矩阵操作
  • MNIST数据集处理