这个编号写的是 3 还是 8
坤坤正在把实验记录里的样品编号录进电脑。有个数字上半圈挤在一起,看着像 3,又像 8。他放大照片看了半天,顺手记下一个小想法:让程序认数字试试。于是找来现成的手写数字图片,训练一个小网络。跑完先看它认错了什么,尤其是那些自己也容易看花眼的数字。

图像分类为每张图片预测一个类别。前馈神经网络让输入依次经过若干层计算;本项目将 64 个像素值映射为 10 个类别得分,选择得分最高的数字。
scikit-learn 自带 1797 张图像,不需要额外下载数据。每个像素的取值为 0 到 16;展开后得到 64 维输入。
相关知识:数组形状、前馈网络、反向传播、框架接口、模型选择。
下载与运行
下载 食品实验室完整项目包,按运行环境安装并启动。在左侧选择“数字识别”。
本实验的练习文件:projects/digits/exercises.py。先运行演示,再填写函数;保存后点击“检查练习”,通过后切到练习模式运行。

看图与实验
先运行 scikit-learn 基线。安装 PyTorch 后填写网络和训练步骤,比较训练曲线。错题墙标题“3 → 8”表示真实数字3被预测成8。点选编号,观察概率,再尝试噪声和平移。
数据集含1797张8×8图像,每张展开为64个输入,像素从0—16缩放到0—1。训练、验证、测试按固定划分分别占约60%、20%、20%。
PyTorch 主线网络为64个输入、一个带整流线性单元(Rectified Linear Unit,ReLU)的隐藏层、10个类别得分。训练时使用交叉熵;评价时转换为各类概率。
错题墙标题“3 → 8”表示真实数字3被预测成8。选择样本后可增加噪声或水平平移;图像使用相同灰度范围,概率图固定在0—1,方便比较变化。平移空出的位置补零,不把右侧像素绕回左侧。
填写函数
normalize
像素的已知范围为0到16,转换为0到1并展平。
接口提示
reshape(-1,64).astype("float32") / 16;先检查形状、范围与有限性。
build_network
隐藏层用 ReLU;最后输出10个类别得分。
接口提示
nn.Sequential(nn.Linear(64,hidden), nn.ReLU(), nn.Linear(hidden,10))。
train_step
每个批次先清除旧梯度,再计算损失、反向传播并更新参数。
接口提示
optimizer.zero_grad() → model(x) → cross_entropy → backward() → optimizer.step()。
evaluate
预测时关闭梯度计算,返回概率和概率最大的类别。
接口提示
model.eval() 与 torch.no_grad();softmax(dim=1),再转 numpy。
训练框架
scikit-learn 是可直接运行的基线。PyTorch 用来填写网络与训练步骤,额外安装:
python -m pip install -r requirements-torch.txt
TensorFlow 保留完整对照示例,按需安装 requirements-tensorflow.txt。三个框架共享数据划分;初始化和优化实现不同,分数差异需要结合设置分析。
进阶与记录
固定数据划分和种子,分别改变隐藏层宽度、学习率、训练轮数。根据验证曲线选配置,再打开测试集。TensorFlow 为完整接口对照,不要求三套框架都安装。
“实验记录”保存当前会话中的参数、种子、指标和结果表,可下载 CSV 或 JSON。修改代码或训练参数后,旧结果会提示待更新;查看图中样本不会重新训练。完整参考实现放在 solutions/digits.py。
