PyTorch实战:从零构建MNIST手写数字识别模型

发布时间:2026/7/7 21:20:25

PyTorch实战:从零构建MNIST手写数字识别模型
1. 项目概述PyTorch与MNIST的经典组合MNIST手写数字识别堪称深度学习领域的Hello World这个包含6万张训练图片和1万张测试图片的数据集每张都是28×28像素的灰度图像。作为计算机视觉入门的最佳试金石它具备了理想数据集的所有特质规模适中、特征清晰、标注准确。而PyTorch作为当前最受欢迎的深度学习框架之一其动态计算图和Pythonic的API设计特别适合教学和快速原型开发。这个项目我们将从零开始用PyTorch搭建一个能够识别手写数字的神经网络。不同于直接调用现成模型我们会一步步实现数据加载、网络构建、训练循环等核心环节过程中你将掌握如何用PyTorch处理图像数据全连接神经网络的基本原理训练过程中的关键参数调节模型评估与优化的实用技巧提示虽然现在的深度学习框架都提供了高级API但理解底层实现原理对debug和模型优化至关重要。这也是我坚持带学员从基础开始实现的原因。2. 环境准备与数据加载2.1 PyTorch环境配置推荐使用Anaconda创建独立环境conda create -n pytorch_mnist python3.8 conda activate pytorch_mnist conda install pytorch torchvision torchaudio -c pytorch验证安装import torch print(torch.__version__) # 应显示如1.12.1 print(torch.cuda.is_available()) # 检查GPU是否可用2.2 MNIST数据集加载PyTorch的torchvision已经内置了MNIST数据集加载工具from torchvision import datasets, transforms # 定义数据转换 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # 下载并加载数据集 train_data datasets.MNIST( rootdata, trainTrue, downloadTrue, transformtransform ) test_data datasets.MNIST( rootdata, trainFalse, transformtransform )这里有两个关键操作ToTensor()将PIL图像转换为PyTorch张量并自动归一化到[0,1]范围Normalize()使用MNIST的全局均值(0.1307)和标准差(0.3081)进行标准化注意这些统计值是针对整个MNIST数据集计算得出的使用它们可以加速模型收敛。如果处理自己的数据集需要重新计算。2.3 数据批处理与可视化创建数据加载器from torch.utils.data import DataLoader batch_size 64 train_loader DataLoader(train_data, batch_sizebatch_size, shuffleTrue) test_loader DataLoader(test_data, batch_sizebatch_size)检查一批数据import matplotlib.pyplot as plt images, labels next(iter(train_loader)) print(images.shape) # torch.Size([64, 1, 28, 28]) print(labels.shape) # torch.Size([64]) # 显示前16张图片 fig plt.figure(figsize(8,8)) for i in range(16): plt.subplot(4,4,i1) plt.imshow(images[i][0], cmapgray_r) plt.title(fLabel: {labels[i]}) plt.axis(off) plt.tight_layout() plt.show()3. 神经网络模型构建3.1 全连接网络设计我们首先实现一个简单的全连接网络import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc1 nn.Linear(28*28, 512) self.fc2 nn.Linear(512, 256) self.fc3 nn.Linear(256, 10) def forward(self, x): x x.view(-1, 28*28) # 展平图像 x F.relu(self.fc1(x)) x F.relu(self.fc2(x)) x self.fc3(x) return F.log_softmax(x, dim1)这个网络包含输入层28×28784个神经元第一个隐藏层512个神经元第二个隐藏层256个神经元输出层10个神经元对应0-9十个数字经验隐藏层神经元数量通常选择2的幂次方这样在GPU上计算效率更高。从输入层到输出层逐渐减少神经元数量形成金字塔结构是常见做法。3.2 模型初始化与GPU加速初始化模型并转移到GPU如果可用device torch.device(cuda if torch.cuda.is_available() else cpu) model Net().to(device) print(model)查看模型参数量def count_parameters(model): return sum(p.numel() for p in model.parameters() if p.requires_grad) print(f模型可训练参数数量: {count_parameters(model):,}) # 典型输出模型可训练参数数量: 535,0504. 模型训练与评估4.1 训练流程实现定义损失函数和优化器from torch.optim import Adam criterion nn.CrossEntropyLoss() optimizer Adam(model.parameters(), lr0.001)训练循环def train(model, device, train_loader, optimizer, epoch): model.train() train_loss 0 correct 0 for batch_idx, (data, target) in enumerate(train_loader): data, target data.to(device), target.to(device) optimizer.zero_grad() output model(data) loss criterion(output, target) loss.backward() optimizer.step() train_loss loss.item() pred output.argmax(dim1, keepdimTrue) correct pred.eq(target.view_as(pred)).sum().item() if batch_idx % 100 0: print(f训练进度: {batch_idx * len(data)}/{len(train_loader.dataset)} f({100. * batch_idx / len(train_loader):.0f}%)) train_loss / len(train_loader) accuracy 100. * correct / len(train_loader.dataset) print(f训练集平均损失: {train_loss:.4f}, 准确率: {accuracy:.2f}%) return train_loss, accuracy4.2 测试评估实现测试函数def test(model, device, test_loader): model.eval() test_loss 0 correct 0 with torch.no_grad(): for data, target in test_loader: data, target data.to(device), target.to(device) output model(data) test_loss criterion(output, target).item() pred output.argmax(dim1, keepdimTrue) correct pred.eq(target.view_as(pred)).sum().item() test_loss / len(test_loader) accuracy 100. * correct / len(test_loader.dataset) print(f测试集平均损失: {test_loss:.4f}, 准确率: {accuracy:.2f}%\n) return test_loss, accuracy4.3 完整训练过程运行多轮训练epochs 10 train_losses, test_losses [], [] train_accs, test_accs [], [] for epoch in range(1, epochs 1): print(fEpoch {epoch}:) train_loss, train_acc train(model, device, train_loader, optimizer, epoch) test_loss, test_acc test(model, device, test_loader) train_losses.append(train_loss) test_losses.append(test_loss) train_accs.append(train_acc) test_accs.append(test_acc)可视化训练过程plt.figure(figsize(12,5)) plt.subplot(1,2,1) plt.plot(train_losses, label训练损失) plt.plot(test_losses, label测试损失) plt.legend() plt.subplot(1,2,2) plt.plot(train_accs, label训练准确率) plt.plot(test_accs, label测试准确率) plt.legend() plt.show()5. 模型优化与改进5.1 学习率调整策略添加学习率调度器from torch.optim.lr_scheduler import StepLR optimizer Adam(model.parameters(), lr0.01) scheduler StepLR(optimizer, step_size5, gamma0.1)然后在每个epoch后调用scheduler.step() print(f当前学习率: {scheduler.get_last_lr()[0]})5.2 添加Dropout防止过拟合修改网络结构class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc1 nn.Linear(28*28, 512) self.drop1 nn.Dropout(0.2) self.fc2 nn.Linear(512, 256) self.drop2 nn.Dropout(0.2) self.fc3 nn.Linear(256, 10) def forward(self, x): x x.view(-1, 28*28) x self.drop1(F.relu(self.fc1(x))) x self.drop2(F.relu(self.fc2(x))) x self.fc3(x) return F.log_softmax(x, dim1)注意Dropout只在训练时激活测试时会自动关闭。PyTorch通过model.train()和model.eval()自动管理这种差异。5.3 卷积神经网络(CNN)改进更强大的CNN实现class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 nn.Conv2d(1, 32, 3, 1) self.conv2 nn.Conv2d(32, 64, 3, 1) self.dropout1 nn.Dropout(0.25) self.dropout2 nn.Dropout(0.5) self.fc1 nn.Linear(9216, 128) self.fc2 nn.Linear(128, 10) def forward(self, x): x self.conv1(x) x F.relu(x) x self.conv2(x) x F.relu(x) x F.max_pool2d(x, 2) x self.dropout1(x) x torch.flatten(x, 1) x self.fc1(x) x F.relu(x) x self.dropout2(x) x self.fc2(x) return F.log_softmax(x, dim1)这个CNN模型通常能达到99%以上的测试准确率显著优于全连接网络。6. 模型保存与应用6.1 保存和加载模型保存最佳模型torch.save(model.state_dict(), mnist_model.pth)加载模型model Net() # 或 CNN() model.load_state_dict(torch.load(mnist_model.pth)) model.eval()6.2 单张图片预测实现预测函数def predict_image(img_path, model, device): img Image.open(img_path).convert(L) transform transforms.Compose([ transforms.Resize((28,28)), transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) img_tensor transform(img).unsqueeze(0).to(device) with torch.no_grad(): output model(img_tensor) pred output.argmax(dim1).item() plt.imshow(img, cmapgray_r) plt.title(f预测结果: {pred}) plt.axis(off) return pred使用示例pred predict_image(my_digit.png, model, device)7. 常见问题与解决方案7.1 训练不收敛的可能原因学习率设置不当症状损失值波动大或几乎不变解决方案尝试0.1, 0.01, 0.001等不同学习率数据未正确归一化症状损失下降非常缓慢检查确认输入数据在-1到1或0到1范围内模型结构问题症状无论怎样调整参数准确率都很低解决方案简化网络结构或增加层数7.2 过拟合处理技巧增加Dropout层在隐藏层后添加Dropout(0.2~0.5)使用L2正则化optimizer Adam(model.parameters(), lr0.001, weight_decay1e-4)数据增强transform transforms.Compose([ transforms.RandomRotation(10), transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ])7.3 GPU内存不足问题减小batch size从64降到32或16使用梯度累积accumulation_steps 4 for batch_idx, (data, target) in enumerate(train_loader): ... loss loss / accumulation_steps loss.backward() if (batch_idx 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()混合精度训练scaler torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): output model(data) loss criterion(output, target) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()8. 项目扩展方向实现更先进的网络结构尝试ResNet、EfficientNet等现代架构实现注意力机制或Transformer结构开发Web应用接口使用Flask或FastAPI创建预测API构建交互式手写画板迁移学习应用在MNIST预训练模型上微调其他数字数据集实现少样本学习(Few-shot Learning)模型量化与部署quantized_model torch.quantization.quantize_dynamic( model, {nn.Linear}, dtypetorch.qint8 )可视化工具集成使用TensorBoard记录训练过程实现激活热力图可视化在实际教学中我发现很多学员容易陷入调参陷阱——不断调整超参数却忽视了对数据和模型结构的深入理解。建议新手先固定一组参数如batch_size64, lr0.001完整跑完训练流程建立端到端的认知后再进行优化。记住好的模型合适的数据恰当的结构合理的训练三者缺一不可。

相关新闻

Three.js 蒸汽粒子教程

Three.js 蒸汽粒子教程

2026/7/8 3:00:11

蒸汽粒子 Steam Particle ▶ 在线运行案例 案例合集: 三维可视化功能案例(threehub.cn)开源仓库github地址: https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 ShaderMaterial 自…

如何从‘能聊天’升级到‘让别人愿意主动找你聊’的系统?

如何从‘能聊天’升级到‘让别人愿意主动找你聊’的系统?

2026/7/7 19:19:25

一、第一刀:为什么大多数人只能“能聊天”,不能“被找聊”? 因为他们停留在:被动对话系统✔ 特征: 别人发起你回应你维持但不会“积累吸引力”👉 本质:只是“对话节点”,不是“对话源…

高效电机驱动方案:TC78H660FTG与STM32G071RB实战解析

高效电机驱动方案:TC78H660FTG与STM32G071RB实战解析

2026/7/7 11:49:02

1. 项目背景与核心器件选型在工业自动化和消费电子领域,电机驱动系统的效率提升一直是工程师们关注的焦点。最近我在一个AGV小车项目中,需要设计一套兼顾性能和功耗的驱动方案,最终选择了东芝的TC78H660FTG电机驱动芯片搭配ST的STM32G071RB微…

bgp基础实验

bgp基础实验

2026/7/8 2:57:21

BGP基础实验实验拓扑及要求:IP配置:R1:interface GigabitEthernet0/0/0ip address 100.1.12.1 255.255.255.0interface GigabitEthernet0/0/1ip address 100.1.13.1 255.255.255.0interface LoopBack0ip address 1.1.1.1 255.255.255.255inte…

2026年国家级制造业单项冠军申报全攻略!

2026年国家级制造业单项冠军申报全攻略!

2026/7/8 2:57:21

近期多地制造企业咨询2026国家级制造业单项冠军申报事宜,行业普遍预判8月底工信部将启动新一轮遴选。作为制造业资质金字塔顶端荣誉,单项冠军评审标准逐年收紧,不少企业卡在硬指标、材料佐证、省级推荐环节遗憾落选。本文结合工信部《制造业单…

APK Editor Studio:当传统逆向工具无法满足时,如何构建跨平台的Android应用分析工作流

APK Editor Studio:当传统逆向工具无法满足时,如何构建跨平台的Android应用分析工作流

2026/7/8 2:57:21

APK Editor Studio:当传统逆向工具无法满足时,如何构建跨平台的Android应用分析工作流 【免费下载链接】apk-editor-studio Powerful yet easy to use APK editor for PC and Mac. 项目地址: https://gitcode.com/gh_mirrors/ap/apk-editor-studio …

vibe coding 整体流程

vibe coding 整体流程

2026/7/8 2:57:21

你要的不是“工具组合”,而是开发操作系统。 结论先说:Obsidian 管知识,Harness 管约束,Trae 管执行,Git/CI 管交付。 你现在最容易犯的错误是:以为把几个 AI 工具串起来,就能自动完成开发。错…

企业做 AI 落地时,为什么越来越需要成本意识

企业做 AI 落地时,为什么越来越需要成本意识

2026/7/8 2:57:21

前段时间,很多团队谈 AI,重点还是“能不能做”“要不要接”“哪个模型更强”。但到了现在,越来越多企业开始把问题换了一种问法:这件事做下来,到底值不值。这个变化很重要,因为它说明 AI 在很多公司里&…

APK-Installer:在Windows上无缝安装Android应用的终极解决方案

APK-Installer:在Windows上无缝安装Android应用的终极解决方案

2026/7/8 2:47:21

APK-Installer:在Windows上无缝安装Android应用的终极解决方案 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否曾经想在Windows电脑上直接安装Androi…

解锁AMD Ryzen处理器深层性能:SMU Debug Tool完全指南

解锁AMD Ryzen处理器深层性能:SMU Debug Tool完全指南

2026/7/8 0:44:47

解锁AMD Ryzen处理器深层性能:SMU Debug Tool完全指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gi…

6个月转型AI工程师:实战路径与核心技能

6个月转型AI工程师:实战路径与核心技能

2026/7/7 4:34:17

1. 项目概述:6个月转型AI工程师的可行性路径在2023年大模型技术爆发的背景下,AI工程师岗位需求同比增长217%(LinkedIn数据)。不同于传统算法工程师需要3-5年培养周期,现代AI工程师更侧重工程化落地能力。我在硅谷科技公…

YOLOv5模型剪枝与量化实战:边缘设备部署优化

YOLOv5模型剪枝与量化实战:边缘设备部署优化

2026/7/7 13:20:59

1. 项目背景与核心价值在计算机视觉领域,YOLOv5因其出色的实时检测性能成为工业界宠儿。但当我们尝试将其部署到边缘设备(如树莓派、Jetson Nano或手机终端)时,立刻会遇到两个致命问题:模型体积庞大(原始YO…

Qwen Code+Obsidian构建可执行的AI时代认知操作系统

Qwen Code+Obsidian构建可执行的AI时代认知操作系统

2026/7/8 0:07:15

1. 项目概述:这不是又一个笔记软件教程,而是一套可复用的“认知操作系统”搭建逻辑你有没有试过,在读完Andrej Karpathy那篇著名的《Software 2.0》之后,满脑子都是“神经网络即代码”的震撼,但合上网页,打…

校园服饰细分赛道测算程序,学生平价国风,机能穿搭市场规模预估。

校园服饰细分赛道测算程序,学生平价国风,机能穿搭市场规模预估。

2026/7/8 0:07:15

校园服饰细分赛道测算程序(Python)——学生平价国风 & 机能穿搭市场规模预估(TAM / SAM / SOM)一、实际应用场景描述(真实业务抽象)在《时尚产业与品牌创新》课程中,细分市场切入&#xff0…

Visual C++运行库完整解决方案:告别软件兼容性问题

Visual C++运行库完整解决方案:告别软件兼容性问题

2026/7/8 0:07:15

Visual C运行库完整解决方案:告别软件兼容性问题 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾为打开游戏时弹出的"DLL缺失"错误…