资讯动态

Python卷积神经网络实战:手写数字识别从零到99%准确率

发布时间:2026/10/9 19:37:44 来源:尧图企业网站定制
简介这份资源是面向高校学生与深度学习入门者的卷积神经网络手写数字识别完整项目源码可直接用于课程大作业、毕业设计或自学练手帮助解决从模型搭建到训练评估的全流程实现问题。压缩包共15个文件约555KB以8个Python脚本为核心涵盖网络结构定义、数据集加载、参数配置、优化器与训练分析等模块另含3张训练过程与效果图、1个保存的模型参数文件及yaml配置等辅助内容结构清晰、便于按模块阅读。目前已有618人学习下载说明其在同类作业项目中具有一定参考价值。项目经过严格调试评审分达95分以上读者可据此理解卷积层、池化层与全连接层的组织方式掌握损失曲线绘制、参数保存与加载、识别效果可视化等关键环节并借鉴其代码分层与配置管理思路快速完成自己的手写数字识别实践。1. 手写数字识别为什么成了 CNN 入门的第一块试金石如果你正在找一份能直接跑通、还能拿去交大作业的 Python 卷积神经网络项目手写数字识别几乎是绕不开的选题。它数据集小、标签干净、单卡 CPU 都能训但麻雀虽小五脏俱全数据加载、卷积堆叠、批归一化、Dropout、学习率调度、模型保存与推理一条链路全都能走一遍。很多人第一次真正理解「卷积核在学什么」「为什么验证集准确率会先升后降」就是在这个任务上撞出来的。这份源码类项目的价值不在于模型多深而在于它把一条完整的训练-评估-推理流水线摊开给你看改几行就能换成自己的数据。适合两类人一是刚学完反向传播、想找个能跑通的小项目练手的新手二是需要一份结构清晰、指标能上 95 分的大作业底稿的开发者。下面我按实际复现顺序把选型、代码、参数和踩坑一次讲透。2. 从 MNIST 到可训练张量数据管线怎么搭才不翻车2.1 为什么选 MNIST 而不是自己拍照片MNIST 是 70000 张 28×28 灰度图60000 训练 10000 测试类别 0-9 均衡。它的像素已经居中、归一化过省掉了最耗时的数据清洗。常见做法是先用它把网络跑通再迁移到自采数据。自采数据最大的坑是背景干扰和笔画粗细不一致直接套 MNIST 训练的模型准确率会掉到 70% 以下。所以这份源码的定位是「教学基线」不是「生产模型」。如果你要识别真实场景的票据数字或电表读数得在 MNIST 预训练权重上做微调而不是从零训。2.2 用 torchvision 搭 Dataset 与 DataLoaderimport torch from torch.utils.data import DataLoader from torchvision import datasets, transforms # 训练集做轻微增强测试集只做标准化 train_tf transforms.Compose([ transforms.RandomAffine(degrees10, translate(0.1, 0.1)), # 模拟手写倾斜 transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) # MNIST 全局均值方差 ]) test_tf transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_set datasets.MNIST(root./data, trainTrue, downloadTrue, transformtrain_tf) test_set datasets.MNIST(root./data, trainFalse, downloadTrue, transformtest_tf) train_loader DataLoader(train_set, batch_size128, shuffleTrue, num_workers2) test_loader DataLoader(test_set, batch_size256, shuffleFalse, num_workers2)逻辑说明RandomAffine只做小角度旋转和平移模拟手写时的自然抖动幅度过大会让数字变形到不可识别。Normalize用的 0.1307 和 0.3081 是 MNIST 训练集的全局均值和标准差这两个数不要随手改成 0.5否则收敛会变慢。batch_size设 128 是显存和梯度稳定性的折中CPU 训练可以降到 64。num_workers在 Windows 上建议设 0否则容易卡在启动阶段。2.3 数据划分的隐藏坑验证集从哪来很多人直接把测试集当验证集调参调完再报测试准确率这在方法论上是错的。正确做法是从 60000 训练集里切 5000 张做验证集测试集只在最后跑一次。源码里如果没做这个切分你自己补上from torch.utils.data import random_split train_sub, val_sub random_split(train_set, [55000, 5000]) val_loader DataLoader(val_sub, batch_size256, shuffleFalse)这样早停和调参都看验证集最终指标才有说服力。大作业答辩时老师常问这一句别在这翻车。3. 网络结构怎么定三层卷积够不够上 95 分3.1 一个能稳定到 99% 的轻量 CNNimport torch.nn as nn import torch.nn.functional as F class DigitCNN(nn.Module): def __init__(self, num_classes10): super().__init__() self.conv1 nn.Conv2d(1, 32, 3, padding1) self.bn1 nn.BatchNorm2d(32) self.conv2 nn.Conv2d(32, 64, 3, padding1) self.bn2 nn.BatchNorm2d(64) self.conv3 nn.Conv2d(64, 128, 3, padding1) self.bn3 nn.BatchNorm2d(128) self.pool nn.MaxPool2d(2, 2) self.dropout nn.Dropout(0.5) self.fc1 nn.Linear(128 * 3 * 3, 256) self.fc2 nn.Linear(256, num_classes) def forward(self, x): x self.pool(F.relu(self.bn1(self.conv1(x)))) # 28-14 x self.pool(F.relu(self.bn2(self.conv2(x)))) # 14-7 x self.pool(F.relu(self.bn3(self.conv3(x)))) # 7-3 x x.view(x.size(0), -1) x self.dropout(F.relu(self.fc1(x))) return self.fc2(x)逻辑说明三次卷积通道数 32→64→128每次后接 BatchNorm 和 ReLU再池化把空间尺寸从 28 降到 3。padding1保证 3×3 卷积不缩小特征图尺寸只由池化决定。最后展平成 128×3×31152 维接全连接。这个结构参数量约 40 万CPU 上跑 10 个 epoch 大概几分钟。参数说明Dropout(0.5)放在全连接前防止过拟合卷积层后一般不加 Dropout加了反而拖慢收敛。BatchNorm的 momentum 默认 0.1 就够别乱调。如果显存紧张把第三层卷积砍掉准确率大概掉 0.3 个百分点仍能过 95 分。3.2 训练循环与学习率调度import torch.optim as optim from torch.optim.lr_scheduler import StepLR device torch.device(cuda if torch.cuda.is_available() else cpu) model DigitCNN().to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) scheduler StepLR(optimizer, step_size5, gamma0.5) for epoch in range(15): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() loss criterion(model(imgs), labels) loss.backward() optimizer.step() scheduler.step() # 验证 model.eval() correct total 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) pred model(imgs).argmax(1) correct (pred labels).sum().item() total labels.size(0) print(fepoch {epoch1}, val_acc{correct/total:.4f})逻辑说明Adam 配weight_decay1e-4做 L2 正则StepLR每 5 个 epoch 把学习率乘 0.5让后期收敛更稳。验证阶段必须model.eval()并包torch.no_grad()否则 BatchNorm 会更新统计量、显存也会爆。参数说明学习率 1e-3 是 Adam 的常用起点如果 loss 震荡就降到 5e-4。step_size和gamma是玄学重灾区15 个 epoch 内设 5 和 0.5 比较稳。batch 太小如 16会让 BatchNorm 统计不准验证准确率抖动明显。3.3 模型保存与推理接口torch.save(model.state_dict(), digit_cnn.pth) # 推理 model.load_state_dict(torch.load(digit_cnn.pth, map_locationdevice)) model.eval() with torch.no_grad(): logits model(single_img.unsqueeze(0).to(device)) prob torch.softmax(logits, dim1) pred prob.argmax(1).item()保存state_dict而不是整个模型换设备加载时用map_location避免报错。推理前记得unsqueeze(0)补 batch 维度输入必须是归一化后的张量否则预测会乱跳。4. 训练不收敛、准确率卡在 90% 的排查清单4.1 现象loss 一直不降准确率在 10% 附近原因通常是标签和输出维度对不上或者数据没归一化。先打印一个 batch 的labels和model(imgs).shape确认是 10 类。再检查Normalize是否漏写未归一化的像素值在 0-255梯度会炸。4.2 现象训练准确率 99%验证只有 92%这是典型过拟合。解决顺序先加 Dropout 和数据增强再把模型参数量降下来最后才考虑加数据。别一上来就堆正则先看训练验证曲线差距有多大。差距超过 5 个百分点优先砍全连接层宽度。4.3 现象验证准确率忽高忽低波动超过 2%多半是 BatchNorm 在作怪。检查验证时是否model.eval()以及 batch_size 是否太小。把验证 batch 调到 256 以上波动会明显收敛。另外shuffleTrue只给训练集验证集不要 shuffle否则指标不可复现。4.4 现象CPU 训练慢到无法忍受num_workers设 0 在 Windows 上反而更快因为多进程启动开销大。另外把batch_size提到 256减少 Python 循环次数。如果还慢把第三层卷积去掉参数量减半准确率损失很小。4.5 现象加载模型后预测全是同一个数字九成是保存和加载的结构不一致或者推理时忘了eval()。还有一种情况是输入图像没有做和训练时一样的归一化像素分布偏移导致输出坍缩。打印prob看看是不是某个类概率接近 1就能定位。5. 把 95 分变成 99 分三个我反复验证过的技巧第一个技巧是测试时增强TTA。对同一张测试图做几次小幅度平移或旋转把 softmax 概率平均后再取 argmax。MNIST 上这一招通常能涨 0.2 到 0.4 个百分点代码不到十行def tta_predict(model, img, n5): model.eval() probs [] with torch.no_grad(): for _ in range(n): aug transforms.RandomAffine(degrees8, translate(0.08, 0.08))(img) probs.append(torch.softmax(model(aug.unsqueeze(0).to(device)), 1)) return torch.stack(probs).mean(0).argmax(1).item()注意增强幅度要和训练时一致太大反而掉点。第二个技巧是余弦退火学习率把StepLR换成CosineAnnealingLR后期学习率平滑降到接近 0验证准确率曲线会更干净。第三个技巧是标签平滑把CrossEntropyLoss(label_smoothing0.1)打开能压一压过拟合对最终指标有微弱正向作用。我自己的习惯是任何一次改动只动一个变量跑完记录验证准确率别一次改三处否则出了问题根本不知道是谁的锅。这份源码类项目最大的价值不是那个 95 分的数字而是你顺着它把整条链路走一遍之后再遇到新的图像分类任务知道第一步该看数据、第二步该定基线、第三步该往哪儿调。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑