ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

卷积神经网络实现手写数字识别:PyTorch实战与实验报告指南

卷积神经网络实现手写数字识别:PyTorch实战与实验报告指南 简介面向机器学习初学者和深度学习课程学生的一份完整实验资源围绕手写数字识别这一经典分类任务采用PyTorch框架搭建卷积神经网络CNN并在测试集上达到99%的准确率能够帮助使用者理解从数据加载到模型评估的完整流程。资源包共17个文件压缩后大小35.15MB主要包含4个Python源文件模型结构、训练与预测脚本、2个Word实验文档实验报告与指导书、预训练模型文件pt、MNIST原始数据集idx格式以及用于展示的PPT等类型覆盖代码、文档、模型与数据便于按需取用。该资源目前已有12134人学习下载广受认可。除了可直接运行的代码和训练好的模型还配有详细实验报告与指导书可辅助理解CNN设计思路、数据预处理细节及调参过程同时附带的数据集文件支持离线复现实验适合课程作业参考、论文复现或个人入门实践是系统掌握手写数字识别任务的有力资料。1. 为什么我推荐用CNN做手写数字识别而不是全连接网络我想先从一个多数人都经历过的挫折说起。刚开始接触深度学习课程时我照着经典的案例用全连接网络去跑手写数字识别MNIST折腾了半天训练集准确率卡在90%上下就再也上不去了。后来我把全连接层换成了卷积神经网络CNN同样只是跑十几个epoch准确率直接冲到了99%以上。这个反差让我意识到选对模型结构比调参重要得多。手写数字识别是深度学习入门阶段最有代表性的图像分类任务几乎每一门深度学习课程都会拿它当第一个像样的实验。它对应的数据集就是MNIST包含6万张训练图片和1万张测试图片每张是28x28的灰度图内容是0到9的十种数字。对课程实验来说这个任务难度适中训练速度快单卡CPU也能跑得动但又能把卷积、池化、全连接、损失函数、优化器这些核心概念全部串起来。这篇文章就是围绕这个实验的完整复盘从模型选型思路、数据准备、PyTorch代码实现到实验报告怎么写、常见坑怎么避我会把我在国科大这门课上的实操经验全部写出来。适合正在选修深度学习课程、需要完成手写数字识别作业的同学参考也适合刚入门深度学习、想做第一个完整图像分类项目的自学者。需要说明的是我这里用的框架是PyTorch这是当前学术和教学场景下最主流的深度学习库。如果你们的课程指定用TensorFlow思路完全一致只是API写法不同。原始实验报告里的核心图表包括损失曲线、准确率曲线、混淆矩阵、部分预测样本可视化这些在PyTorch里都有对应的实现方案我会逐一讲到。2. 整体方案设计从任务分析到模型选型的完整链路2.1 为什么图像分类不用全连接网络而是用卷积神经网络在动手写代码之前要先想清楚一个核心问题为什么图像分类任务要用CNN一张28x28的灰度图片如果把它拉平成一个一维向量长度是784。如果构建一个两层的全连接网络第一层784个输入神经元、第二层256个神经元那么这一层的参数数量就是784×256再加上偏置项大约是20万个参数。这还只是一个全连接层整个网络的参数量会非常可观。更关键的是全连接层把每个像素都当作独立的特征来处理完全丢弃了像素之间的空间位置关系——而图像的本质恰恰是相邻像素之间存在强相关性比如数字“1”是一条竖直的笔画这种结构信息是需要卷积操作来提取的。卷积神经网络通过局部感受野、权值共享和空间下采样三大特性来解决这个问题。卷积核每次只看一个小区域比如3x3通过滑动窗口在整张图上提取局部特征同一个卷积核在整个图像上共享权重大幅减少参数量池化层则对特征图进行下采样保留主要特征的同时降低计算量。这三者结合让CNN天然适合处理图像这种具有空间结构的数据。从实验结果来看我在同一份代码里做过对比实验用两层全连接网络训练10个epoch测试准确率约为91%换上卷积神经网络同样训练10个epoch测试准确率能到99%左右。这个差距不是调参能弥补的是模型结构对图像数据本质规律的拟合能力差异。所以这个实验虽然叫“手写数字识别”真正的核心考点是理解“为什么CNN比全连接网络更适合图像分类”。2.2 数据集分析与预处理策略MNIST数据集本身是经过处理的每张图片已经做了尺寸归一化到28x28灰度值范围是0到255其中0代表黑色背景255代表白色前景笔画。虽然MNIST的图片质量已经很规整但还是有几个点需要注意。第一是数据归一化。把像素值从0到255缩放到0到1或者-1到1的区间能帮助梯度下降更快收敛。我在代码里使用了均值0.1307、标准差0.3081这两个MNIST官方统计值做标准化这是PyTorch官方示例的推荐参数训练效果比较稳定。如果懒得查这些统计值直接除以255也可以只是收敛速度会稍慢一些。第二是数据增强。MNIST数字识别的准确率已经很高一般不需要做复杂的增强操作因为数字识别的关键挑战在于书写风格多样而MNIST已经包含了大量不同人群的书写样本。不过如果你们的实验要求扩展到其他数据集或者希望进一步提高模型的泛化能力可以尝试随机旋转、随机平移、随机缩放等操作。第三是数据加载方式。PyTorch的DataLoader支持自动分批batch、随机打乱shuffle和多进程加载num_workers这些看起来不起眼的配置会直接影响训练速度和模型收敛效果。训练集必须设置shuffleTrue否则模型会按顺序看到同一个类别的样本破坏梯度更新的随机性测试集不需要shuffle。batch size的选择上我用的64这是一个在收敛速度和内存占用之间比较平衡的值。2.3 网络结构设计一个精简版LeNet-5我最终选用的网络结构参考了经典的LeNet-5但做了适当的精简和调整让它更适配PyTorch的实现方式同时也更容易在实验报告中解释清楚每一层的作用。网络的详细结构如下第一层是卷积层输入通道为1输出通道为32卷积核大小为3x3padding为1。这一层负责提取低级特征比如边缘、角点、笔画方向等。因为MNIST只有灰度图输入通道为1输出32个通道意味着用32个不同的卷积核从不同角度提取特征。第二层是ReLU激活函数和2x2最大池化。ReLU的作用是引入非线性让网络能够拟合更复杂的函数。最大池化在2x2窗口内取最大值把特征图的尺寸从28x28降到14x14同时保留最显著的特征。第三层是卷积层输入通道为32输出通道为64卷积核大小仍为3x3padding为1。这一层负责组合低级特征提取更高级的抽象特征比如数字的轮廓结构、交叉点、封闭区域等。经过这一层后的ReLU和池化操作后特征图从14x14降到7x7。接下来是展平操作把64个7x7的特征图拉平变成3136维的向量。后面接两个全连接层第一层是3136到128中间加ReLU和dropout丢弃概率0.5第二层是128到10直接输出10个类别的得分。dropout层的设计是防止过拟合的关键。MNIST虽然数据集规模够大但模型本身也有一定的容量训练后期容易出现训练集准确率接近100%、测试集接近98%的过拟合现象。我在训练时打开了dropout推理时PyTorch会自动关闭dropout这一行的效果往往被很多初学者忽略但它在实验报告里是很值得展开讨论的一个设计决策。最后一层全连接输出10个值通过交叉熵损失函数CrossEntropyLoss处理。这里有一个容易搞混的点PyTorch的CrossEntropyLoss内部已经包含了softmax操作所以模型最后一层不需要额外加softmax直接输出原始得分即可。如果你在模型末尾加了softmax再输入CrossEntropyLoss等于做了两次softmax会导致梯度数值变小、训练变慢属于新手非常容易踩的坑。3. 核心代码实现与关键参数讲解3.1 环境准备与依赖库我完成这个实验用的环境是Python 3.9 PyTorch 2.0 CUDA 11.7但如果你没有GPU纯CPU训练也完全够用。MNIST这个规模的数据集CPU训练一个epoch大约需要30到60秒跑10个epoch也就是10分钟左右完全在可接受范围内。需要安装的依赖库如下pip install torch torchvision matplotlib numpytorchvision是PyTorch官方的视觉库自带MNIST数据集的下载接口和常见的数据预处理工具。这个库非常关键很多教程会让你手动下载MNIST的四个gz文件然后自己写解析代码完全没有必要torchvision的datasets.MNIST接口一行代码就能搞定数据加载。3.2 数据加载与预处理代码import torch import torchvision import torchvision.transforms as transforms from torch.utils.data import DataLoader transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset torchvision.datasets.MNIST( root./data, trainTrue, transformtransform, downloadTrue ) test_dataset torchvision.datasets.MNIST( root./data, trainFalse, transformtransform, downloadTrue ) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue, num_workers2) test_loader DataLoader(test_dataset, batch_size256, shuffleFalse, num_workers2)这里有几个细节值得放到实验报告里说明transforms.ToTensor()会把PIL图像或者numpy数组的HWC格式转换为CHW格式的张量同时把像素值从0到255缩放到0到1这个缩放和后面的标准化不同ToTensor是除以255Normalize是减去均值再除以标准差。normalize的均值0.1307和标准差0.3081是MNIST全数据集的统计值。标准化之后的像素值大致落在-1到1之间这能帮助优化器在不同维度上有相似的尺度从而加速收敛。我在实验中发现不做标准化直接训练模型在第3个epoch时准确率约96%而标准化后第3个epoch就能到98%左右收敛速度的差异肉眼可见。num_workers这个参数控制数据加载的子进程数量。在Windows系统上如果设置为大于0可能会报多进程相关的错误建议在Windows上设置为0。在Linux或者macOS上设置2到4可以加速数据加载因为GPU训练时数据加载往往成为瓶颈。3.3 模型定义代码import torch.nn as nn import torch.nn.functional as F class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 nn.Conv2d(1, 32, kernel_size3, padding1) self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) self.pool nn.MaxPool2d(2, 2) self.fc1 nn.Linear(64 * 7 * 7, 128) self.fc2 nn.Linear(128, 10) self.dropout nn.Dropout(0.5) def forward(self, x): x self.pool(F.relu(self.conv1(x))) x self.pool(F.relu(self.conv2(x))) x x.view(-1, 64 * 7 * 7) x F.relu(self.fc1(x)) x self.dropout(x) x self.fc2(x) return x这段代码最需要理解的是x.view(-1, 64 * 7 * 7)这一行。view操作相当于把三维特征图展平成二维矩阵-1表示自动推断这一维的大小实际就是batch size。如果不做这个展平操作全连接层无法接收三维数据。关于全连接层第一层的输入维度64 * 7 * 7这个数字不是拍脑袋定的而是通过计算得出的。28x28的输入经过第一次2x2池化变成14x14再经过第二次池化变成7x7通道数为第二个卷积层设置的64所以展平后的长度为64乘以7乘以7即3136。有些同学会问两个卷积层的输出通道为什么选32和64而不是16和32也不是128和256。这属于典型的工程经验值通道数太少特征提取能力不足通道数太多MNIST这个简单任务会出现不必要的过拟合和计算开销。32和64的组合在MNIST上是经过大量实践验证的性价比之选。3.4 训练和测试代码import torch.optim as optim device torch.device(cuda if torch.cuda.is_available() else cpu) model CNN().to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) epochs 10 train_losses [] train_accs [] test_accs [] for epoch in range(epochs): model.train() running_loss 0.0 correct 0 total 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() train_loss running_loss / len(train_loader) train_acc 100.0 * correct / total test_acc evaluate(model, test_loader, device) train_losses.append(train_loss) train_accs.append(train_acc) test_accs.append(test_acc) print(fEpoch {epoch1}/{epochs}, Loss: {train_loss:.4f}, fTrain Acc: {train_acc:.2f}%, Test Acc: {test_acc:.2f}%)这里有几个容易出问题的操作逐个说清楚。optimizer.zero_grad()出现在每次参数更新之前作用是清空上一步计算的梯度。PyTorch的梯度是累加的如果不手动清零梯度会在多个batch之间累积导致参数更新方向错误。这是一行看起来多余但绝对不能省的代码。model.train()和model.eval()这两个模式切换非常关键。我的模型里有dropout层train模式下dropout生效随机丢弃部分神经元eval模式下dropout不生效使用全部神经元。如果不切换到eval模式测试时模型的输出会带有随机性每次预测结果可能不一样。养成在训练前写train、在验证或测试前写eval的习惯。选Adam优化器而不是传统的随机梯度下降SGD理由是Adam自适应调整每个参数的学习率对初始学习率的敏感度低收敛速度快更适合课程实验这种需要快速出结果的场景。如果要在实验报告中展示更深的分析可以额外跑一组SGD加动量momentum0.9的对比实验SGDmomentum在MNIST上最终准确率往往略高一些但需要更精细地调学习率这也是一个可以写进报告的技术细节。3.5 测试与可视化代码测试阶段的代码和训练有较大的区别关键是使用torch.no_grad()来关闭梯度计算这个操作能显著减少内存占用并加快计算速度。def evaluate(model, test_loader, device): model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() return 100.0 * correct / total最终我运行10个epoch得到的测试准确率在99.1%左右最好的一次是99.24%。这个数字在MNIST上属于常规水平LeNet-5原论文报的准确率在99%左右我们的精简版能达到同样的水平说明模型结构是合理有效的。可视化部分我画了三张图放进实验报告第一张是训练损失随epoch下降的曲线第二张是训练集和测试集准确率随epoch上升的曲线第三张是模型在测试集上随机抽取的16张图片预测结果。画图用matplotlib代码不复杂核心是subplot的布局和imshow的用法。import matplotlib.pyplot as plt import numpy as np def visualize_predictions(model, test_loader, device, num_images16): model.eval() images, labels next(iter(test_loader)) images, labels images[:num_images].to(device), labels[:num_images] with torch.no_grad(): outputs model(images) _, predicted torch.max(outputs, 1) fig, axes plt.subplots(4, 4, figsize(8, 8)) for i, ax in enumerate(axes.flat): ax.imshow(images[i].cpu().squeeze(), cmapgray) ax.set_title(fTrue: {labels[i].item()}, Pred: {predicted[i].item()}) ax.axis(off) plt.tight_layout() plt.savefig(predictions.png, dpi150) plt.show()在实验报告里引用图片时重点突出那些预测错误的样本。我在测试集上统计过10000张测试图片中大约有80到90张会被预测错错误率不到1%但每一张错误的图片都值得单独拿出来分析。比如数字4被预测成9数字7被预测成2这些错误在人类看来似乎不应该发生但仔细看图片就会发现这些手写数字的笔画确实写得比较潦草边界特征模糊。这种分析能体现你对模型局限性的理解在评分中是很加分的亮点。4. 实验报告核心模块拆解从摘要到结论的写作要点4.1 摘要和引言怎么写实验报告和普通的课程作业不一样它有固定的结构要求。按照国科大深度学习课程实验报告的通用格式第一页需要写摘要和引言。摘要部分用三到五句话概括实验的核心内容基于PyTorch框架实现了卷积神经网络模型使用MNIST数据集进行手写数字识别实验最终测试准确率达到99.1%。同时简要说明模型结构的特点和实验的主要结论。引言部分需要交代实验背景从深度学习在计算机视觉领域的应用切入说明手写数字识别作为经典分类问题的研究意义。这里不需要长篇大论写三到四段即可。写引言时有一个技巧引用一两篇经典的文献比如LeCun等人1998年发表的Gradient-Based Learning Applied to Document Recognition能让报告看起来更专业。LeNet-5就是在这篇论文中提出的和我们的实验直接相关。4.2 实验设计与结果分析怎么写实验设计部分要包含数据集描述、模型结构说明、训练参数配置和评估指标。我建议用一个表格整理模型结构列名分别为层类型、输出尺寸、参数量这样一目了然。结果分析部分是评分最看重的环节不只是贴训练日志或准确率数字而是要对实验结果做出解释。比如训练准确率从第一个epoch的94%迅速上升到第五个epoch的98.7%这说明模型在前期快速学习了数字的基本结构特征。测试准确率的上升速度略慢于训练准确率这属于正常现象因为测试集上的数据模型从未见过。最终训练准确率99.4%和测试准确率99.1%非常接近说明过拟合程度很低dropout和池化在控制过拟合方面起到了作用。4.3 结论与实验心得怎么写结论部分总结实验结果突出模型的有效性和实验中的核心发现。实验心得部分要写真实的学习体会比如我一开始直接用全连接网络准确率只有91%后来改用了CNN准确率提升到99%这个对比让我直观感受到了卷积操作在图像特征提取上的强大能力。还可以写一写踩过的坑比如刚开始忘记在测试时切换model.eval()导致预测结果不稳定排查了很久才发现问题。这种真实的经验描述比任何空泛的“通过这次实验我学到了很多”都有说服力。4.4 实验报告常见扣分点提醒根据我自己的经验实验报告最容易被扣分的几个地方如下运行结果的截图不完整只截了准确率数字没有截损失曲线和预测可视化。最好把训练过程的打印输出、损失曲线图、混淆矩阵图都保存完整统一打包放进附录。代码注释太少。大部分同学提交的代码几乎没有注释这在大作业评分中很吃亏。核心代码段至少要有中文或英文注释说明每一层的作用尤其是卷积层的输入输出通道变化和全连接层的维度计算。没有分析错误案例。测试准确率99%不代表模型完美剩余的1%恰恰是值得深入分析的对象。在报告中加入错误预测的可视化和原因分析能体现你对模型的理解深度。5. 训练过程实录与常见问题排查5.1 一次完整的训练日志分析我在这里放一段真实训练过程的前几个epoch输出方便你对正常的训练节奏有一个直观的预期。Epoch 1/10, Loss: 0.3102, Train Acc: 93.45%, Test Acc: 97.21% Epoch 2/10, Loss: 0.1085, Train Acc: 97.23%, Test Acc: 97.98% Epoch 3/10, Loss: 0.0714, Train Acc: 98.05%, Test Acc: 98.51% Epoch 4/10, Loss: 0.0521, Train Acc: 98.67%, Test Acc: 98.57% Epoch 5/10, Loss: 0.0420, Train Acc: 98.94%, Test Acc: 98.75% Epoch 6/10, Loss: 0.0346, Train Acc: 99.12%, Test Acc: 98.91% Epoch 7/10, Loss: 0.0299, Train Acc: 99.26%, Test Acc: 99.01% Epoch 8/10, Loss: 0.0261, Train Acc: 99.35%, Test Acc: 99.10% Epoch 9/10, Loss: 0.0223, Train Acc: 99.47%, Test Acc: 99.13% Epoch 10/10, Loss: 0.0198, Train Acc: 99.54%, Test Acc: 99.15%这组数据有几个特征值得注意。训练集的准确率在持续上升但测试集的准确率在第七个epoch之后上升幅度变得很缓慢从99.01%到99.15%只提升了0.14个百分点。损失值还在下降从0.0299降到0.0198说明模型在训练集上仍在拟合更细的特征但这对测试集的泛化帮助已经很小了。这就是模型接近过拟合的边缘信号。很多同学会问要不要训练更多epoch让准确率再高一点从我的实践经验来说对于课程实验10个epoch已经足够了。MNIST的难度上限摆在那里再往上提升准确率需要更复杂的结构或者集成方法收益很低而且容易过拟合。实验报告里能解释清楚“为什么在10个epoch停止训练”本身就是加分项。5.2 训练不收敛的排查方法最典型的故障是损失值在一两个epoch后直接变成nan或者准确率一直停留在10%左右相当于随机猜测。根据我实际遇到的问题最可能的原因是学习率设置不当。Adam优化器的默认学习率是0.001这个值对MNIST来说是有效的。如果你手动把学习率调到0.01以上模型可能会发散损失直接变成nan。排查方法是打印前几个batch的损失值如果是nan先降低学习率。第二个常见问题是数据加载不对。PyTorch的DataLoader返回的形状是(batch_size, 1, 28, 28)也就是说第二维是通道数1。如果你在网络定义时把第一个卷积层的输入通道写成3因为习惯了RGB三通道就会报维度不匹配的错误。排查方法很简单在第一个batch数据进入模型前打印images.shape确认维度和模型输入一致。第三个问题是类别不平衡或者标签错位。MNIST数据集本身的类别分布是均匀的每个数字约6000张训练图片不存在类别不平衡问题。如果自己做数据集就要检查标签是否从0开始连续编号因为CrossEntropyLoss期望的标签是0到9的整数。5.3 测试准确率波动的原因和解决办法有些同学会发现每次运行训练代码得到的测试准确率都不一样波动范围在98.8%到99.2%之间。这是正常的因为训练的初始权重是随机初始化的数据打乱的顺序也不同。要想得到可复现的结果可以设置随机种子torch.manual_seed(0) np.random.seed(0)手动固定随机种子之后多次运行的结果会完全一致。这个设置在做对比实验时尤其重要只有在相同初始条件下对比不同模型或不同超参数结论才是公平的。如果测试准确率在98%以下通常意味着模型欠拟合或者数据预处理有问题。检查的顺序是先看训练集准确率如果训练集准确率也低说明模型容量不足或者学习率太低如果训练集准确率很高而测试集准确率低说明过拟合需要增加dropout比例或者减少模型参数量。5.4 数据增强的适用边界MNIST数据增强有一个容易犯的错误使用随机旋转时角度不宜设置过大否则会把数字6旋转成9或者把2旋转成7反而引入错误的标签信息。我建议旋转角度控制在10度以内平移控制在2个像素以内。做不做数据增强对MNIST的最终准确率影响很小因为MNIST本身已经足够多样但对于理解数据增强的作用机制很有帮助。如果你想把实验做深一点可以在实验报告增加一个对照实验同一模型分别在有无数据增强的情况下训练比较测试准确率。正常情况下数据增强可能会让测试准确率略有下降或持平因为测试集中的数字基本是正的旋转生成的数据反而可能干扰模型学习。这个反直觉的发现写进报告里比只是简单地说“加了数据增强效果更好”要有价值得多。6. 如何让实验成绩更亮眼进阶优化方向与实用心得6.1 三个值得尝试的进阶方向如果你的课程实验要求比较高或者你想争取一个更好的成绩可以在基础实验之外做三个方向的延伸。第一个方向是混淆矩阵分析。torchmetrics库提供了ConfusionMatrix接口也可以手动实现。混淆矩阵能展示每个类别的误判情况我运行的结果中混淆最集中的是4和9之间的误判以及3和5之间的误判。这类误判的根源在于手写体中4的顶部开口和9的底部圆圈在某些人书写时非常相似3和5则都可能带有明显的曲线底部。结合具体样本分析误判原因能体现你对分类问题的深入理解。第二个方向是梯度类激活图可视化。Grad-CAM能够显示出模型在分类时重点关注图像的哪些区域对于手写数字来说模型通常会重点关注笔画的中心区域和转折点。这个可视化需要写相对复杂的代码但效果图放在实验报告中非常有冲击力。如果你的课程没有教到这一块可以量力而行。第三个方向是超参数敏感性实验。固定其他条件单独改变卷积核数量、学习率、dropout比例观察准确率的变化绘制成曲线或表格。这类实验的工作量不大但能让实验报告显得很有深度因为这体现的是一种系统的实验方法论而不只是跑通一条代码。6.2 给初学者的几条实操建议第一不要一上来就复制网上的完整代码。先自己搭一个最简单的网络哪怕只有两个卷积层和一个全连接层跑通之后在逐步加深。这个过程能帮你建立对每个组件作用的直觉尤其是对维度变化的计算。第二学会用print调试维度。每一层之后打印输出张量的形状这是排查维度错误最高效的方法。很多初学者看到维度报错就慌但实际上几乎所有的维度错误都可以通过逐层打印来定位。使用PyTorch时可以在forward函数中加print(x.shape)跑完一个batch后删除。第三把代码写到模块化。模型定义、训练函数、评估函数、画图函数分开写不要全部堆在一个文件里。这不仅让代码更清晰更重要的是方便做实验对比你需要反复切换不同模型结构和超参数来跑实验。我最终提交的代码结构是data_loader.py、model.py、train.py、evaluate.py、visualize.py五个文件每个文件只负责一件事。第四保存模型的checkpoint。在训练过程中定期保存模型权重这样即使训练中断也不需要重新开始。保存方式很简单torch.save(model.state_dict(), model_epoch_{}.pth.format(epoch))加载时用model.load_state_dict(torch.load(model_epoch_10.pth))。6.3 这个实验做完之后还能做什么手写数字识别虽然看起来只是一个入门实验但它代表了一整套标准的图像分类问题解决框架。换一个数据集比如CIFAR-10或者Fashion-MNIST只需要修改数据加载部分的代码和最后一个全连接层的输出维度其他部分几乎不用动。换一个模型结构从LeNet-5换到ResNet或者VGG只需要修改模型定义部分。我在完成这个实验之后沿着同样的思路做了一个Fashion-MNIST的分类实验模型结构几乎没变只是把数据改成十个类别的服装图片。结果发现相同结构的CNN在Fashion-MNIST上的准确率只有90%左右比MNIST低了将近9个百分点。这个对比非常直观地说明了一个问题任务的难度不在于模型结构有多复杂而在于数据集本身的特征区分度。MNIST的数字笔画结构清晰、类间差异大所以简单模型就能有很高准确率Fashion-MNIST中有些类别比如衬衫和外套外观上就很相似需要更强的特征提取能力。这个认识对你后续选择模型和评估实验结果会很有帮助。最后一件事也是我认为这个实验最有价值的地方它会逼着你完整体验一遍从数据加载、模型设计、训练调参到结果分析的全流程。很多人在课程结束后很久都记不清那些理论公式但绝不会忘记自己第一次看到训练准确率稳定上升、最终从测试集上输出99%数字时的那种成就感。把这个过程完整记录下来包括踩坑和修复的每一个细节这份实验报告的价值就远远不止一个分数了。本文还有配套的精品资源点击获取
返回列表