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

第五章 Python手写数字识别【CNN卷积神经网络实现】

刚看完鱼书,不得不说确实讲的通透,对我一个没系统学过深度学习的都能看得懂,里面详细说了神经网络的实现,手把手教的那种,而且也没有遇到需要解锁前置知识的情况

鱼书pdf分享链接(知乎好人)https://zhuanlan.zhihu.com/p/665578698

安装好虚拟环境(创建虚拟环境、安装好对应python版本、安装cuda版的Pytorch),建好项目,跟着视频开干,感谢大佬

【附:代码】手写数字识别python项目实战手把手教学_计算机视觉_人工智能_哔哩哔哩_bilibili

 加载数据

包引用

import torch                                #pytorch库加载
import torchvision.datasets as dataset      #数据集接口
import torchvision.transforms as transforms #数据转换
import torch.utils.data as data_utils       #数据分批

下载数据

###############################数据加载###############################
train_data = dataset.MNIST(           #训练集root = "mnist",                    #数据存放目录train = True,                      #加载训练数据transform = transforms.ToTensor(), #将数据转为张量格式download = True                    #如果数据集不存在,下载到本地
);test_data = dataset.MNIST(           #测试集root = "mnist",                    #数据存放目录train = False,                      #加载训练数据transform = transforms.ToTensor(), #将数据转为张量格式download = True                    #如果数据集不存在,下载到本地
)print(train_data)
print(test_data)

执行

数据分批

###############################数据分批###############################
train_loader = data_utils.DataLoader(dataset=train_data,        #训练集分批batch_size=64,             #一批64shuffle=True)              #打乱数据test_loader = data_utils.DataLoader(dataset=test_data,          #测试集分批batch_size=64,             #一批64shuffle=True)              #打乱数据print(train_loader)
print(test_loader)# for index,value in enumerate(train_loader):      #遍历训练集
#     print(index)
#     print(value)
#     break                                        #只打印一批for index,(images,labels) in enumerate(train_loader):      #遍历训练集【分开图片与标签】print(index)                                    #索引(批号)print(images)                                   #图像print(labels)                                   #真实标签break                                           #只打印一批

执行

网络结构设计

创建model文件CNN

利用Pytorch库可以很容易构建起我们的model

import torchclass CNN(torch.nn.Module):                 #继承torch.nn.Module这个基类,我们就可以直接使用人家做好的函数和属性def __init__(self):                      #构造函数super(CNN,self).__init__()          #继承自己的父类self.conv = torch.nn.Sequential(    #Sequential是序列容器,用来实现前向传播#1、卷积操作:卷积层torch.nn.Conv2d(1,                #通道数32,              #卷积核数量(输出特征图的数量),即进行32次卷积操作,得到32张输出图kernel_size=5,              #kernel_size=5在Conv2d中表示卷积核大小(5*5)padding=2,                  #填充(0填充)stride=1                    #步幅),#   卷积后的输出大小计算:(H为图片的高、p为填充、FH为滤波器高、S为步幅)#     OH = (H+2*p-FH)/S + 1 = (28+2*2-5)/1 + 1 = 28#     OW = (W+2*p-FW)/S + 1 = (28+2*2-5)/1 + 1 = 28#2、归一化操作:BN层(Batch Normalization)torch.nn.BatchNorm2d(32),       #初始化32个卷积核#3、激活函数:ReLUtorch.nn.ReLU(),                #激活函数#4、最大池化:池化层torch.nn.MaxPool2d(2),          #2表示池化大小为2*2#   池化后的输出大小计算:(每一块都是4选1) => 长宽减半#       OH = H/2  =   14#       OW = W/2  =   14)#全连接操作self.fc = torch.nn.Linear(in_features=14*14*32,out_features=10)   #前面的参数表示输入的结点数(池化后)【输出的图大小为14*14,共32张】,后面的参数表示输出的结点数(0~9)#前向传播def forward(self,x):           #x为输入的数据,输入形式为张量,tensor(样本数,通道数,高,宽)即(n,c,h,w)out = self.conv(x)         #卷积out = out.view(out.size()[0],-1)    #进行全连接前需要把图像数据转为一维的格式,out.size()[0]为样本数n,表示保持n个样本;-1表示剩下部分全部展为一维out = self.fc(out)         #全连接fc层return out

实例化model

导入model类

在训练前声明,实例化

损失函数及优化函数

###############################损失函数###############################
loss_func = torch.nn.CrossEntropyLoss()       #交叉熵损失函数###############################优化函数###############################
optimizer = torch.optim.Adam(cnn.parameters(), lr = 0.01)        #使用Adam优化方法,传入cnn.parameters()即cnn所有的参数,学习率lr设置为0.01

模型训练

单次遍历训练的训练过程

    for index,(images,labels) in enumerate(train_loader):      #遍历训练集【分开图片与标签】,enumerate的意思是枚举# print(index)                                    #索引(批号)# print(images)                                   #图像# print(labels)                                   #真实标签images = images.cuda()                            #将图片也放到GPU上加载labels = labels.cuda()                            #将标签也放到GPU上加载#前向传播outputs = cnn(images)                             #传入数据(实例),会自动调用前向传播forwardloss = loss_func(outputs, labels)                 #根据真实标签和模型输出计算损失lossoptimizer.zero_grad()                             #梯度清空#反向传播loss.backward()                                   #根据损失函数loss进行反向传播optimizer.step()                                  #优化参数#提示信息print("当前epoch = {},当前批次为{}/{},目前loss为{}".format(epoch+1,             #当前epochindex+1,                 #index表示批次len(train_data)//64,     #//64表示整除64,len(train_data)//64即是将完整数据集分了多少批loss.item()))

一轮epoch后,通过测试集评估效果

    loss_test = 0               #测试中的总损失for(index2,(images,labels)) in enumerate(test_loader):images = images.cuda()                            #将图片也放到GPU上加载labels = labels.cuda()                            #将标签也放到GPU上加载outputs = cnn(images)                             #将测试数据传入模型中计算验证print(outputs)print(labels)loss_test += loss_func(outputs, labels)

查看模型训练后的测试效果

        _, pred = torch.max(outputs,1)                  #torch.max即提取输出的outputs中第1维度数据中的最大值,_部分对应接收最大值(变量_我们默认无意义),pred接受最大值对应的索引print(pred)print(labels)print(pred == labels)print((pred == labels).sum().item())              #sum求和预测结果(正确为1,错误为0),pytorch中输入和输出都是张量,因此需要用到item()取到结果值

模型保存

import os
model_dir = "model"
model_path = os.path.join(model_dir, "mnist_model.pkl")# 检查并创建目录(如果不存在)
if not os.path.exists(model_dir):os.makedirs(model_dir)  # 递归创建目录(即使父目录不存在)torch.save(cnn,model_path)                    #保存模型

epoch设置为10,验证:

总实现代码

CNN.py

import torchclass CNN(torch.nn.Module):                 #继承torch.nn.Module这个基类,我们就可以直接使用人家做好的函数和属性def __init__(self):                      #构造函数super(CNN,self).__init__()          #继承自己的父类self.conv = torch.nn.Sequential(    #Sequential是序列容器,用来实现前向传播#1、卷积操作:卷积层torch.nn.Conv2d(1,                #通道数32,              #卷积核数量(输出特征图的数量),即进行32次卷积操作,得到32张输出图kernel_size=5,              #kernel_size=5在Conv2d中表示卷积核大小(5*5)padding=2,                  #填充(0填充)stride=1                    #步幅),#   卷积后的输出大小计算:(H为图片的高、p为填充、FH为滤波器高、S为步幅)#     OH = (H+2*p-FH)/S + 1 = (28+2*2-5)/1 + 1 = 28#     OW = (W+2*p-FW)/S + 1 = (28+2*2-5)/1 + 1 = 28#2、归一化操作:BN层(Batch Normalization)torch.nn.BatchNorm2d(32),       #初始化32个卷积核#3、激活函数:ReLUtorch.nn.ReLU(),                #激活函数#4、最大池化:池化层torch.nn.MaxPool2d(2),          #2表示池化大小为2*2#   池化后的输出大小计算:(每一块都是4选1) => 长宽减半#       OH = H/2  =   14#       OW = W/2  =   14)#全连接操作self.fc = torch.nn.Linear(in_features=14*14*32,out_features=10)   #前面的参数表示输入的结点数(池化后)【输出的图大小为14*14,共32张】,后面的参数表示输出的结点数(0~9)#前向传播def forward(self,x):           #x为输入的数据,输入形式为张量,tensor(样本数,通道数,高,宽)即(n,c,h,w)out = self.conv(x)         #卷积out = out.view(out.size()[0],-1)    #进行全连接前需要把图像数据转为一维的格式,out.size()[0]为样本数n,表示保持n个样本;-1表示剩下部分全部展为一维out = self.fc(out)         #全连接fc层return out

训练主函数

import os
import torch                                #pytorch库加载
import torchvision.datasets as dataset      #数据集接口
import torchvision.transforms as transforms #数据转换
import torch.utils.data as data_utils       #数据分批from CNN import CNN###############################数据加载###############################
train_data = dataset.MNIST(           #训练集root = "mnist",                    #数据存放目录train = True,                      #加载训练数据transform = transforms.ToTensor(), #将数据转为张量格式download = True                    #如果数据集不存在,下载到本地
);test_data = dataset.MNIST(           #测试集root = "mnist",                    #数据存放目录train = False,                      #加载训练数据transform = transforms.ToTensor(), #将数据转为张量格式download = True                    #如果数据集不存在,下载到本地
)# print(train_data)
# print(test_data)###############################数据分批###############################
train_loader = data_utils.DataLoader(dataset=train_data,        #训练集分批batch_size=64,             #一批64shuffle=True)              #打乱数据test_loader = data_utils.DataLoader(dataset=test_data,          #测试集分批batch_size=64,             #一批64shuffle=True)              #打乱数据# print(train_loader)
# print(test_loader)# for index,value in enumerate(train_loader):      #遍历训练集
#     print(index)
#     print(value)
#     break                                        #只打印一批# for index,(images,labels) in enumerate(train_loader):      #遍历训练集【分开图片与标签】
#     print(index)                                    #索引(批号)
#     print(images)                                   #图像
#     print(labels)                                   #真实标签
#     break                                           #只打印一批###############################模型声明###############################
cnn = CNN()             #声明实例# print(torch.cuda.is_available())        #确认cuda能用
cnn = cnn.cuda()              #声明在cuda上运行【利用GPU运行】###############################损失函数###############################
loss_func = torch.nn.CrossEntropyLoss()       #交叉熵损失函数###############################优化函数###############################
optimizer = torch.optim.Adam(cnn.parameters(), lr = 0.01)        #使用Adam优化方法,传入cnn.parameters()即cnn所有的参数,学习率lr设置为0.01###############################模型训练###############################
for epoch in range(10):                                  #epoch指将所有的数据全部训练一遍for index,(images,labels) in enumerate(train_loader):      #遍历训练集【分开图片与标签】,enumerate的意思是枚举# print(index)                                    #索引(批号)# print(images)                                   #图像# print(labels)                                   #真实标签images = images.cuda()                            #将图片也放到GPU上加载labels = labels.cuda()                            #将标签也放到GPU上加载#前向传播outputs = cnn(images)                             #传入数据(实例),会自动调用前向传播forwardloss = loss_func(outputs, labels)                 #根据真实标签和模型输出计算损失lossoptimizer.zero_grad()                             #梯度清空#反向传播loss.backward()                                   #根据损失函数loss进行反向传播optimizer.step()                                  #优化参数# #提示信息# print("当前epoch = {},当前批次为{}/{},目前loss为{}".format(epoch+1,             #当前epoch#                                                         index+1,                 #index表示批次#                                                         len(train_data)//64,     #//64表示整除64,len(train_data)//64即是将完整数据集分了多少批#                                                         loss.item()))###############################测试集验证###############################loss_test = 0               #测试中的总损失acc = 0for(index2,(images,labels)) in enumerate(test_loader):images = images.cuda()                            #将图片也放到GPU上加载labels = labels.cuda()                            #将标签也放到GPU上加载outputs = cnn(images)                             #将测试数据传入模型中计算验证# print(outputs)# print(labels)_, pred = torch.max(outputs,1)                  #torch.max即提取输出的outputs中第1维度数据中的最大值,_部分对应接收最大值(变量_我们默认无意义),pred接受最大值对应的索引# # print(pred)# # print(labels)# # print(pred == labels)## print((pred == labels).sum().item())              #sum求和预测结果(正确为1,错误为0),pytorch中输入和输出都是张量,因此需要用到item()取到结果值acc += (pred == labels).sum().item()                #相加预测正确的数量loss_test += loss_func(outputs, labels)             #叠加每一步损失print("当前epoch = {},目前loss为{},准确率ACC={}".format(epoch+1,             #当前epochloss_test,acc / len(test_data)))model_dir = "model"
model_path = os.path.join(model_dir, "mnist_model.pkl")# 检查并创建目录(如果不存在)
if not os.path.exists(model_dir):os.makedirs(model_dir)  # 递归创建目录(即使父目录不存在)torch.save(cnn,model_path)                    #保存模型

验证模型test.py

在虚拟环境中安装opencv

conda install -c conda-forge opencv
import cv2
import torch                                #pytorch库加载
import torchvision.datasets as dataset      #数据集接口
import torchvision.transforms as transforms #数据转换
import torch.utils.data as data_utils       #数据分批from CNN import CNN# print(cv2.__version__)test_data = dataset.MNIST(           #测试集root = "mnist",                    #数据存放目录train = False,                      #加载训练数据transform = transforms.ToTensor(), #将数据转为张量格式download = True                    #如果数据集不存在,下载到本地
)test_loader = data_utils.DataLoader(dataset=test_data,          #测试集分批batch_size=64,             #一批64shuffle=True)              #打乱数据cnn = torch.load("model/mnist_model.pkl")       #加载模型
# print(torch.cuda.is_available())        #确认cuda能用
cnn = cnn.cuda()              #声明在cuda上运行【利用GPU运行】###############################损失函数###############################
loss_func = torch.nn.CrossEntropyLoss()       #交叉熵损失函数loss_test = 0  # 测试中的总损失
acc = 0
for (index, (images, labels)) in enumerate(test_loader):images = images.cuda()  # 将图片也放到GPU上加载labels = labels.cuda()  # 将标签也放到GPU上加载outputs = cnn(images)  # 将测试数据传入模型中计算验证(前向传播)_, pred = torch.max(outputs, 1)  # torch.max即提取输出的outputs中第1维度数据中的最大值,_部分对应接收最大值(变量_我们默认无意义),pred接受最大值对应的索引acc += (pred == labels).sum().item()  # 相加预测正确的数量loss_test += loss_func(outputs, labels)  # 叠加每一步损失##opencv可视化#首先要把图片放回cpu,并且将张量转为numpy格式images = images.cpu().numpy()labels = labels.cpu().numpy()preds = pred.cpu().numpy()for idx in range(images.shape[0]):      #遍历每一张图片【B,C,H,W】,shape[0]即是B(batch) = 64im_data = images[idx]               #取出图片【即(C,H,W)】im_label = labels[idx]              #取出真实标签im_pred = pred[idx]                 #取出预测值print("预测值为{}".format(im_pred))print("真实值为{}".format(im_label))im_data = im_data.transpose((1, 2, 0))        #调整图片格式(C,H,W)=>(H,W,C)cv2.imshow("The image",im_data)      #显示图像,第一个参数是当前窗口的命名,第二个是图像,只接受前两个维度,输入格式为(H,W)cv2.waitKey(0)                                #窗口弹出时停止运行,等关闭窗口后继续执行,参数0代表延迟为0print("loss为{},准确率ACC={}".format(loss_test,acc / len(test_data)))

http://www.dtcms.com/a/278000.html

相关文章:

  • Windows怎样同步时间服务器?
  • 最简约的Windows多标签页文件管理器推荐 - 360文件夹 - 免费开源绿色软件推荐
  • Lucene原理
  • Android自定义View的事件分发流程
  • (33)记录描述窗体组件属性的枚举量 enum Qt :: WidgetAttribute, 简记为 WA_
  • Java结构型模式---外观模式
  • 和 *,以及 -> 和 .
  • C语言基础知识--柔性数组
  • 串口学习和蓝牙通信HC05(第八天)
  • LlamaIndex 检索器 Retriever
  • 题目V^V
  • 008_Claude_Code开发工具
  • 自注意力机制及其与早期注意力机制的区别
  • C++高频知识点(十)
  • Android 响应式编程完整指南:StateFlow、SharedFlow、LiveData 详解
  • 封装---统一封装处理页面标题
  • 关于 java:11. 项目结构、Maven、Gradle 构建系统
  • DAY02:【ML 第一弹】KNN算法
  • Datawhale AI夏令营——用AI预测新增用户学习笔记
  • 【VLLM】大模型本地化部署
  • 【图片识别内容改名】用图片的内容改图片文件的名字,批量OCR识别图片上的文字并同时进行批量改名的操作步骤和注意事项
  • 深入了解JAVA中Synchronized
  • MD5算法深度剖析与可视化解析
  • Kubernetes集群安装
  • Codeforces Round 1032 (Div. 3)(A-G)
  • 嵌入式 Linux开发环境构建之安装 Samba
  • Wireshark的安装和基本使用
  • C语言---自定义类型(上)(结构体类型)
  • Vue Router 完全指南:从入门到实战,高效管理前端路由
  • C++高频知识点(十二)