pytorch 实战二 CNN手写数字识别

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、torchvision.datasets
    • 1. 数据下载
    • 2. 数据分批次传入
  • 二、网络
    • 1. 网络搭建
    • 2. 训练
    • 3.测试
  • 完整代码
  • 三、保存模型与推理(inference)
    • 模型保存
    • 推理
    • 鸣谢


前言

  手写数字识别,就是要根据手写的数字0~9,然后我们搭建网络,进行智能识别。说句实话,中草药识别啊,等等。都可以用在本科的毕业设计论文中,妥妥的加分项,而且还不难。最离谱的是我有一个研究生的哥们儿,也用这个当做毕业设计论文,准确率高达99%。笑死我了,哈哈哈,我只能说,水深啊
   在实战一中,我们已经详细讲解了 torch 完成一个神经网络搭建的过程,在以后我将直接给出代码,讲解额外的库函数。

一、torchvision.datasets

1. 数据下载

  本文使用的手写数字的识别就在 torchvision.datasets 中,我们可以直接调用。在手写数据中,有 6 w张训练集和 1 w 张测试集。
dowload = True, 只能运行一次,之后改成False, transform.Tensor ,就是把数据转化成我们可以使用的 Tensor 数据类型。
代码加载数据:

# data
train_data = datasets.MNIST(root='./datasets',train = True,transform = transforms.ToTensor(),download=True)

但是这个下载可能有点慢?这里给出一个百度链接:

https://pan.baidu.com/s/1ia3vFA73hEtWK9qU-O-4iQ?pwd=mnis

2. 数据分批次传入

  在第一个章节中,我们把数据一次加载到训练集和测试集中,但是深度学习的照片可能很大,我们这里要用一种新的方法:

# batchSize
train_loader = data_util.DataLoader(dataset=train_data,batch_size=64,shuffle=True)test_loader = data_util.DataLoader(dataset=test_data,batch_size=64,shuffle=True)

dataset = train_data 这个参数是传入要分割的数据集,batch_size 分块的批次,shuffle 数据是否需要打乱,那必须的啊。所以把这个当做模版来,用的时候就 CV。

二、网络

1. 网络搭建

  在实战一中,我们是直接定义算子,然后组合在一起。在实战二中,我们玩点不一样的,使用序列定义算子。

class CNN(torch.nn.Module):def __init__(self):super(CNN, self).__init__()# 定义第一个卷积操作self.conv = torch.nn.Sequential(# 参数的具体含义我们在卷积神经网络中介绍torch.nn.Conv2d(1, 32, kernel_size=(5,5),padding = 2),# 第一个参数是灰度,第二个参数是输出通道,第三个参数是卷积核torch.nn.BatchNorm2d(32),torch.nn.ReLU(),torch.nn.MaxPool2d(2))self.fc = torch.nn.Linear(14*14*32,10) # 第一个是特征图整体的数量,第二个是Y的种类def forward(self, x):out = self.conv(x) # 第一个卷积out = out.view(out.size()[0],-1) # 拉成一个一维的向量out = self.fc(out) # 线性运算return out # 返回最后的输出
cnn = CNN()

  class 用于构建我们的网络,第一个函数除了继承父类的初始化操作,还要定义我们的算子。卷积操作,用一个序列torch.nn.Sequential,在里面定义了一个 2d 卷积、BatchNorm等等,这些参数的配置我们在卷积神经网络会细讲。此处我们需要了解编程的结构。网络中定义了一个前向传播算子,经过卷积,拉直,输出的线性化。

2. 训练

cnn = cnn.cuda()
# loss
loss_fn = torch.nn.CrossEntropyLoss() # 交叉熵的损失函数# optimizer
optimizer = torch.optim.Adam(cnn.parameters(),lr=0.01)# 采用Adam 对学习率不是特别敏感
# training
for epoch in range(10):# 这里我们把整个数据集过 10 遍for i, (images, labels) in enumerate(train_loader):images = images.cuda() # 图片放到cuda上面labels = labels.cuda()# 定义输出结构outputs = cnn(images)loss = loss_fn(outputs,labels) # loss 传入输出的结果optimizer.zero_grad() # 梯度优化loss.backward() # 反向传播optimizer.step()print("Epoch is:{} item is :{}/{} Loss is :{}".format(epoch+1,i,len(train_data)//64,loss.item()))

把网络加载到cuda上面,使用显卡跑数据。损失函数采用交叉熵,优化函数采用Adam,这可以减小学习率的影响。然后训练10个轮次,每个轮次还有64个区块,我们都要训练完全。训练过程每个batch:

  1. 把数据加载到 cuda 上面
  2. 网络上跑一边获得第一个输出,输出在net中已经处理了。
  3. 计算损失函数
  4. 梯度优化器
  5. 损失函数反向传播
  6. 优化器保存参数

图片:

图 1 训练结果
我们从训练结果可以看出,epoch 有10轮,其中有937次区块训练。损失函数万分之二,非常好了。

3.测试

  测试我们在每个epoch测试一次(每轮测试),注意代码的位置。用测试集来跑模型,然后计算出准确率。

# testloss_test = 0accuracy = 0for i, (images, labels) in enumerate(test_loader):images = images.cuda()  # 图片放到cuda上面labels = labels.cuda()# 定义输出结构outputs = cnn(images)# 我们使用交叉熵计算,label 的维度是[batchsize],每个样本的标签样本是一个值# outputs = batches * cls_num  cls_num 概率分布也就是种类10 个loss_test += loss_fn(outputs, labels)  # loss 传入输出的结果,对text_loss 进行一个累加,最后求平均值_, predicted = outputs.max(dim=1)   # 获得最大的概率作为我们的预测值accuracy += (predicted ==labels).sum().item() # 我们通过相同的样本进行统计求和,通过item获取这个值accuracy = accuracy/len(test_data)loss_test=loss_test/(len(test_data)//64) # 除以batch的个数print("测试:\nEpoch is:{} accuracy is :{} loss_test is :{}".format(epoch+1,accuracy,loss_test.item()))

  测试我们定义在每轮测试一次。先定义损失函数的值和准确率为零,我们把他们的每个batch求和,然后求得平均值,这是我们测试经常做的。注意预测, _, predicted = outputs.max(dim=1) 我们取得最大的预测结果,_ 表示忽略索引,只保留预测的标签。accuracy += (predicted ==labels).sum().item() 把预测值和真实值进行比较,再求和,再取得数据,加到 accuracy (每个batch的轮次)。.item() 将单元素张量转换为Python标量,有利于计算。最后计算求平均值,准确率除以整个测试集,而loss_test 除以batch的数量,因为损失函数这一个batch只有一个,准确率统计了这个batch的所有正确样本。

图片:

图 2 测试

  从数据来看,准确率 98%,loss 也在0.06 左右,基本是个好模型了。

完整代码

记得修改数据集的路径:

import torch
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import torch.utils.data as data_util
# data
train_data = datasets.MNIST(root='datasets',train = True,transform = transforms.ToTensor(),download=True)
test_data = datasets.MNIST(root='datasets',train = False,transform = transforms.ToTensor(),download=False)# batchSize
train_loader = data_util.DataLoader(dataset=train_data,batch_size=64,shuffle=True)test_loader = data_util.DataLoader(dataset=test_data,batch_size=64,shuffle=True)
# net
class CNN(torch.nn.Module):def __init__(self):super(CNN, self).__init__()# 定义第一个卷积操作self.conv = torch.nn.Sequential(# 参数的具体含义我们在卷积神经网络中介绍torch.nn.Conv2d(1, 32, kernel_size=(5,5),padding=2),# 第一个参数是灰度,第二个参数是输出通道,第三个参数是卷积核torch.nn.BatchNorm2d(32),torch.nn.ReLU(),torch.nn.MaxPool2d(2))self.fc = torch.nn.Linear(14*14*32,10) # 第一个是特征图整体的数量,第二个是Y的种类def forward(self, x):out = self.conv(x) # 第一个卷积out = out.view(out.size()[0],-1) # 拉成一个一维的向量out = self.fc(out) # 线性运算return out # 返回最后的输出
cnn = CNN()
cnn = cnn.cuda()
# loss
loss_fn = torch.nn.CrossEntropyLoss() # 交叉熵的损失函数# optimizer
optimizer = torch.optim.Adam(cnn.parameters(),lr=0.01)# 采用Adam 对学习率不是特别敏感
# training
for epoch in range(10):# 这里我们把整个数据集过 10 遍for i, (images, labels) in enumerate(train_loader):images = images.cuda() # 图片放到cuda上面labels = labels.cuda()# 定义输出结构outputs = cnn(images)loss = loss_fn(outputs,labels) # loss 传入输出的结果optimizer.zero_grad() # 梯度优化loss.backward() # 反向传播optimizer.step()# print("训练:\nEpoch is:{}  Loss is :{}".format(epoch+1,loss.item()))
# testloss_test = 0accuracy = 0for i, (images, labels) in enumerate(test_loader):images = images.cuda()  # 图片放到cuda上面labels = labels.cuda()# 定义输出结构outputs = cnn(images)# 我们使用交叉熵计算,label 的维度是[batchsize],每个样本的标签样本是一个值# outputs = batches * cls_num  cls_num 概率分布也就是种类10 个loss_test += loss_fn(outputs, labels)  # loss 传入输出的结果,对text_loss 进行一个累加,最后求平均值_, predicted = outputs.max(dim=1)   # 获得最大的概率作为我们的预测值accuracy += (predicted ==labels).sum().item() # 我们通过相同的样本进行统计求和,通过item获取这个值accuracy = accuracy/len(test_data)loss_test=loss_test/(len(test_data)//64) # 除以batch的个数# .item() 将单元素张量转换为Python标量# print("测试:\nEpoch is:{} accuracy is :{} loss_test is :{}".format(epoch+1,accuracy,loss_test.item()))# 保存模型
torch.save(cnn,"model/cnn.pkl")

三、保存模型与推理(inference)

模型保存

# 保存模型
torch.save(cnn,"model/cnn.pkl")

推理

import torch
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import torch.utils.data as data_util# net
class CNN(torch.nn.Module):def __init__(self):super(CNN, self).__init__()# 定义第一个卷积操作self.conv = torch.nn.Sequential(# 参数的具体含义我们在卷积神经网络中介绍torch.nn.Conv2d(1, 32, kernel_size=(5,5),padding=2),# 第一个参数是灰度,第二个参数是输出通道,第三个参数是卷积核torch.nn.BatchNorm2d(32),torch.nn.ReLU(),torch.nn.MaxPool2d(2))self.fc = torch.nn.Linear(14*14*32,10) # 第一个是特征图整体的数量,第二个是Y的种类def forward(self, x):out = self.conv(x) # 第一个卷积out = out.view(out.size()[0],-1) # 拉成一个一维的向量out = self.fc(out) # 线性运算return out # 返回最后的输出test_data = datasets.MNIST(root='../datasets',train = False,transform = transforms.ToTensor(),download=False)# batchSize
test_loader = data_util.DataLoader(dataset=test_data,batch_size=64,shuffle=True)
cnn = torch.load("cnn.pkl")
cnn = cnn.cuda()
# loss
loss_fn = torch.nn.CrossEntropyLoss() # 交叉熵的损失函数# optimizer
optimizer = torch.optim.Adam(cnn.parameters(),lr=0.01)# 采用Adam 对学习率不是特别敏感
# training
for epoch in range(10):# 这里我们把整个数据集过 10 遍
# testloss_test = 0accuracy = 0for i, (images, labels) in enumerate(test_loader):images = images.cuda()  # 图片放到cuda上面labels = labels.cuda()# 定义输出结构outputs = cnn(images)# 我们使用交叉熵计算,label 的维度是[batchsize],每个样本的标签样本是一个值# outputs = batches * cls_num  cls_num 概率分布也就是种类10 个loss_test += loss_fn(outputs, labels)  # loss 传入输出的结果,对text_loss 进行一个累加,最后求平均值_, predicted = outputs.max(dim=1)   # 获得最大的概率作为我们的预测值accuracy += (predicted ==labels).sum().item() # 我们通过相同的样本进行统计求和,通过item获取这个值accuracy = accuracy/len(test_data)loss_test=loss_test/(len(test_data)//64) # 除以batch的个数# .item() 将单元素张量转换为Python标量# print("测试:\nEpoch is:{} accuracy is :{} loss_test is :{}".format(epoch+1,accuracy,loss_test.item()))
print("accuracy:",accuracy)

  模型推理的数据集一定要找准确,模型调用的路径也要准确。我们可以直接复制代码到新建的推理 python 文件中:

  1. 删除所有与训练有关的数据
  2. 删除模型的保存部分
    3.模型初始化改成调用:cnn = torch.load("cnn.pkl")
    4.修改打印参数

运行结果:

图 3 模型推理结果

鸣谢

  为了感谢我弟对我的照顾,我也没有什么可以报答的,如果这篇文章有助于大家搭建一个神经网络,有助于大家的pytorch编程,还请大家来我弟的小店逛一逛,增加一点访问量,小店才开也不容易,点击一下这里 ,谢谢大家~。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/84980.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Godot] C#读取CSV表格创建双层字典实现本地化

最近研究了一下本地化,给大家用简单易懂的方式说明我是怎么实现的,使用CSV表格填写翻译,然后在Godot中读取为字典 表格填写 首先,我们表格可以按照下面这种格式填写 idzhenjaruesdefrapple苹果appleリンゴяблокоmanzanaA…

Spark 之 Subquery

各类 Subquery src/main/scala/org/apache/spark/sql/catalyst/expressions/predicates.scala /*** Evaluates to `true` if `values` are returned in `query`s result set.*/ case class InSubquery(values: Seq[Expression], query: ListQuery)extends Predicate with Une…

3.1.3_栈的链式存储实现

知识总览: 链栈定义: 头插法建立单链表: 每次要插入一个元素的时候,总是把该元素插在头节点之后的位置,如果规定只能在单链表的链头一端进行操作即为进栈操作 每次删除一个元素的时候,规定只能在单链表…

华为OD机试_2025 B卷_字符串重新排列(Python,100分)(附详细解题思路)

题目描述 给定一个字符串s,s包括以空格分隔的若干个单词,请对s进行如下处理后输出: 1、单词内部调整:对每个单词字母重新按字典序排序 2、单词间顺序调整: 1)统计每个单词出现的次数,并按次数降…

http的缓存问题

一句话概括:浏览器请求资源的时候,会首先检查本地是否有缓存,减少向服务器请求的次数 一、缓存类型: 1. 强缓存(本地缓存):直接读本地,不发请求 控制方式: ① Cache-C…

【网络安全】SRC漏洞挖掘思路/手法分享

文章目录 Tip1Tip2Tip3Tip4Tip5Tip6Tip7Tip8Tip9Tip10Tip11Tip12Tip13Tip14Tip15Tip16Tip17Tip18Tip19Tip20Tip21Tip22Tip23Tip24Tip25Tip26Tip27Tip28Tip29Tip30Tip1 “复制该主机所有 URL”:包含该主机上的所有接口等资源。 “复制此主机里的链接”:包括该主机加载的第三…

「Linux中Shell命令」Shell常见命令

知识点及案例解析 1. who 命令 功能:显示当前登录系统的用户信息,包括用户名、终端、登录时间、IP等。 案例: who输出示例: root tty1 2025-06-13 19:42 root pts/0 2025-06-13 19:45 (192.168.226.1)解析: 显示两个用户登录信息: 第一列(用…

StampedLock入门教程

文章目录 一、理解“戳” (Stamp)二、为什么 StampedLock 能提高读性能?秘密在于“乐观读”StampedLock性能对比性能对比结果图 总结 StampedLock完整演示代码对代码的疑问之处问题一:为什么 demonstrateOptimisticReadFailure 中写线程能修改成功&#…

基于云计算的振动弦分析:谐波可视化与波动方程参数理解-AI云计算数值分析和代码验证

振动弦方程是一个基础的偏微分方程,它描述了弹性弦的横向振动。其应用范围广泛,不仅可用于模拟乐器和一般的波动现象,更是数学物理以及深奥的弦理论中的重要基石。 ☁️AI云计算数值分析和代码验证 振动弦方程是描述固定两端弹性弦横向振动的…

Qt .pro配置gcc相关命令(三):-W1、-L、-rpath和-rpath-link

目录 1.Linux 动态库相关知识 1.1.动态库查找路径 1.2.查看程序依赖的动态库 1.3.修改动态库查找路径的方法 1.4.动态链接器缓存管理 2.-Wl参数 3.-L选项(编译时路径) 4.-rpath参数(运行时路径) 5.-rpath-link 参数 6.常见问题与解决方案 7.总…

Hoppscotch

官方地址 xixiaxiazxiaxix下载 • Hoppscotch Hoppscotch 是一款轻量级、基于 Web 的 API 开发套件,其核心功能和特点如下: 核心功能3 交互式 API 测试:允许用户实时发送请求并查看响应,方便记录 API 行为,在记录响…

RabbitMQ 知识详解(Java版)

RabbitMQ 知识详解(Java版) RabbitMQ 是一个开源的消息代理,实现了高级消息队列协议(AMQP)。它用于在分布式系统中实现应用解耦、异步通信和流量削峰。 核心概念 生产者(Producer):发送消息的应用消费者(…

Flink task、Operator 和 UDF 之间的关系

要真正驾驭 Flink 并构建出高效、稳定、可扩展的流处理应用,仅仅停留在 API 的表面使用是远远不够的。深入理解其内部的运行机制,洞悉数据从代码到分布式执行的完整生命周期,以及明晰各个核心组件之间错综复杂而又协同工作的关系,…

Veeam Backup Replication系统的安装与使用

Veeam Backup & Replication系统安装与使用 系统简介 核心功能 备份与恢复:专注于虚拟化环境(如VMware和Hyper-V)的备份与恢复,支持物理服务器和云环境。快速恢复:提供即时恢复功能,可在几分钟内恢复…

十四、【ESP32全栈开发指南:搭建轻量级HTTP服务器】

一、HTTP协议基础 HTTP(Hyper Text Transfer Protocol)作为互联网基础协议,采用请求-响应模型工作: 1.1 HTTP请求组成 GET /uri?query1value1 HTTP/1.1 // 请求行(方法URI协议版本) Host: example…

java中LinkedList和ArrayList的区别和联系?

我们被要求解释Java中LinkedList和ArrayList的区别和联系。下面将分别从实现原理、性能特点、使用场景等方面进行详细说明,并总结它们的相同点和不同点。 # 一、联系(共同点) 1. 都实现了List接口,因此具有List接口的所有方法&…

明远智睿SD2351核心板:边缘计算时代的工业级核心引擎深度解析

在工业4.0与物联网深度融合的背景下,边缘计算设备正从单一功能模块向高集成度、智能化平台演进。明远智睿推出的SD2351核心板,凭借其异构计算架构、工业级接口资源和全栈技术生态,重新定义了边缘计算设备的性能边界。本文将从技术架构、场景适…

Flask 动态模块注册

目录 1. 项目概述2. 项目结构3. 核心组件解析3.1 动态模块注册系统 (api/__init__.py)3.2 应用程序入口 (setup_demo.py) 4. 模块开发指南4.1 标准模块 (*_app.py)4.2 SDK模块 (sdk/*.py) 5. URL路径规则6. 如何使用6.1 启动应用6.2 添加新模块 7. 工作原理 1. 项目概述 这个项…

JVM 内存、JMM内存与集群机器节点内存的联系

目录 1、JVM 内存 1.1、分配机制 1.2、jvm模型位置 1.3、字节码内存块 2、JMM内存 2.1、JMM模型 2.2、工作流程图 1、工作内存与主内存的交互 2. 多线程下的主内存与堆内存交互 2.3、 主内存与工作内存的同步方案 1、volatile 2、synchronized 3、final 3、内存使…

学习昇腾开发的第一天--环境配置

1、昇腾社区官网:昇腾社区官网-昇腾万里 让智能无所不及 2、产品-->选择开发者套件-->点击制卡工具的下载:资源-Atlas 200I DK A2-昇腾社区 3、如果制卡工具不能使用在线制卡,可以下载镜像到本地使用本地制卡:Linux系统制…