当前位置: 首页 > news >正文

P1周:Minist手写识别

P1周:Minist手写识别

文章目录

  • P1周:Minist手写识别
  • 目标
  • 一、前期准备
    • 1.引入库及前期设置
    • 2.导入数据
    • 3.数据可视化
  • 二、CNN网络构建
  • 三、训练模型
    • 1.设置超参数
    • 2.编写训练函数
    • 3.编写测试函数
  • 四、正式训练
    • 1.训练模型
    • 2.可视化图表
    • 3.模型测试
  • 问题总结
      • 为什么只选中部分代码执行到控制台,不在选中代码中的变量也都能用?
      • 相对路径的工作目录问题,数据集下载的相对路径与想象的相对路径总是不同
      • 数据集维度问题总结
  • 整体工作流程总结
    • 1. 环境与数据准备
    • 2. 模型构建
    • 3. 训练准备
    • 4. 模型训练与评估
    • 5. 模型测试与可视化

目标

  • 了解Pytorch
  • 手敲简易的深度学习代码
  • 弄清函数方法

一、前期准备

1.引入库及前期设置

importtorchimporttorch.nnasnnimportmatplotlib.pyplotaspltimporttorchvisionimportnumpyasnpfromdatetimeimportdatetime plt.rcParams['font.sans-serif']=['SimHei']# 用来正常显示中文标签plt.rcParams['axes.unicode_minus']=False# 用来正常显示负号plt.rcParams['figure.dpi']=100#分辨率MODE="train"device=torch.device("cuda"iftorch.cuda.is_available()else"cpu")print(device)

代码输出:

D:\conda\envs\yolo_env\python.exe D:\deeplearning\minist\main.py cuda

2.导入数据

##下载数据集##root是保存地址,torchvision.transforms是pytorch的图像处理箱##Tensor为张量,一个数组,格式为(通道C × 高H × 宽W),#正常图片为((高H × 宽W × 通道C),img[0,2,3]表示第二行第三列为黑色,##表层的通道就是不同颜色,img[1, 2, 3] = 0.3←这像素的绿色强度train_ds=torchvision.datasets.MNIST(root='D:/deeplearning/minist/data',train=True,download=True,transform=torchvision.transforms.ToTensor())test_ds=torchvision.datasets.MNIST(root='D:/deeplearning/minist/data',train=False,download=True,transform=torchvision.transforms.ToTensor())##引入数据加载器,用于对数据集进行抽取一批次一批次的样本##形参说明:dataset为样本从哪个数据集抽取,batch_size为每批次抽取数量,sampler为高级抽法# shuffle为打乱抽取,num_workers在windows取0,drop_last为最后一批不足时,是否丢弃batchsize=32train_dl=torch.utils.data.DataLoader(dataset=train_ds,batch_size=batchsize,shuffle=True,num_workers=0,drop_last=False)test_dl=torch.utils.data.DataLoader(dataset=test_ds,batch_size=batchsize)# #把train_dl变成迭代器 ,imgs.shape即张量形状[32,1,28,28]imgs,labels=next(iter(train_dl))

3.数据可视化

#matplotlib.pyplot as plt ,创建画布,后面plt.imshow的内容会呈现在画布上ifMODE=="train":plt.figure(figsize=(20,5))fori,imgsinenumerate(imgs[:20]):##张量形状[20,1,28,28] 单张[1,28,28]npimg=np.squeeze(imgs.numpy())##维度缩减 [1,28,28]→ [28,28],把pt张量变为numpy数组,移除为1的维度plt.subplot(2,10,i+1)##行2*列10的网格,激活第i+1个格子(从1开始,从左往右,从上往下)plt.imshow(npimg,cmap=plt.cm.binary)##plt.imshow(data,cmap)cmap是颜色规则plt.axis('off')##隐藏坐标轴plt.show()##用于展示画布

二、CNN网络构建

importtorch.nn.functionalasF##函数APInum_class=10classModel(nn.Module):##继承自父类 nn.Moduledef__init__(self):## 构造函数super().__init__()## 调用父类的构造函数#特征提取网络 ##参数计算通用公式:输出尺寸 = 输入尺寸 - 卷积核尺寸 + 1self.conv1=nn.Conv2d(1,32,kernel_size=3)#输入1通道,输出32个特征图 [1,28,28]→[32,26,26] 26=28-3+1self.pool1=nn.MaxPool2d(2)#2*2窗口,尺寸减半 [32,26,26]→[32,13,13] 13=26/2self.conv2=nn.Conv2d(32,64,kernel_size=3)#[32,13,13]→[64,11,11] 13-3+1=11self.pool2=nn.MaxPool2d(2)##[64,11,11]→[64,5,5] 11//2=5#分类网络self.fc1=nn.Linear(1600,64)##1600个输入特征数怎么来的:64*5*5self.fc2=nn.Linear(64,num_class)defforward(self,x):x=self.pool1(F.relu(self.conv1(x)))x=self.pool2(F.relu(self.conv2(x)))x=torch.flatten(x,start_dim=1)##torch.flatten(x,start_dim=1):# start_dim=1 表示从第1维开始压平。# 把64个5*5的列表一共1600个输入特征拉成一个1600维的向量,就像把一个表格的每个格子里的数字抄成一长行# 第 0 维是 batch(批次),必须保留:x=F.relu(self.fc1(x))x=self.fc2(x)returnx##看清模型架构,每层的参数 conv2d 1-1: 1*3*3*32=288 288+32=320#conv2d 1-3: 32*3*3*64=18432 18432+64=18496# from torchinfo import summarymodel=Model().to(device)##实例化模型并且搬到gpu##summary(model)

三、训练模型

1.设置超参数

loss_fn=nn.CrossEntropyLoss()##交叉熵损失函数learning_rate=0.001opt=torch.optim.SGD(model.parameters(),lr=learning_rate)## optimizer(优化器)创建一个SGD优化器(随机梯度下降),## model.parameters() — 告诉优化器要去更新模型里的哪些参数

2.编写训练函数

deftrain(dataloader,model,loss_fn,optimizer):size=len(dataloader.dataset)num_batches=len(dataloader)train_loss,train_acc=0,0forX,yindataloader:X,y=X.to(device),y.to(device)pred=model(X)#网络输出# pred 的形状是 [32, 10],每列代表的是一种数字的概率,每行代表的是一张图片它的数字概率分布###前面 model=Model().to(device),model是Model的对象,Model继承了父类Module.nn ,# Module.nn包含了.__call__()方法,所以model(X)==model.forward(X)loss=loss_fn(pred,y)#计算损失一张图的概率[2.0,1.0,0.1],softmax,[0.659,0.242,0.099],比如第一列为正确标签,# 将第一列作负对数,-log(0.659)=0.417 以此得到32张图的负对数,相加/32=loss#也就是说交叉熵只关心正确类别的概率,希望越大越好#必要步骤optimizer.zero_grad()##梯度清零loss.backward()##反向传播##链式法则:y=wx x=3 y_true=6 y_pred=w*x=1*x=3 损失函数=(y_pred-y_true)^2=9#我们想知道w变化,loss如何变化,# 即d(loss)/d(w),d(loss)/d(y_pred)*d(y_pred)/d(w)=-6*3=-18(-18即为梯度),,optimizer.step()##自动更新 # 新w=旧w-lr*梯度=1.18 故y_pred=1.18*3=3.54train_acc+=(pred.argmax(1)==y).type(torch.float).sum().item()## pred.argmax(1) 对每行,找出最大概率在第几列,返回列的索引##假设这批 32 张图中,y 的真实标签和 pred 的预测结果如下:#真实标签 y: [3, 7, 1, 0, 9, ...] (32个)#pred.argmax(1): [3, 7, 0, 0, 9, ...] (32个,第3个预测错了)# == 比较: [T, T, F, T, T, ...] (31个True, 1个False)# .float(): [1.0, 1.0, 0.0, 1.0, 1.0, ...]#.sum(): [31.0] (这张卡32张图,对了31张)#.item(): 31 标量值train_loss+=loss.item()##.item()把标量张量的数值提取出来train_acc/=size#一个epoch的准确率train_loss/=num_batches#一个epoch的损失率returntrain_loss,train_acc

3.编写测试函数

##编写测试函数deftest(dataloader,model,loss_fn):##测试函数只用于测试,不反向传播,所以不用优化器size=len(dataloader.dataset)num_batches=len(dataloader)test_loss,test_acc=0,0withtorch.no_grad():forX,yindataloader:X,y=X.to(device),y.to(device)#计算损失target_pred=model(X)loss=loss_fn(target_pred,y)test_loss+=loss.item()test_acc+=(target_pred.argmax(1)==y).type(torch.float).sum().item()test_acc/=size test_loss/=num_batchesreturntest_loss,test_acc

四、正式训练

1.训练模型

##正式训练ifMODE=="train":epochs=20train_loss,train_acc,test_loss,test_acc=[],[],[],[]forepochinrange(epochs):model.train()##测试模式,模型Dropout启用,BatchNorm 层:使用当前 batch 的均值和方差进行标准化,并更新其内部的运行均值和方差epoch_train_loss,epoch_train_acc=train(train_dl,model,loss_fn,opt)train_loss.append(epoch_train_loss)train_acc.append(epoch_train_acc)model.eval()##评估模式,Dropout 层:关闭(不再随机丢弃神经元) BatchNorm 层:使用训练时记录的均值和方差,不再更新epoch_test_loss,epoch_test_acc=test(test_dl,model,loss_fn)test_loss.append(epoch_test_loss)test_acc.append(epoch_test_acc)template=('Epoch:{:2d},Train_acc{:.1f}%,Train_loss:{:.3f},Test_acc{:.1f}%,Test_loss:{:.3f}')print(template.format(epoch+1,train_acc[-1]*100,train_loss[-1],test_acc[-1]*100,test_loss[-1]))print("Done")torch.save(model.state_dict(),"D:/deeplearning/minist/model.pth")###保存训练好的权重参数

2.可视化图表

###############可视化############current_time=datetime.now()# 获取当前时间epochs_range=range(epochs)plt.figure(figsize=(12,3))plt.subplot(1,2,1)plt.plot(epochs_range,train_acc,label='Training Accuracy')plt.plot(epochs_range,test_acc,label='Test Accuracy')plt.legend(loc='lower right')##plt.title('Training and Validation Accuracy')plt.xlabel(current_time)# 时间戳plt.subplot(1,2,2)plt.plot(epochs_range,train_loss,label='Training Loss')plt.plot(epochs_range,test_loss,label='Test Loss')plt.legend(loc='upper right')plt.title('Training and Validation Loss')plt.show()###########可视化###############

3.模型测试

elifMODE=="test":# ===== 测试模式:加载权重 → 评估 → 可视化 =====model.load_state_dict(torch.load("D:/deeplearning/minist/model.pth"))model.eval()# 可视化 10 张图(和 use.py 一样)plt.figure(figsize=(15,5))foriinrange(10):img,label=test_ds[i]pred_label=model(img.unsqueeze(0).to(device)).argmax(1).item()plt.subplot(2,5,i+1)plt.imshow(img.squeeze(),cmap="gray")color="green"ifpred_label==labelelse"red"plt.title(f"truth:{label}predict:{pred_label}",color=color)plt.axis("off")plt.show()

问题总结

为什么只选中部分代码执行到控制台,不在选中代码中的变量也都能用?

原因:之前已经完整运行过整个 .py 文件,所有变量早已驻留在控制台内存中

  • 关键:Python 控制台维护持久化内存命名空间,执行选区只是新增/覆盖变量,不会清除已有变量
    验证:重启控制台后只执行前文代码,后文的变量就会报NameError

相对路径的工作目录问题,数据集下载的相对路径与想象的相对路径总是不同

原因:./data 不是相对于 main.py 的位置,而是相对于Python 进程的当前工作目录

  • 验证import os; print(os.getcwd())
  • 修复:改成绝对路径

数据集维度问题总结

位置变量/操作形状 (Shape)含义
原始 MNIST 图片PIL Image[28, 28]宽 28 × 高 28 的二维灰度矩阵。
ToTensor()转换后test_ds[i][0][1, 28, 28]ToTensor()自动添加了通道维度 (Channel),变为三维张量:[通道, 高, 宽]。MNIST 是单通道灰度图,所以通道数为 1。
DataLoader取一批数据X(在for X, y in dataloader中)[32, 1, 28, 28]DataLoader自动堆叠了batch_size(此处为 32) 个样本,添加了批次维度 (Batch),变为四维张量:[批次大小, 通道, 高, 宽]
单张图片喂入模型img.unsqueeze(0)[1, 1, 28, 28]模型通常要求输入包含批次维度。unsqueeze(0)dim=0位置手动添加一个维度,模拟一个批次大小为 1 的输入。

核心要点

  1. ToTensor():负责[H, W][C, H, W]的转换,并归一化像素值到[0, 1]
  2. DataLoader:负责[C, H, W][B, C, H, W]的转换,即组织成批次。
  3. unsqueeze(0):当需要将单张图片输入模型进行预测时,必须手动补上批次维度B,否则会因维度不匹配而报错。

整体工作流程总结

本 MNIST 手写识别项目的完整流程可概括为:首先进行环境与数据准备,包括导入必要的 PyTorch 库、设置计算设备、下载 MNIST 数据集并通过ToTensor()DataLoader进行预处理与批次加载;接着构建一个包含卷积层、池化层和全连接层的 CNN 模型;然后定义交叉熵损失函数和 SGD 优化器,并编写训练与测试函数;随后进入训练循环,在多个 epoch 中交替进行训练(前向传播、损失计算、反向传播、参数更新)和评估,同时保存训练好的模型权重;最后,在测试模式下加载模型对测试集样本进行预测,并通过可视化图表展示预测结果与训练过程曲线,从而完成从数据到模型训练再到评估的端到端深度学习流程。

1. 环境与数据准备

  • 引入库:导入torchtorch.nntorchvisionmatplotlib等核心库。
  • 设置设备:检测并指定使用 GPU (cuda) 或 CPU 进行计算。
  • 下载与加载数据
    1. 使用torchvision.datasets.MNIST下载 MNIST 训练集和测试集。
    2. 通过torchvision.transforms.ToTensor()将图片数据转换为 PyTorch 张量,并自动归一化像素值到 [0, 1] 区间。
    3. 使用torch.utils.data.DataLoader将数据集封装成可按批次 (batch_size) 迭代的数据加载器,便于训练时高效读取。

2. 模型构建

  • 定义网络结构:创建一个继承自nn.ModuleModel类。
  • 特征提取部分:使用两个Conv2d(卷积层)和MaxPool2d(最大池化层)的组合,逐步提取图像特征并降低空间维度。
  • 分类部分:使用Linear(全连接层)将提取的特征映射到 10 个类别(数字 0-9)的输出。
  • 实例化模型:创建Model实例,并通过.to(device)将其移动到指定的计算设备上。

3. 训练准备

  • 定义损失函数:使用nn.CrossEntropyLoss()(交叉熵损失函数)来衡量模型预测与真实标签之间的差距。
  • 定义优化器:使用torch.optim.SGD(随机梯度下降优化器)来根据损失函数的梯度更新模型的参数。
  • 编写训练/测试函数
    • train函数:在一个 epoch 内,遍历训练数据加载器,完成前向传播、损失计算、反向传播和参数更新,并计算该 epoch 的平均训练损失和准确率。
    • test函数:在一个 epoch 内,遍历测试数据加载器,完成前向传播和损失计算(不更新参数),并计算该 epoch 的平均测试损失和准确率。

4. 模型训练与评估

  • 循环训练:设置训练轮数 (epochs),在每一轮中:
    1. 调用model.train()将模型设置为训练模式。
    2. 调用train函数在训练集上进行一次完整的训练,得到本轮训练指标。
    3. 调用model.eval()将模型设置为评估模式。
    4. 调用test函数在测试集上进行一次完整的评估,得到本轮测试指标。
    5. 打印并记录本轮训练和测试的损失与准确率。
  • 保存模型:训练完成后,使用torch.save将训练好的模型权重(state_dict)保存到本地文件(如model.pth)。

5. 模型测试与可视化

  • 加载与测试:在测试模式下 (MODE=="test"):
    1. 使用torch.load加载之前保存的模型权重。
    2. 从测试集中抽取若干样本,通过模型进行预测。
    3. 将预测结果与真实标签对比,并使用matplotlib可视化展示,绿色标题表示预测正确,红色表示错误。
  • 训练过程可视化:在训练模式下 (MODE=="train"),使用matplotlib绘制训练和测试的准确率、损失曲线随 epoch 变化的图表,用于分析模型的学习情况。
http://www.jsqmd.com/news/1164702/

相关文章:

  • MCP 工具调用机制详解
  • 2026MP4转MP3,免费方法合集,电脑手机在线、离线本地工具完整指南
  • 2026 年新消息:吴桥口碑好的注塑高分散彩色母粒工厂怎么联系,明明加了色母,塑料件为什么还是泛白?这3个细节90%的人搞错 - 行业甄选官
  • 串口波特率精度优化:分数波特率发生器(FDR)原理与C语言实现(附误差<0.2%代码)
  • 2026年7月最新佛山宇舶官方售后联系电话与客户服务中心网点地址 - 亨得利官方服务中心
  • AI绘画Prompt心法:从翻车到精通的四层结构实战指南
  • 5分钟快速上手:XXMI Launcher跨平台游戏模组管理完整指南
  • PixVerse本地AI广告生成:文生图、视频创作与API集成实战
  • STM32F103C8T6 PWM调速实战:L298N驱动双电机,8档位平滑切换代码解析
  • 大数据毕设项目: 基于 Python 的广东乡村旅游数据可视化系统的设计与实现 基于 Python 的广东滨海旅游资源分析系统(源码+文档,讲解、调试运行,定制等)
  • SolidWorks_工程图设计6_断裂视图与剪裁视图
  • Hack The Box 入门密码学:ASCII 编码解题 Writeup
  • 建议收藏|盘点2026年最受喜爱的AI论文网站
  • 第 9 篇:状态机实践——状态环 + 步进表替代 if-else 地狱
  • PIC18F25K80驱动EPT-14A4005P蜂鸣器的警报系统设计
  • STM32F405ZG与MCP3551高精度ADC信号采集实战
  • LabVIEW 混合编程性能实测:C++ DLL 与 C# .NET Assembly 调用开销对比
  • 辉光管驱动电路设计:K155ID1 BCD译码与74HC595级联的3种方案
  • 2026年车间吸尘器选购指南:哪些品牌值得考虑? - 品牌排行榜
  • Fable多模态AI框架技术解析:从架构推测到部署实践
  • 2026质量好的工业洗地机性能与品牌解析 - 品牌排行榜
  • VMware-vdiskmanager 工具全解:5个核心参数搞定磁盘创建、转换与扩容
  • MHMarkets迈汇:“成本调整影响芯片周期”
  • AutoAgent:零代码构建可部署AI智能体的开源框架
  • 操作系统核心知识 · 攻防实战背诵宝典
  • 气体打火机压电陶瓷拆解实测:两段并联结构产生3000V高压的3个关键设计
  • 数据库的“隐形守护者”:深度解析Oracle后台进程及其核心功能
  • Boehm GC 终结器机制解析:对象复活与最少2轮GC释放的3种场景
  • S32K144 SPI 驱动 MCP2515 实战:3种波特率配置与中断接收代码详解
  • 2026年7月最新杭州芝柏官方售后热线及客户服务网点地址 - 亨得利官方服务中心