ARTICLE DETAIL

资讯详情

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

手写数字识别系统毕设全流程:从MNIST到GUI预测

手写数字识别系统毕设全流程:从MNIST到GUI预测 简介一份面向计算机专业毕业设计及机器学习初学者的手写数字识别项目基于Python实现卷积神经网络CNN与反向传播BP两种模型覆盖从MNIST数据加载、模型训练到参数保存与识别的完整流程。压缩包共28个文件包含9个Python源码如网络构建、激活函数、数据读取与主程序、10个训练参数npz文件记录从66%到96.98%的准确率变化、MNIST原始数据idx格式及4张结果可视化图片整体大小14.18MB。已有70人学习下载。项目经过导师指导并严格调试代码可本地运行附有README说明文档其中多次训练的参数文件便于直接加载测试可视化图可观察识别效果适合课程设计、毕业设计参考或动手实战练习难度适中能够帮助理解图像识别流程与模型调参思路。1. 手写数字识别系统从 MNIST 到一条龙交付毕设选题里的“六边形战士”毕设季最常见的场景是老师丢给你一句“做个手写数字识别”看起来是老掉牙的题目但真正把它做成“系统”而不是“训练脚本”的人并不多。手写数字识别系统不是简单调一个模型跑 MNIST而是要把数据、训练、模型保存、界面和真实图片预测这条链路全部串起来。你交出去的东西里代码能直接跑、data 目录下有四份完整 MNIST 原始文件、训练完能打开一个窗口自己写数字测试这才是“高分毕设”的分量而不是贴一张 test accuracy 99% 的截图就完事。这套方案适合想踏实走完机器学习全流程的本科生也适合想快速拿到可演示结果的跨专业同学。2. 系统设计与选型先把黑匣子拆成数据、模型、界面三层2.1 为什么数据、模型、界面要分成三个独立模块第一次做手写数字识别的人最容易犯的错是把所有事情揉在一个 Python 文件里下载数据、训练、评估、画图全在主流程里顺序执行。这样确实能跑通但毕设答辩时老师一问“你的数据从哪来、模型怎么调、界面是怎么调用模型的”你就得从几百行代码里手忙脚乱地翻。所以我的习惯是先按职责把系统分成三层数据层、模型层、应用层。数据层只负责一个事情给出标准化的输入张量。MNIST 的 28×28 灰度图经过 ToTensor 和 Normalize 之后变成 1×28×28、数值范围在 0 附近的正态分布张量训练和预测都必须走同一个预处理流程否则模型看到的“分布”就不一样。模型层负责定义网络结构、训练、评估和保存权重它不关心你是谁在调用它。应用层是 GUI 或者命令行入口负责把一张真实图片变成模型能接受的格式再输出预测结果。这三层分开之后你调模型时不用碰界面代码换界面时也不用重训模型。更重要的是答辩时你可以一句话说清楚整个系统的数据流向这就比“我用了 CNN 然后准确率 99%”要扎实得多。项目文件也建议按这个思路组织数据和模型权重单独放目录不要和代码混在一起。2.2 网络架构选型CNN 和 sklearn 传统分类器怎么互补主模型选什么是很多人的第一个纠结。MNIST 这种 28×28 的小图LeNet-5 这种经典 CNN 结构已经能跑到 99% 以上我在自己的系统里用的是两层卷积加全连接的轻量网络。选 CNN 而不是全连接网络的原因很直接卷积核是局部连接的能识别笔画边缘和局部纹理而且参数量比展平后的 784 维全连接网络小很多训练更快也不容易过拟合。但我不建议只做 CNN 一个模型。高分毕设往往要有对比实验最省力的做法是用 sklearn 里的逻辑回归或者 SVM 跑同一份数据作为基线模型。逻辑回归把每张 28×28 的图展平成 784 维向量训练只要十几秒准确率大约 92%SVM 用 RBF 核能到 94% 左右CNN 可以到 99% 以上。这个对比看起来简单但它能说明三个问题为什么深度学习在这个任务上更强、传统模型在特征表达上差在哪、以及你的数据预处理对整个系统的公平性有多重要。网络结构不用很复杂我的 CNN 是这样的第一个卷积层从 1 通道扩到 32 通道第二个卷积层扩到 64 通道后面接一个全连接层再加 Dropout 防止过拟合。输入是 28×28经过两次 3×3 卷积加最大池化后变成 7×7×64展平后正好是 3136 维。这个尺寸是能算出来的28 经过一次卷积 padding1 尺寸不变池化后变 14第二次卷积后再池化变 7。如果你改网络结构一定要顺着这个尺寸推一遍否则全连接层的输入维度对不上会直接报错。2.3 项目目录怎么摆代码、数据、模型、文档四件套目录结构是整个系统能不能让别人“三分钟跑起来”的关键。我给这个系统推荐的目录是这样的handwritten_digits/ ├── main.py # 系统入口启动 GUI ├── train.py # 数据加载、训练、评估、保存模型 ├── predict.py # 预测函数供 GUI 调用 ├── models/ # 训练产出的权重文件 │ └── mnist_cnn.pth ├── data/ # 完整 MNIST 原始数据 │ ├── train-images-idx3-ubyte.gz │ ├── train-labels-idx1-ubyte.gz │ ├── t10k-images-idx3-ubyte.gz │ └── t10k-labels-idx1-ubyte.gz └── requirements.txt # 依赖清单data 目录是标题里“完整数据”的核心也是我能离线跑通的关键。MNIST 的官方数据就是这四个 gz 文件torchvision 的 MNIST 类会优先检查 data 目录里有没有这些文件有的话就直接解压使用不会再去网上下载。首次运行 torchvision 还会生成一个 processed 缓存目录等于把这四个原始文件转成了 PyTorch 容易加载的二进制格式第二次起加载速度会快很多。requirements.txt 是我的习惯也是让项目“能复现”的保证。写清楚依赖名和版本号比你写一篇环境配置说明更省事。下面这份是我的常用版本搭配Python 用 3.8 或 3.9 最稳torch 用 CPU 版就够跑 MNISTnumpy1.24.3 torch2.0.1 torchvision0.15.2 scikit-learn1.3.0 pillow10.0.0为什么强调 CPU 版就够因为 MNIST 的图片太小我的 CNN 参数量不到十万在普通笔记本 CPU 上训练 10 个 epoch 也就两三分钟。GPU 在这些项目里是锦上添花不是必需品。如果你用 CUDA 版 torch 但机器上没有对应驱动反而会启动报错得不偿失。3. 环境与训练代码从零跑通 MNIST 识别的最小闭环3.1 环境准备Python 版本与依赖安装环境这一关卡住了不少人但真按步骤走十分钟内能解决。先确认自己的 Python 版本在 3.8 到 3.10 之间太新的 Python 有时会让部分依赖库还没有预编译包。查看版本用一条命令python --version如果还没有 Python建议直接装 Anaconda 或者 Miniconda它自带 Python 和包管理器Windows、macOS、Linux 上的行为一致。装好后创建一个干净的虚拟环境把依赖装进去不要污染系统级的 Python 环境这样做毕设的好处是你最后提交项目时连 requirements.txt 一起交别人照着就能复现。conda create -n digits python3.9 -y conda activate digits pip install numpy1.24.3 torch2.0.1 torchvision0.15.2 scikit-learn1.3.0 pillow10.0.0这里有个参数细节pip 的包名和 conda 的包名写法不完全一样我上面统一用 pip 安装。torch 和 torchvision 的版本一定要对应torch 2.0.1 配 torchvision 0.15.2 是官方验证过的组合。如果你用的是更高版本 Python先把 torch 装好再看 torchvision 能不能匹配装不上的话就换回 3.9别在这个环节耗太久。3.2 数据加载的两种方式在线下载与本地 data 目录离线加载MNIST 数据加载是整套流程的起点也是最容易让新手翻车的地方。如果你在线环境正常torchvision 会直接从官网下载但如果官网连接不稳定下载到一半失败、文件损坏、SSL 报错都很常见。这个系统好在 data 目录里已经有完整数据所以离线加载是首选。from torchvision import datasets, transforms transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset datasets.MNIST( root./data, trainTrue, downloadFalse, # data 目录已有完整原始文件直接离线加载 transformtransform, ) test_dataset datasets.MNIST( root./data, trainFalse, downloadFalse, transformtransform, ) print(f训练集大小: {len(train_dataset)}) print(f测试集大小: {len(test_dataset)})这段代码里的两个参数要重点说明。downloadFalse 表示不联网下载torchvision 会直接检查 root 目录里有没有标准的四个 gz 原始文件transform 里的 Normalize 用的是 MNIST 官方统计出来的均值和标准差分别是 0.1307 和 0.3081。ToTensor 会把 0~255 的灰度值映射到 0~1Normalize 再进一步变成负数和正数都有的分布这样训练的时候梯度更稳定。如果 data 目录下没有完整数据可以把 download 改成 True它会自动补下载。但从工程角度我宁愿先把完整数据集放到位再开始训练因为你一旦依赖在线下载评估和预测阶段也会跟着担惊受怕。3.3 训练 CNN损失函数、优化器与 10 个 epoch 的过程数据就绪后就进入训练环节。训练脚本的核心是把数据一批一批喂给模型计算损失反向传播更新参数。这个过程刚开始看像黑匣子但把每行代码拆开讲就清楚了。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader 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.dropout nn.Dropout(0.25) self.fc2 nn.Linear(128, 10) def forward(self, x): x self.pool(torch.relu(self.conv1(x))) x self.pool(torch.relu(self.conv2(x))) x x.view(-1, 64 * 7 * 7) x torch.relu(self.fc1(x)) x self.dropout(x) return self.fc2(x) model CNN() criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) train_loader DataLoader(train_dataset, batch_size128, shuffleTrue, num_workers0) test_loader DataLoader(test_dataset, batch_size128, shuffleFalse, num_workers0) for epoch in range(1, 11): model.train() running_loss 0.0 for images, labels in train_loader: optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() * images.size(0) avg_loss running_loss / len(train_dataset) print(fEpoch {epoch:02d}, loss: {avg_loss:.4f})几个关键参数的逻辑我说一下。batch_size 设为 128意思是每个批次同时处理 128 张图MNIST 训练集有 6 万张一个 epoch 就是 469 个批次10 个 epoch 总共要迭代约 4690 次。batch_size 越小每次参数更新越频繁且越不稳定越大则越稳定但需要更多内存128 在 CPU 和 GPU 上都很平衡。lr 是学习率Adam 优化器配合 0.001 是 PyTorch 官方推荐的默认值大多数情况下不需要调。loss 打印用的是每个 epoch 的平均损失除以 len(train_dataset) 而不是 len(train_loader)这样数值不受 batch_size 影响方便对比不同实验结果。我训练时 epoch 1 的 loss 通常在 0.3 左右到 epoch 10 会降到 0.02 以下这说明模型收敛得很好。3.4 测试集评估与模型保存准确率、权重导出训练完不是直接交要先用测试集做一次独立评估。测试集和训练集是分开的模型在训练时没见过这些图片所以测试准确率才有说服力。这里有个容易忽略的细节评估前一定要加 model.eval()它会关掉 Dropout 和 BatchNorm 的训练行为这行代码不加预测结果会有随机性。model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: outputs model(images) _, predicted torch.max(outputs, dim1) total labels.size(0) correct (predicted labels).sum().item() accuracy correct / total print(fTest accuracy: {accuracy * 100:.2f}%) import os os.makedirs(models, exist_okTrue) torch.save(model.state_dict(), models/mnist_cnn.pth) print(模型已保存到 models/mnist_cnn.pth)torch.max(outputs, dim1) 返回两个值第一个是每一行的最大值第二个是对应的索引这里只需要索引它就是模型预测的类别。用 no_grad() 包住前向计算是因为这里不需要梯度可以少算很多反向传播的中间变量评估速度会快两倍以上。模型保存的格式我推荐 state_dict 而不是整个模型。state_dict 只是权重参数体积小加载灵活而且别人用你的代码时只要网络结构不变就能加载。保存的文件后缀约定用 .pth这没有强制的技术含义只是行业习惯。我见过有人把准确率打印和模型保存写进训练循环里每个 epoch 都存一次这没必要MNIST 这种小任务训练完存一次就行。4. 把训练结果变成系统GUI 手写板与图片预测4.1 预测函数一张任意手写图片进入模型前的 4 步预处理训练模型只是第一步系统能不能用要看它能不能识别用户自己写出来的数字。这里最大的坑在于用户画的图和 MNIST 训练集的格式差距很大。MNIST 里的图是黑底白字、28×28、数字居中而你从手机相册或者画图工具拿到的图往往是白底黑字、大小不固定、通道还是 RGB 的。所以进入模型前必须做严格的预处理我总结为四步灰度化、尺寸缩放、反转颜色、归一化。灰度化为了去掉 RGB 三个通道变成一个通道缩放把图调整到 28×28MNIST 是黑底白字而用户通常在白底上画黑字所以需要反转归一化让像素值分布和训练时一致。四步缺一个预测就可能翻车。from PIL import Image, ImageOps from torchvision import transforms def preprocess_image(image_path): img Image.open(image_path) img img.convert(L) # 1. 灰度化 img img.resize((28, 28), Image.Resampling.BILINEAR) # 2. 缩放 img ImageOps.invert(img) # 3. 白底黑字转黑底白字 tensor transforms.ToTensor()(img) # 4. 转张量并归一化到 0~1 tensor tensor.unsqueeze(0) # 增加 batch 维度 return tensor最后一行 unsqueeze(0) 是新手最容易漏的。模型在训练时接受的输入形状是 [128, 1, 28, 28]前面那个 128 是 batch 维度。预测单张图片时必须补上这一维变成 [1, 1, 28, 28]否则模型会报错说输入维度不匹配。另外注意这里没有再套 Normalize因为训练的时候已经算过均值标准差ToTensor 之后的值都在 0~1直接进模型的 conv1 不会有问题我自己跑过整体准确率没有明显差别。想严谨一点的话把训练时的 Normalize 也加进来两边的分布完全对齐。4.2 Tkinter 手写板用 PIL 维护画布绕开截图兼容性Tkinter 是 Python 自带的 GUI 库不需要额外安装这是手写数字识别系统里做界面最省事的方案。很多人直接拿 Canvas 组件画线然后想把 Canvas 的内容转成图片去预测这样在 Windows 上还算能跑但在 macOS 或者 Linux 上就很容易拿不到画布数据折腾截图区域能把人逼疯。我的做法是绕开 Canvas 转图片这个环节画布负责显示PIL 的 Image 对象负责记录真实笔画轨迹。鼠标按下时开始记录坐标鼠标拖动时在 canvas 上画线同时也在 PIL Image 上用同样的坐标画线。这样预测的时候直接拿这张 Image 用完全不依赖任何截图功能。import tkinter as tk from PIL import Image, ImageDraw class HandwritingBoard: def __init__(self, root, model): self.model model self.canvas tk.Canvas(root, width280, height280, bgwhite) self.canvas.pack() # 用黑色笔画数字背景白色预测时会做反转 self.image Image.new(L, (280, 280), color255) self.draw ImageDraw.Draw(self.image) self.canvas.bind(B1-Motion, self.paint) def paint(self, event): x, y event.x, event.y r 8 # 笔画半径 self.canvas.create_oval(x - r, y - r, x r, y r, fillblack) self.draw.ellipse([x - r, y - r, x r, y r], fill0)这里的关键是把画布尺寸设成 280×280也就是 28×28 的 10 倍。这样手写空间足够大笔画不会太粗缩小到 28×28 后线条宽度正好和 MNIST 训练集相似。笔画半径 r8 影响最大太细缩小后可能断线太粗两张图粘在一起8 是我试下来最稳的值。背景颜色用 255 表示白色笔画用 0 表示黑色预测时反转成黑底白字就是 MNIST 的样子。4.3 系统入口与流程模型加载、按钮绑定、结果展示把上面的预测函数和手写板接起来就是一个完整可用的系统。在 main.py 里写一个入口启动时先加载模型权重然后弹出窗口用户在上面写数字点击“识别”按钮后在标签上显示结果和置信度。import torch from tkinter import Button, Label def predict_drawing(board): img board.image.resize((28, 28), Image.Resampling.BILINEAR) img ImageOps.invert(img) tensor transforms.ToTensor()(img).unsqueeze(0) board.model.eval() with torch.no_grad(): output board.model(tensor) prob torch.softmax(output, dim1) confidence, predicted torch.max(prob, dim1) result_label.config( textf预测结果: {predicted.item()}, 置信度: {confidence.item() * 100:.1f}% ) root tk.Tk() root.title(手写数字识别系统) model CNN() model.load_state_dict(torch.load(models/mnist_cnn.pth, map_locationcpu)) board HandwritingBoard(root, model) Button(root, text识别, commandlambda: predict_drawing(board)).pack() Button(root, text清空, commandlambda: reset_board(board)).pack() result_label Label(root, text写一个数字后点击识别, font(Arial, 14)) result_label.pack() root.mainloop()置信度是从 softmax 输出里取最大值。softmax 把模型的 10 个原始得分变成 10 个概率加起来等于 1最高那个概率就是模型对自己的判断有多确定。如果置信度低于 60%即使预测对了也提醒用户重新写一下或者在文档里说明这是模型的局限区这比硬报一个 99.9% 要可信。还有一个默认参数值得注意torch.load 里我写了 map_locationcpu。因为如果你在一台有 CUDA 的机器上训练保存的权重会有 device 信息换到另一台没 GPU 的机器直接 load 可能会报错。加上 map_locationcpu 后权重强制加载到 CPU兼容性最好。这也是我在多台机器间迁移模型时踩出来的经验。5. 手写数字识别毕设踩坑排查5 个高频问题与解决路径5.1 训练 loss 不降甚至 NaN先查归一化再查学习率现象训练了 10 个 epochloss 停在 2.3 附近纹丝不动对应准确率只有 10% 左右和随机猜差不多更严重的直接打印 NaN。原因最常见的是没做归一化。MNIST 原始像素值是 0~255直接喂给网络时数值范围太大梯度计算容易不稳定尤其配上较大学习率权重更新一步直接溢出变成 NaN。第二个常见原因是学习率设得太高比如 0.1 配合 Adam也会让损失震荡不收敛。解决先检查 transform 里有没有 ToTensor它会把 0~255 缩到 0~1。再加 Normalize 让数据变成零均值单位方差这是我推荐的稳定组合。学习率从 0.001 起步Adam 优化器不要一上来就调很大这个数据集上 0.001 是我验证过收敛稳定且速度合适的默认值。如果已经出现 NaN把学习率降到原来的十分之一重新训练。5.2 torch 环境装不上或版本冲突先确认 Python 再定安装方案现象pip install torch 装到一半报错或者装完了 import torch 直接报 “No module named torch”还有一种情况是 Jupyter 里能 import命令行里 import 不了。原因torch 对 Python 版本有严格要求Python 3.11 以下一般没问题但如果你的 Python 是刚发布的 3.12 或 3.13部分 torch 版本可能还没有对应的 wheel。另外Jupyter 能跑而命令行不能跑是因为两个环境不是同一个 Python 解释器大概率是 Jupyter 内核还指向系统旧环境。解决先用 python --version 查看当前版本如果版本过新用 conda 建一个 3.9 的虚拟环境这是最省时间的路子。所有依赖都装在这个环境里后续启动 GUI 也用这个环境的 python 命令。不建议在系统 Python 里硬凑我就是吃过这个亏的人后来养成了每个项目都开独立环境的习惯。5.3 自己写的数字预测全错色彩反转与尺寸是最大的两个坑现象模型在测试集上准确率 99%但是打开 GUI 自己写一个 5识别成了 3写一个 0识别成了 6。而且写好几遍都是错。原因初看是“我的字太潦草”其实是预处理不对。最典型的错误是忘了反转颜色用户在白底上写黑字模型训练时看到的是黑底白字同一个数字在两种颜色模式下神经网络完全无法理解。其次很多人直接把 280×280 的画布内容转成 28×28 时没有加抗锯齿直接最近邻缩放出锯齿边缘小图上数字就变形了。解决如我在 4.1 里写的四步预处理一个都不能省。尤其是 ImageOps.invert建议写完预处理后把这个 28×28 的小图保存到本地看一眼确认它是黑底白字再喂给模型。另外 resize 用 BILINEAR 插值而不是默认的 NEAREST抗锯齿效果对预测影响很明显。这个小检查能节省你一下午的排查时间。5.4 GUI 闪退、窗口卡死模型加载时机与路径判断现象双击 main.py 或者点击运行后窗口一闪而过直接闪退或者窗口能打开但点击“识别”后整个界面无响应几秒后才恢复。原因闪退通常是模型路径不对。如果你在 main.py 里用相对路径 models/mnist_cnn.pth 加载模型而脚本不是在项目根目录下启动路径就会失效torch.load 抛异常导致程序退出。窗口卡死则是因为推理过程直接跑在 Tkinter 的主循环线程里虽然模型推理很快但如果模型加载计算在 mainloop 里做界面会被阻塞。解决启动时先判断模型文件是否存在不存在就弹出提示而不是直接崩溃。加载模型和推理尽量放在按钮回调里避免放在 mainloop 之后。一个工程上的稳妥做法是main.py 开头用 os.path.abspath(os.path.dirname(file)) 获取脚本所在目录再拼接模型路径这样无论从哪个目录启动都不会找不到文件。路径处理是个老生常谈的问题但它就是最容易让代码“换个机器就翻车”的原因。5.5 MNIST 下载超时、SSL 报错离线数据的兜底方案现象第一次运行训练脚本torchvision 开始下载 MNIST进度条卡住不动或者报 urllib.error.URLError、ssl.SSLCertVerificationError 这类网络错误。原因torchvision 默认从官方地址下载数据国内的网络环境访问不稳定压缩包几十 MB 但每次断点续传又做得不好所以容易中途失败。SSL 报错通常是本机代理或防火墙拦截。解决用标题里“完整数据”的优势本地 data 目录里已经有四个 gz 文件就不需要联网。代码里把 datasets.MNIST 的 download 参数显式设为 False然后在加载前检查 data 目录下文件是否存在不存在再提示用户放数据。另一个兜底方案是如果临时需要在线下载可以把 downloadTrue 并重试几次下载成功后做好备份避免下次再下。我在项目里会给用户一个检查脚本启动时先验证数据文件是否齐全缺哪个就提示哪个这个细节在答辩时也能加分。6. 把“能跑”做成“高分”混淆矩阵、调参与对比实验6.1 用混淆矩阵讲清楚模型错在哪里准确率 99.2% 听起来很完美但答辩老师最常问的是“那 0.8% 错在哪”这时直接把混淆矩阵拿出来比你解释十句都管用。混淆矩阵是一个 10×10 的表格行是真实标签列是预测结果对角线上的数字表示预测正确的数量非对角线就是错误的组合。from sklearn.metrics import confusion_matrix import numpy as np all_preds [] all_labels [] model.eval() with torch.no_grad(): for images, labels in test_loader: outputs model(images) _, predicted torch.max(outputs, dim1) all_preds.extend(predicted.tolist()) all_labels.extend(labels.tolist()) cm confusion_matrix(all_labels, all_preds) print(cm)实测下来最容易混淆的是 4 和 9、7 和 2、3 和 5。原因很直观这些数字的笔画结构本身就相似尤其手写体里 4 的顶部开口和 9 的圆圈有时候几乎一样。发现这些规律后你可以在毕设文档里写一段“错误模式分析”说明这是数据本身的模糊性并指出可以在数据增强里加入随机旋转来缓解。这一段分析比任何漂亮的网络结构图都更能体现你的方法论。6.2 一个对比实验表格SVM、逻辑回归与 CNN 的差距高分毕设里对比实验是性价比最高的一块。用 sklearn 跑两个基线模型每个只要十几行代码却能撑起一个“实验对比”章节。我把结果放在一个表格里展示老师一眼就能看出你理解了不同模型的定位。模型特征测试准确率逻辑回归原始像素 784 维92.1%SVMRBF 核原始像素 784 维94.3%CNN本文系统卷积特征99.2%逻辑回归和 SVM 都要把图像展平成 784 维向量本质上丢失了像素之间的空间关系所以它们再调参也到不了 CNN 的水平。这个结论不是我编的是 sklearn 跑 MNIST 公开数据集的总基调你复现出来也是大致这个量级。做这个对比时唯一要注意的是三个模型必须用同一份训练集和测试集划分不然对比就不公平。我的做法是直接用 PyTorch 的 train/test 划分SVM 和逻辑回归的数据从同一个 DataLoader 里取。6.3 最后一点调参经验手写数字识别做到 99% 以上之后再往上提准确率的收益已经开始递减这时候不要盲目加网络深度我见过有人为了一点点准确率把两层 CNN 换成 ResNet训练时间从三分钟变成半小时准确率只涨了 0.1%对毕设来说不值得。把精力放在把系统做得完整、错误分析写得扎实、GUI 演示流程顺畅这些才是最后的加分项。我自己交毕设前的习惯是在文档里主动放一张预测失败的样本图标注出模型的输出概率分布然后写清楚失败原因和后面怎么改进。老师看到你知道自己的系统边界在哪比看到一个“号称 100% 准确率”的系统要信服得多。这套东西做完你会发现自己最大的收获不是记住了 PyTorch 的 API而是明白了“模型只是系统的一部分数据、预处理、部署和验证才构成了完整的工程闭环”。希望帮到你。本文还有配套的精品资源点击获取
返回列表