
文章目录01 本篇要解决什么问题02 背景MNIST很小但项目流程要完整03 实操从一批灰度图到十个类别分数1准备依赖与下载数据2固定输入约定3定义一个LeNet风格的小CNN4把公共训练流程接上5先做小规模运行再使用全量数据6结果要能看也要能追溯04 一个具体例子运行入口与单图预测05 常见问题与自测06 本篇回顾与下一步01 本篇要解决什么问题前面几篇已经讲过Tensor、训练循环和损失函数现在开始把这些知识放进一个真正的数据集。第一个项目选择手写数字识别输入一张数字图片输出09中的一个类别。这个任务容易观察也适合排查流程。我们关心的不只是训练时出现一个准确率还包括数据怎样划分、最优权重怎样选择以及保存的模型能否重新加载去预测一张图片。本篇关键词MNIST、CNN、分类流程、数据划分、单图预测。完成标志完成下载、训练、验证、测试和单张图片推理并保存可复查的结果。运行约定继续使用dl-course环境。将配套examples目录完整放在练习根目录所有命令从根目录执行PowerShell与Bash中的这些单行命令相同。项目代码私信获取02 背景MNIST很小但项目流程要完整MNIST包含60000张训练图和10000张测试图每张是28×28的灰度数字图。Torchvision提供数据加载与下载接口见MNIST官方接口。本篇从官方训练部分按类别固定抽取约10%作为验证集其余用于训练官方测试部分保留到模型选定以后。因此默认使用54000张训练图、6000张验证图、10000张测试图。数据部分用途可以用它决定什么训练集更新参数根据梯度学习特征验证集选最优轮次学习率、模型及训练设置测试集最终评价报告已确定方案的表现程序会保存实际索引随机种子默认42。缩小训练规模时也会记录实际使用数量避免把小样本验证结果误写成完整数据集成绩。03 实操从一批灰度图到十个类别分数1准备依赖与下载数据按第03篇安装相互匹配的PyTorch与Torchvision另外需要NumPy和Pillow。本批不需要额外安装OpenCV或数据集账号。python-m pip install numpy pillow python examples/download_data.py--dataset mnist--datadatadownload_data.py只准备数据不开始训练。网络不通时可以根据官方接口和文件校验要求手动准备缓存不要修改校验逻辑来绕过损坏文件。2固定输入约定Pillow灰度图经过ToTensor()后变成[1,28,28]浮点Tensor8位强度缩放到01再用均值0.5、标准差0.5映射到约−11。一个批次就是[N,1,28,28]。标签是[N]的long类型类别ID。输入与标签的形状不同因为每张图只对应一个正确数字而不是每个像素对应一个类别。3定义一个LeNet风格的小CNN下面的教学网络使用两组卷积、ReLU和池化再接分类头。它借鉴经典小型卷积分类思路具体通道和激活配置采用本专栏版本并非原始LeNet的逐层复现。fromtorchimportnnclassGrayCNN(nn.Module):def__init__(self,classes10):super().__init__()self.featuresnn.Sequential(nn.Conv2d(1,16,5,padding2),nn.ReLU(),nn.MaxPool2d(2),nn.Conv2d(16,32,5,padding2),nn.ReLU(),nn.MaxPool2d(2))self.headnn.Sequential(nn.Flatten(),nn.Linear(32*7*7,64),nn.ReLU(),nn.Linear(64,classes))defforward(self,x):returnself.head(self.features(x))尺寸变化为[N,1,28,28] → 卷积与池化 → [N,16,14,14] → 卷积与池化 → [N,32,7,7] → 展平与全连接 → [N,10]最后的10个数是logits直接交给交叉熵。训练前不额外Softmax需要概率时再转换。4把公共训练流程接上五篇共用project_common.py、project_data.py和project_models.py。它们分别处理训练评价、数据划分和模型定义。主入口与公共文件应保留在同一个examples目录。公共训练函数按批次清梯度、计算交叉熵、反向传播和更新每轮结束计算验证损失保存验证损失最小的权重。最后加载这个权重对测试集评价一次。统计损失按样本数量加权验证使用eval()与no_grad()。这延续了第09篇的规则并把分类指标、结果保存和权重恢复补齐。5先做小规模运行再使用全量数据python examples/train_mnist.py--epochs 1--limit-train1024--limit-val256--limit-test256这是检查流程的命令不是正式训练配置。三个--limit-*参数为0时使用相应完整划分默认值均为0。python examples/train_mnist.py--epochs 3--output artifacts/12_mnist_full默认CPU即可运行耗时取决于机器。已经有可用CUDA环境时可以加--device cuda模型与数据会一起移动。6结果要能看也要能追溯每次运行生成best.pt、history.json、metrics.json、split_manifest.json、predictions.csv、预测图和错分图。检查曲线与混淆矩阵时执行python examples/visualize_run.py--run artifacts/12_mnist混淆矩阵行是真实类别列是预测类别。对角线表示预测正确非对角线帮助定位容易混淆的数字。若某个很小的测试子集没有错分错分图会明确显示这一情况不伪造错误样本。图本篇原创项目流程图。验证用于选权重测试在选择完成后评价。04 一个具体例子运行入口与单图预测完整主入口为examples/train_mnist.py公共文件的完整代码随工程提供也收录在公共代码附录。Chapter 12: CNN digit classification.fromproject_commonimportparser_for,validate_args,seed_everything,run_experimentfromproject_dataimportload_data,preprocessingfromproject_modelsimportbuild_modeldefmain():parserparser_for(__doc__,artifacts/12_mnist,epochs3)argsparser.parse_args()validate_args(parser,args)seed_everything(args.seed,args.threads)data,classes,manifestload_data(mnist,args)spec{kind:gray_cnn,classes:10}modelbuild_model(spec)run_experiment(model,spec,data,classes,manifest,preprocessing(mnist),args)if__name____main__:main()训练后把准备好的数字图片保存为data/my_digit.png执行python examples/predict_image.py--checkpoint artifacts/12_mnist/best.pt--imagedata/my_digit.png推理脚本从权重中读取类别名称和预处理参数输出类别与各类分数。先拿与MNIST风格相近的居中、浅色数字深色背景图片检查普通手机照片存在光照、背景、位置和线宽差异单纯缩放到28×28并不会自动解决这些问题。数字类别名保留数据集提供的顺序不手写一份可能错位的映射。权重重建也不会再次下载其他模型参数。05 常见问题与自测问题1为什么不能直接拿官方测试集每轮选模型那会让测试集参与选择。本篇另划验证集测试留到轮次选择完成后使用。问题2网络最后为什么没有Softmax交叉熵使用logits。概率转换放在评价或推理阶段避免把概率再次当成原始分数。问题3MNIST效果不错自己写的数字却预测错了先检查前景极性、居中、留白和输入范围再分析数据分布差异。训练集表现不能保证手机照片也相同。问题4limit参数改变了什么它在固定划分内随机抽取子集方便检查流程不会把验证样本重新放回训练集。实际使用索引会被保存。问题5best.pt与最后一轮权重一定相同吗不一定。它对应验证损失最小的那一轮具体轮次记录在结果文件中。06 本篇回顾与下一步第一个项目建立了以后反复使用的基本流程固定数据划分明确输入输出按验证集选权重最后评价并保存预测结果。中文常见英文或写法手写数字识别Handwritten Digit Recognition灰度图Grayscale Image混淆矩阵Confusion Matrix单图推理Single-image Inference下一篇切换到服装图像让MLP与CNN在相同数据和训练预算下进行比较。参考与延伸阅读PyTorch数据集与DataLoader教程PyTorch保存和加载模型