完整代码及解析!!手写数字识别系统(手写数字测试识别 + pytoch实现 + 完整代码及解析) 您所在的位置:网站首页 手写数字识别步骤 完整代码及解析!!手写数字识别系统(手写数字测试识别 + pytoch实现 + 完整代码及解析)

完整代码及解析!!手写数字识别系统(手写数字测试识别 + pytoch实现 + 完整代码及解析)

2023-07-26 09:25| 来源: 网络整理| 查看: 265

基于深度学习的手写数字识别系统 一、实验目的

​ 1、任选实验环境及深度学习框架,实现手写数字识别系统;

​ 2、掌握所采用的深度血迹框架构建方式。

二、实验理论基础 1、MNIST数据集

​ MNIST 数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 包含四个部分:

​ (1)Training set images:train-images-idx3-ubyte.gz(9.9MB,解压后47MB,包含60000个样本)

​ (2)Training set labels:train-labels-idx1-ubyte.gz(29KB,解压后60KB,包含60000个样本)

​ (3)Test set images:t10k-images-idx3-ubyte.gz(1.6MB,解压后7.8MB,包含10000个样本)

​ (4)Test set labels:t10k-labels-idx1-ubyte.gz(5KB,解压后10KB,包含10000个样本)

​ 训练集 (training set) 由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局 (the Census Bureau) 的工作人员. 测试集(test set) 也是同样比例的手写数字数据。图片是以字节的形式进行存储, 我们需要把它们读取到 NumPy array 中, 以便训练和测试算法.

​ 可以建立一个Mnist文件夹,将下载的数据集放入此文件夹中,方便处理

2、数据集中训练集、验证集和测试集的作用

​ 使用了所有的原始数据去训练模型,得到的结果很可能是该模型最大程度地拟合了原始数据,亦即该模型是为了拟合所有原始数据而存在。当新的样本出现,再使用该模型进行预测,效果可能还不如只使用一部分数据训练的模型。因此,为了防止模型过拟合,对原始数据进行了三个数据集的划分。

​ (1)训练集:作用是用来拟合模型,通过设置分类器的参数,训练分类模型。后续结合验证集作用时,会选出同一参数的不同取值,拟合出多个分类器。手写数字识别系统中是用训练集中的images,进行检测,用labels与之对应,使得网络形成记忆。

​ (2)验证集:作用是当通过训练集训练出多个模型后,为了能找出效果最佳的模型,使用各个模型对验证集数据进行预测,并记录模型准确率。选出效果最佳的模型所对应的参数,即用来调整模型参数。

​ (3)测试集:通过训练集和验证集得出最优模型后,使用测试集进行模型预测。用来衡量该最优模型的性能和分类能力。即可以把测试集当做从来不存在的数据集,当已经确定模型参数后,使用测试集进行模型性能评价。在手写数字识别系统中是将测试集中的images输入网络,并将结果与labels比较,用于计算测试集准确率。

三、实验内容 1、 网络模型搭建;

​ 网络一共有7层,分别是《卷积层–池化层–卷积层–池化层–全连接层–全连接层–全连接层》;

​ (1)输入是一个单通道28*28大小的手写数字图片;

​ (2)定义卷积层1,输入通道为1,卷积核数为6,卷积核大小为5*5,池化层1,池化核大小为2*2;

​ (3)定义卷积层2,输入通道为6,卷积核数为16,卷积核大小为5*5,池化层2,池化核大小为2*2;

​ (4)全连接层1、2、3;

​ (5)定义前向传播顺序。

class LeNet5(nn.Module): def __init__(self): # 初始化函数 super(LeNet5,self).__init__()# 多基层一般使用super self.conv1 = nn.Conv2d(1, 6, 5)# 定义第一个卷积层,1是通道数灰度图片,6是卷积核个数,5卷积核大小 self.pool1 = nn.MaxPool2d(2, 2)# 池化核大小2*2,步距也为2,池化层,只改变高和宽,深度不改变 self.conv2 = nn.Conv2d(6, 16, 5)# 输入变为6,因为通过第一个卷积层有6个卷积核,输出深度为6 self.pool2 = nn.MaxPool2d(2, 2) self.fc1 = nn.Linear(16*4*4, 120) # 展开成一维的,第一层120个节点,输入16*4*4个节点 self.fc2 = nn.Linear(120, 84) # 输入120,设置84个节点 self.fc3 = nn.Linear(84, 10) # 输出根据训练集修改 def forward(self,x): x = self.pool1(F.relu(self.conv1(x))) # input(1,28,28),output1(6,24,24) output2(6,12,12) x = self.pool2(F.relu(self.conv2(x))) # input(6,12,12),output1(16,8,8) output2(16,4,4) x = x.view(-1, 16*4*4) # -1第一个维度 x = F.relu(self.fc1(x)) # 全连接层1及其激活函数 x = F.relu(self.fc2(x)) # 全连接层3得到输出 x = self.fc3(x) return x 2、 数据集模型搭建 ;

​ 构建数据集模型是为了在之后的训练和测试中方便对数据集的调用。

​ (1)定义Dataset类型的数据集Mnist,设置三个参数root、train、transform,其中root是需要调用的数据的储存路径,利用train是否为true,判断调用的数据为训练集还是测试集。

class Mnist(Dataset): def __init__(self, root, train=True, transform=None): # 根据是否为训练集,得到文件名前缀 self.file_pre = 'train' if train == True else 't10k' self.transform = transform#定义变换函数 # 生成对应数据集的图片和标签文件路径 self.label_path = os.path.join(root, '%s-labels-idx1-ubyte.gz' % self.file_pre) self.image_path = os.path.join(root, '%s-images-idx3-ubyte.gz' % self.file_pre) # 读取文件数据,返回图片和标签 self.images, self.labels = self.__read_data__( self.image_path, self.label_path)

​ (2)定义__read_data__函数,将data以流的形式读入转化成ndarray对象,ndarray对象是用于存放同类型元素的多维数组,利用reshape函数将图片以标签文件的元素个数读取,设置大小为28*28。

def __read_data__(self, image_path, label_path): # 数据集读取 with gzip.open(label_path, 'rb') as lbpath: labels = np.frombuffer(lbpath.read(), np.uint8, offset=8)#将data以流的形式读入转化成ndarray对象,ndarray对象是用于存放同类型元素的多维数组 with gzip.open(image_path, 'rb') as imgpath: images = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(len(labels), 28, 28)#将图片以标签文件的元素个数读取,设置大小为28*28 return images, labels

​ (3)利用transform函数,将numpy.ndarray类型数据转化为张量。

# 如果需要转成 tensor,(RGB,HWC)张量, 则使用 tansform if self.transform is not None: image = self.transform(np.array(image)) # 此处需要用 np.array(image),转化为数组 return image, label 3、 训练模型搭建;

​ (1)利用定义的Mnist数据集生成训练集,同时利用DataLoader函数,将训练集中的元素随机排序后,以batch_size=32大小返回。

# 生成训练集 train_set = Mnist( root=r'E:\CUDA\Dataset\Mnist', train=True,#训练集 transform=transforms.Compose([#Compose方法是将多种变换组合在一起。 transforms.ToTensor(),#函数接受PIL Image或numpy.ndarray,将其先由HWC转置为CHW格式 transforms.Normalize((0.1037,), (0.3081,))#灰度图像,一个通道,均值和方差,标准化 ]) ) train_loader = DataLoader(#主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor dataset=train_set,#输出的数据 batch_size=32, shuffle=True)#将元素随机排序

​ (2)实化一个定义好的网络,并将网络放进GPU中,定义损失函数和优化器,用来训练网络模型。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")#判断能否调用GPU print(device) # 实例化一个网络 net = LeNet5().to(device) #将网络放进GPU # 定义损失函数和优化器 loss_function = torch.nn.CrossEntropyLoss() optimizer = optim.SGD( net.parameters(),#网络参数 lr=0.001,#学习率 momentum=0.9#Momentum 用于加速 SGD(随机梯度下降)在某一方向上的搜索以及抑制震荡的发生。 )

​ (3)定义训练模型,设置训练10个epoch,在每个epoch中每300个batch打印一次平均损失值,,将训练好的模型保存,打印每次训练后的损失值变化曲线。

# 3 训练模型 loss_list = []#保存损失函数的值 for epoch in range(10):#训练10次 running_loss = 0.0#误差清零? for batch_idx, data in enumerate(train_loader, start=0):#enumerate索引函数,start下标开始位置 images, labels = data # 读取一个batch的数据 images=images.to(device) #将images放进GPU labels=labels.to(device) #将labels放进GPU optimizer.zero_grad() # 梯度清零,初始化,如果不初始化,则梯度会叠加 outputs = net(images) # 前向传播 loss = loss_function(outputs, labels) # 计算误差,label标准? loss.backward() # 反向传播 optimizer.step() # 权重更新 running_loss += loss.item() # 误差累计 # 每300个batch 打印一次损失值 if batch_idx % 300 == 299:#(0-299)(300-599) print('epoch:{} batch_idx:{} loss:{}' .format(epoch+1, batch_idx+1, running_loss/300)) loss_list.append(running_loss/300)#将新的每个平均误差加到损失函数列表后面 running_loss = 0.0 #误差清零 print('Finished Training.') torch.save(net.state_dict(),"Linear.pth")#保存训练模型 # 打印损失值变化曲线 import matplotlib.pyplot as plt plt.plot(loss_list) plt.title('traning loss') plt.xlabel('epochs') plt.ylabel('loss') plt.show()

​ (4)对训练好的网络进行测试,生成测试集的方法如同生成训练集的方法,记录通过网络预测正确的图片数和总图片数,将测试集中images、labels数据送入GPU中,images输入网络,将输出outputs与labels比较,得到预测正确的数目correct,输出测试集准确率。在测试集images中选取前10个数据,进行预测,并于对应labels比较。

# 测试 test_set = Mnist(#生成测试集 root='E:\CUDA\Dataset\Mnist', train=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ]) ) test_loader = DataLoader( dataset=test_set, batch_size=32, shuffle=True ) correct = 0 # 预测正确数 total = 0 # 总图片数 for data in test_loader: images, labels = data images=images.to(device) labels=labels.to(device) outputs = net(images) _, predict = torch.max(outputs.data, 1)#1是指按行,0是按列,-1是指最后一个维度,一般也是按行 total += labels.size(0) correct += (predict == labels).sum() print('测试集准确率 {}%'.format(100*correct // total)) #检测网络 test_output=net(images[:10])#在测试集中选择10张图片输入网络,得到结果 pred_y = torch.max(test_output, 1)[1].data#对得到的结果进行预测 print(pred_y, 'prediction numbe')#输出预测结果 print(labels[:10], 'real number')#输出真实结果 4、 验证模型搭建;

​ (1)调用训练好的网络,生成测试集,从测试集images数据中选取前10个图片输入网络,将预测结果和真实结果输出,并将这10个images数据绘制出来进行比较。

net=LeNet5() net.load_state_dict(torch.load("Linear.pth"))#调用训练好的网络 test_set = Mnist(#生成测试集 root='E:\CUDA\Dataset\Mnist', train=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ]) ) test_loader = DataLoader( dataset=test_set, batch_size=32, shuffle=True) for data in test_loader: images, labels = data #检测网络 test_output=net(images[:10])#在测试集中选择10张图片输入网络,得到结果 pred_y = torch.max(test_output, 1)[1].data#对得到的结果进行预测 print(pred_y, 'prediction numbe')#输出预测结果 print(labels[:10], 'real number')#输出真实结果 def plt_image(image):#定义一个函数,将需要预测的手写数字图画出来 n = 10 plt.figure(figsize=(10,4)) for i in range(n): ax = plt.subplot(2,5,i+1) plt.imshow(images[i].reshape(28,28)) plt.gray() ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) plt.show() plt_image(images)

​ (2)测试自己手动设计的手写数字

I = Image.open('7.jpg') L = I.convert('L')#转化为二值图像 plt.imshow(L, cmap='gray') transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ]) im = transform(L) # [C, H, W] im = torch.unsqueeze(im, dim=0) # [N, C, H, W],扩展维度 with torch.no_grad(): outputs = net(im) _, predict = torch.max(outputs.data, 1) print(predict) 四、附件 1、实验环境

​ (1)编程语言:python 3.6.12

​ (2)编程环境:spyder

​ (3)深度学习框架:pytorch 1.7.0

​ (4)系统环境:64位Windows系统

2、实验源码 (1)数据集模块data.py import os import numpy as np from torch.utils.data import Dataset import gzip import matplotlib.pyplot as plt#绘图库 class Mnist(Dataset): def __init__(self, root, train=True, transform=None): # 根据是否为训练集,得到文件名前缀 self.file_pre = 'train' if train == True else 't10k' self.transform = transform#定义变换函数 # 生成对应数据集的图片和标签文件路径 self.label_path = os.path.join(root, '%s-labels-idx1-ubyte.gz' % self.file_pre) self.image_path = os.path.join(root, '%s-images-idx3-ubyte.gz' % self.file_pre) # 读取文件数据,返回图片和标签 self.images, self.labels = self.__read_data__( self.image_path, self.label_path) def __read_data__(self, image_path, label_path): # 数据集读取 with gzip.open(label_path, 'rb') as lbpath: labels = np.frombuffer(lbpath.read(), np.uint8, offset=8)#将data以流的形式读入转化成ndarray对象,ndarray对象是用于存放同类型元素的多维数组 with gzip.open(image_path, 'rb') as imgpath: images = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(len(labels), 28, 28)#将图片以标签文件的元素个数读取,设置大小为28*28 return images, labels def __getitem__(self, index):#迭代使用?使用Minist()会调用_getitem_ image, label = self.images[index], int(self.labels[index]) # 如果需要转成 tensor,(RGB,HWC)张量, 则使用 tansform if self.transform is not None: image = self.transform(np.array(image)) # 此处需要用 np.array(image),转化为数组 return image, label def __len__(self):#获取元素个数 return len(self.labels) if __name__ == '__main__': # 生成实例 train_set = Mnist( root=r'E:\CUDA\Dataset\Mnist',#MNIST数据集路径 train=False, ) # 取一组数据并展示 (data, label) = train_set[0]#第一组数据 plt.imshow(data.reshape(28, 28), cmap='gray')#灰色图像 plt.title('label is :{}'.format(label)) plt.show() (2)网络模型模块model.py import torch.nn as nn import torch.nn.functional as F class LeNet5(nn.Module): def __init__(self): # 初始化函数 super(LeNet5,self).__init__()# 多基层一般使用super self.conv1 = nn.Conv2d(1, 6, 5)# 定义第一个卷积层,1是通道数灰度图片,6是卷积核个数,5卷积核大小 self.pool1 = nn.MaxPool2d(2, 2)# 池化核大小2*2,步距也为2,池化层,只改变高和宽,深度不改变 self.conv2 = nn.Conv2d(6, 16, 5)# 输入变为6,因为通过第一个卷积层有6个卷积核,输出深度为6 self.pool2 = nn.MaxPool2d(2, 2) self.fc1 = nn.Linear(16*4*4, 120) # 展开成一维的,第一层120个节点,输入16*4*4个节点 self.fc2 = nn.Linear(120, 84) # 输入120,设置84个节点 self.fc3 = nn.Linear(84, 10) # 输出根据训练集修改 def forward(self,x): x = self.pool1(F.relu(self.conv1(x))) # input(1,28,28),output1(6,24,24) output2(6,12,12) x = self.pool2(F.relu(self.conv2(x))) # input(6,12,12),output1(16,8,8) output2(16,4,4) x = x.view(-1, 16*4*4) # -1第一个维度 x = F.relu(self.fc1(x)) # 全连接层1及其激活函数 x = F.relu(self.fc2(x)) # 全连接层3得到输出 x = self.fc3(x) return x if __name__ == '__main__': net = LeNet5() print(net) (3)训练模块train.py import torch import torchvision.transforms as transforms import torch.optim as optim#优化器 from torch.utils.data import DataLoader from data import Mnist from model import LeNet5 # 生成训练集 train_set = Mnist( root=r'E:\CUDA\Dataset\Mnist', train=True,#训练集 transform=transforms.Compose([#Compose方法是将多种变换组合在一起。 transforms.ToTensor(),#函数接受PIL Image或numpy.ndarray,将其先由HWC转置为CHW格式 transforms.Normalize((0.1037,), (0.3081,))#灰度图像,一个通道,均值和方差,标准化 ]) ) train_loader = DataLoader(#主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor dataset=train_set,#输出的数据 batch_size=32, shuffle=True#将元素随机排序 ) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")#判断能否调用GPU print(device) # 实例化一个网络 net = LeNet5().to(device) #将网络放进GPU # 定义损失函数和优化器 loss_function = torch.nn.CrossEntropyLoss() optimizer = optim.SGD( net.parameters(),#网络参数 lr=0.001,#学习率 momentum=0.9#Momentum 用于加速 SGD(随机梯度下降)在某一方向上的搜索以及抑制震荡的发生。 ) # 3 训练模型 loss_list = []#保存损失函数的值 for epoch in range(10):#训练10次 running_loss = 0.0#误差清零? for batch_idx, data in enumerate(train_loader, start=0):#enumerate索引函数,start下标开始位置 images, labels = data # 读取一个batch的数据 images=images.to(device) #将images放进GPU labels=labels.to(device) #将labels放进GPU optimizer.zero_grad() # 梯度清零,初始化,如果不初始化,则梯度会叠加 outputs = net(images) # 前向传播 loss = loss_function(outputs, labels) # 计算误差,label标准? loss.backward() # 反向传播 optimizer.step() # 权重更新 running_loss += loss.item() # 误差累计 # 每300个batch 打印一次损失值 if batch_idx % 300 == 299:#(0-299)(300-599) print('epoch:{} batch_idx:{} loss:{}' .format(epoch+1, batch_idx+1, running_loss/300)) loss_list.append(running_loss/300)#将新的每个平均误差加到损失函数列表后面 running_loss = 0.0 #误差清零 print('Finished Training.') torch.save(net.state_dict(),"Linear.pth")#保存训练模型 # 打印损失值变化曲线 import matplotlib.pyplot as plt plt.plot(loss_list) plt.title('traning loss') plt.xlabel('epochs') plt.ylabel('loss') plt.show() # 测试 test_set = Mnist(#生成测试集 root='E:\CUDA\Dataset\Mnist', train=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ]) ) test_loader = DataLoader( dataset=test_set, batch_size=32, shuffle=True ) correct = 0 # 预测正确数 total = 0 # 总图片数 for data in test_loader: images, labels = data images=images.to(device) labels=labels.to(device) outputs = net(images) _, predict = torch.max(outputs.data, 1)#1是指按行,0是按列,-1是指最后一个维度,一般也是按行 total += labels.size(0) correct += (predict == labels).sum() print('测试集准确率 {}%'.format(100*correct // total)) #检测网络 test_output=net(images[:10])#在测试集中选择10张图片输入网络,得到结果 pred_y = torch.max(test_output, 1)[1].data#对得到的结果进行预测 print(pred_y, 'prediction numbe')#输出预测结果 print(labels[:10], 'real number')#输出真实结果 (4)测试模块test.py import torch import torchvision.transforms as transforms from torch.utils.data import DataLoader from data import Mnist from model import LeNet5 import matplotlib.pyplot as plt net=LeNet5() net.load_state_dict(torch.load("Linear.pth"))#调用训练好的网络 #检测网络 test_set = Mnist(#生成测试集 root='E:\CUDA\Dataset\Mnist', train=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ]) ) test_loader = DataLoader( dataset=test_set, batch_size=32, shuffle=True ) for data in test_loader: images, labels = data test_output=net(images[:10])#在测试集中选择10张图片输入网络,得到结果 pred_y = torch.max(test_output, 1)[1].data#对得到的结果进行预测 print(pred_y, 'prediction numbe')#输出预测结果 print(labels[:10], 'real number')#输出真实结果 def plt_image(image):#定义一个函数,将需要预测的手写数字图画出来 n = 10 plt.figure(figsize=(10,4)) for i in range(n): ax = plt.subplot(2,5,i+1) plt.imshow(images[i].reshape(28,28)) plt.gray() ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) plt.show() plt_image(images) # 测试自己手动设计的手写数字 from PIL import Image I = Image.open('2.jpg') L = I.convert('L')#转化为二值图像 plt.imshow(L, cmap='gray') transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1037,), (0.3081,)) ]) im = transform(L) # [C, H, W] im = torch.unsqueeze(im, dim=0) # [N, C, H, W],扩展维度 with torch.no_grad(): outputs = net(im) _, predict = torch.max(outputs.data, 1) print(predict)


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有