初探深度学习-手写字体识别

前言

手写数字的神经网络识别通常指的是通过训练有素的神经网络模型来识别和分类手写数字图像的任务。这种类型的任务是机器学习和计算机视觉领域的一个经典问题,经常作为入门级的图像识别问题来展示和测试各种机器学习算法的能力。
在实际应用中,手写数字识别可以用于处理和分析用户书写的数字信息,比如自动识别和输入手写数字文档中的数据,或者用于教育领域的练习评分系统,其中系统可以自动识别学生书写的数学题答案并提供反馈。
MNIST数据集是手写数字识别中最常使用的数据集之一。它包含了大量不同书写风格的手写数字图片,以及与之对应的标签。这个数据集被广泛用于训练和测试各种机器学习模型,包括深度学习模型。
在训练过程中,神经网络会学习如何识别和区分不同的手写数字,这个过程涉及到从大量的样本中提取特征,并调整网络内部参数,以便在看到一个新的手写数字图片时,能够准确地预测它所代表的数字。

训练过程

1-数据处理

本次训练的数据集选自 MNIST 数据集,该数据集保存在torchvision包中,因此我们只需要在训练开始前导入该包并进行数据处理即可:

# 导入训练集相关数据包
import torchvision
from torchvision.transforms import ToTensor
# 导入相关数据,并保存到本地,以便于下次训练
train_ds = torchvision.datasets.MNIST('data/',train=True,transform=ToTensor(),download=True)
test_ds = torchvision.datasets.MNIST('data/',train=False,transform=ToTensor(),download=True)
#对数据进行相关的处理
train_dl = torch.utils.data.DataLoader(train_ds,batch_size=64,shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds,batch_size=64)

在导入数据集合的时候,transform=Tensor()做的事情是将图片和标签转换为PyTorch张量,这样为我们后续进行相关模型处理提供了方便。
其中DataLoader函数对我们导入的相关数据进行了处理,其中进行了两个重要的步骤:

  1. 对训练集和测试集的数据设置了batch_size=64,这是每个批量包含64个样本的意思是,在每一次训练迭代中,模型会同时处理64个样本。这些样本会一起通过模型的前向传播(forward pass)过程,然后计算损失(如何预测不准确)并更新模型的参数。
  2. 对训练集的数据设置了打乱,这样会增加了模型的泛化能力,模型能更好地从整体上学习数据的分布,而不是记住每个单独的数据点。

2-模型构建

在导入完数据后,我们需要设计一个简单的神经网络,因为我们处理的是图像,因此输入维度是28*28,又因为我们处理的是手写字体识别,这是一个十分类的问题,因此我们的输出层的维度是10。在此基础上,我们对这个三层全连接的神经网络中间的隐藏层进行相关的设置,具体的代码如下:

class Model(nn.Module):def __init__(self):super().__init__()# 第一层输入展平后长度为28X28,创建128个神经元self.liner_1 = nn.Linear(28*28,128)# 第二层是前一层的输出,创建84个神经元self.liner_2 = nn.Linear(128,84)# 输出层接收第二层的输入84,输出分类个数为10self.liner_3 = nn.Linear(84,10)

在简单设置完这个三层的神经网络之后,我们需要设计这个神经网络的前向传播过程,具体代码如下:

    def forward(self,input):x = input.view(-1,28*28) #将输入展平为二维(1,28,28)->(28*28)x = torch.relu(self.liner_1(x))x = torch.relu(self.liner_2(x))x = self.liner_3(x)return x

这段代码实现的是将每个批量的多维张量的大小进行改变,以保证保持批次大小不变,但同时得到图像的像素总数,作为第一个线性层的输入,然后将输入通过第一个全连接层(self.liner_1),然后应用ReLU(Rectified Linear Unit)激活函数。
输入完成后再进行传入到第二个全连接层,这是另一个非线性变换,进一步帮助网络提取和组合特征。
x = self.liner_3(x) 这行代码将上一个层的输出通过输出层(self.liner_3)。这一层通常用于生成最终的预测结果。在这个例子中,self.liner_3是一个分类层,它将输出一个10维的向量,表示10个类别的得分。
最后,函数返回通过整个网络前向传播后的输出x。这个输出将用于后续的损失计算和梯度下降步骤,以训练网络的权重。
在设置完相关的神经网络模型后,我们还需要设置相关的损失函数和优化器,相关代码如下:

model = Model().to(device)
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(),lr=0.001)

这段代码创建了一个损失函数CrossEntropyLoss的实例。这是一个用于分类问题的损失函数,它计算预测的概率分布与真实标签之间的交叉熵。
然后创建了一个优化器SGD(Stochastic Gradient Descent)的实例。这个优化器用于更新模型的参数,以最小化损失函数。
通过将模型中模型中所有可训练参数的列表进行返回,并且设置学习率为0.001进行优化器每次更新参数时所使用的步长的设定。

3-训练函数&测试函数

设计完模型后,我们需要通过对模型的使用,来设计我们的训练函数和测试函数,其中训练函数代码如下:

#编写训练循环
def train(dataloader,model,loss_fn,optimizer):size = len(dataloader.dataset)num_batchs = len(dataloader)train_loss,correct = 0,0for X,y in dataloader:X,y =X.to(device),y.to(device)pred = model(X)loss = loss_fn(pred,y)optimizer.zero_grad()loss.backward()optimizer.step()with torch.no_grad():correct += (pred.argmax(1)==y).type(torch.float).sum().item()train_loss += loss.item()train_loss /= num_batchscorrect /= sizereturn train_loss,correct

这段代码首先获取数据加载器中整个数据集的大小和批量,将其赋值给sizenum_batchs,同时将train_losscorrect置空。
然后遍历数据加载器中的每个批次,将其数据和标签赋值给Xy,并将其移动到GPU中。
在此基础上,进行模型的训练,首先通过将X传入模型,得到前向传播的结果pred,然后计算预测pred和真实标签y之间的损失。
其中以下三行代码极其关键:

        optimizer.zero_grad()loss.backward()optimizer.step()

这三段代码实现的功能是:首先清除优化器中先前计算的梯度,为新的梯度更新做准备。然后计算损失关于模型参数的梯度。最后使用计算出的梯度更新模型的参数。
最后通过以下代码更新损失和准确度,并将其返回:

        with torch.no_grad():correct += (pred.argmax(1)==y).type(torch.float).sum().item()train_loss += loss.item()train_loss /= num_batchscorrect /= sizereturn train_loss,correct

测试函数与训练函数大体相似,不同的是没有使用相关的传播代码,因为它只需要测试,不需要训练:

def test(dataloader,model):size = len(dataloader.dataset)num_batches =len(dataloader)test_loss,correct = 0,0with torch.no_grad():for X,y in dataloader:X,y =X.to(device),y.to(device)pred = model(X)test_loss += loss_fn(pred,y).item()correct +=(pred.argmax(1)==y).type(torch.float).sum().item()test_loss /= num_batchescorrect /= sizereturn test_loss,correct

4-模型训练

模型训练的过程就是通过持续调用模型的训练函数和测试函数,进行多轮训练,此时我们设置训练轮数为 50 轮,相关代码如下:

epochs = 50
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):epoch_loss,epoch_acc=train(train_dl,model,loss_fn,optimizer)epoch_test_loss,epoch_test_acc=test(test_dl,model)train_loss.append(epoch_loss)train_acc.append(epoch_acc)test_loss.append(epoch_test_loss)test_acc.append(epoch_test_loss)template=("epoch:{:2d},train_loss:{:.5f},train_acc:{:.1f}%,""test_loss:{:.5f},test_acc:{:.1f}%")print(template.format(epoch,epoch_loss,epoch_acc*100,epoch_test_loss,epoch_test_acc*100))
print("Done!")  

5-模型使用

在训练完模型后,我们需要对模型进行保存,以便后续将模型赋值给相关函数和功能,保存模型的代码如下,该代码会将训练的模型以model_complete_pth方式进行保存:

torch.save(model,'model_complete.pth')

接下来我们调用该模型,对传入的图形数据进行预测,整体代码如下:

import torch
import torchvision
from torchvision.transforms import ToTensor
# 对训练数据进行处理
from random import shuffle
import matplotlib.pyplot as plt
import numpy as np
from torch import nn
class Model(nn.Module):def __init__(self):super().__init__()# 第一层输入展平后长度为28X28,创建128个神经元self.liner_1 = nn.Linear(28*28,128)# 第二层是前一层的输出,创建84个神经元self.liner_2 = nn.Linear(128,84)# 输出层接收第二层的输入84,输出分类个数为10self.liner_3 = nn.Linear(84,10)def forward(self,input):x = input.view(-1,28*28) #将输入展平为二维(1,28,28)->(28*28)x = torch.relu(self.liner_1(x))x = torch.relu(self.liner_2(x))x = self.liner_3(x)return x
device = 'cuda' if torch.cuda.is_available() else "cpu"
model = Model().to(device)
train_ds = torchvision.datasets.MNIST('data/',train=True,transform=ToTensor(),download=True)
test_ds = torchvision.datasets.MNIST('data/',train=False,transform=ToTensor(),download=True)
train_dl = torch.utils.data.DataLoader(train_ds,batch_size=64,shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds,batch_size=64)
def process_image(image):image = image.unsqueeze(0)  # 在第一维增加一个维度,因为模型期望的输入形状是 (batch_size, channels, height, width)image = image.to(device)return image
def predict(image):image = process_image(image)with torch.no_grad():prediction = model(image)_,predicted_digit = torch.max(prediction, 1)return predicted_digit.item()
device = 'cuda' if torch.cuda.is_available() else "cpu"
model = torch.load('model_complete.pth')
for i in range(5):test_image, _ = test_ds[i]plt.imshow(test_image.squeeze(), cmap='gray')plt.show()predicted_digit = predict(test_image)print(f"The predicted digit is: {predicted_digit}")

程序运行后,会根据输入的图片进行相关的分类,准确度较高:image.png

总结

本篇文章我们记录了对手写字体识别的训练任务,包括了数据处理、模型构建、训练函数构建、测试函数构建、模型训练及使用五个部分,作为初级的深度学习训练任务,这是一个十分类的任务,通过这个任务,我们可以具体的去感知神经网络任务的设计、使用、损失函数的使用、优化器的使用,这背后涉及到很多的数学知识需要我们去学习和调优。

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

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

相关文章

mac电脑总卡蓝屏是怎么回事,苹果电脑老卡蓝屏怎么办

电脑老卡蓝屏是比较常见的电脑故障之一,导致这一问题的出现很可能是电脑本身的硬件,或电脑上的驱动安装错误,没法运行,当然也不排除其他的一些因素。虽说电脑蓝屏是电脑几乎都会出现的小毛病,不足以致命,但…

基于决策树实现葡萄酒分类

基于决策树实现葡萄酒分类 将葡萄酒数据集拆分成训练集和测试集,搭建tree_1和tree_2两个决策树模型,tree_1使用信息增益作为特征选择指标,B树使用基尼指数作为特征选择指标,各自对训练集进行训练,然后分别对训练集和测…

图论练习6

[NOIP2013]车站分级 Here 解题思路 由于起始点之间所选的站号,相互之间一定满足那么对于起始点间未选择的站号,一定满足选择的站号考虑用边来维护信息,表示的级别大于按题意,则车站会被分为几个联通块,且保证块内无环…

So you think you understand IP fragmentation?

文章目录 前言一、Why care?二、Prevention三、Well-understood?四、Introducing fragquiz五、A novel (?) algorithm六、Reader challenge七、traceroute八、ICMP参考资料 前言 本文来自:https://lwn.net/Articles/960913/ February 7, 2024This article was …

【Python】成功解决ModuleNotFoundError: No module named ‘seaborn’

【Python】成功解决ModuleNotFoundError: No module named ‘seaborn’ 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程👈 …

高分辨率全球海洋温度和盐度再分析数据Global Ocean Physics Reanalysis(0.083°),并利用matlab读取绘图

1.引言 在研究全球海平面变化的问题中,卫星测高获得总的海平面变化,而海平面变化包含质量变化和比容变化。因此测高数据和海洋物理分析数据对于海平面研究至关重要。 测高数据下载网址: Global Ocean Gridded L 4 Sea Surface Heights And …

动态规划课堂4-----子数组系列

目录 引入: 例题1:最大子数组和 例题2:环形子数组的最大和 例题3:乘积最大子数组 例题4:乘积为正数的最长子数组 总结: 结语: 引入: 在动态规划(DP)子…

农场管理小程序|基于微信小程序的农场管理系统设计与实现(源码+数据库+文档)

农场管理小程序目录 目录 基于微信小程序的农场管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、用户信息管理 2、农场信息管理 3、公告信息管理 4、论坛信息管理 四、数据库设计 五、核心代码 七、最新计算机毕设选题推荐 八、源码获取&#x…

【工具使用-VScode】VScode如何设置空格和tab键显示

一,简介 在提交代码的时候,行末尾的tab和空格不符合规范,但是如果在vscode中不显示tab和空格的话,不能及时的查看到并改正,导致提交代码之后还需要再次进行修改,效率比较低。 代码编辑界面如图所示&#…

【大厂AI课学习笔记NO.68】开源和开源发展情况

开源即源代码公开,任何人能获取源代码,查看、修改、分发他们认为合适的代码。 依托同行评审和社区生成,旨在以分散、协作的方式开发。 我们曾经很详细的讨论过开源协议的问题,详细可以参考我的文章: https://giszz.…

LeetCode-1004. 最大连续1的个数 III

每日一题系列(day 20) 前言: 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 &#x1f50…

docker部署在线聊天室平台Fiora

Fiora 是一款开源免费的在线聊天系统 https://github.com/yinxin630/fiora 部署 创建docker网络 docker network create fiora-networkdocker-compose部署 vim docker-compose.yml version: 3 services:fiora_redis:image: rediscontainer_name: fiora_redisrestart: alway…

电脑解锁后黑屏有鼠标--亲测!!不需要重装系统!!

问题:上周电脑黑屏,只有鼠标,鼠标还不能右键!! 中招:win10系统最新版火绒安全 ,那你有概率获得开机黑屏套餐一份。 原因是:火绒把我们的explorer删除了导致黑屏,这个文…

【OpenGL手册11】材质的模型

目录 一、说明二、材质表面和光照三、设置材质四、光的属性五、不同的光源颜色练习 一、说明 在现实世界里,每个物体会对光产生不同的反应。比如,钢制物体看起来通常会比陶土花瓶更闪闪发光,一个木头箱子也不会与一个钢制箱子反射同样程度的…

1分钟带你学会使用装饰器编写Python函数

1.需求 向 test() 函数中,新增一个功能,多输出一句话"给他补铁" def test():print("水中放吸铁石") # test()# 第一种方式:重写函数 def test():print("水中放吸铁石")print("给他补铁") test()# …

py脚本模拟json数据,StructuredStreaming接收数据存储HDFS一些小细节 ERROR:‘path‘ is not specified

很多初次接触到StructuredStreaming 应该会写一个这样的案例 - py脚本不断产生数据写入linux本地, 通过hdfs dfs 建目录文件来实时存储到HDFS中 1. 指定数据schema: 实时json数据 2. 数据源地址:HDFS 3. 结果落地位置: HDFS …

高级语言讲义2010软专(仅高级语言部分)

1.编写一程序&#xff0c;对输入的正整数&#xff0c;求他的约数和。 如&#xff1a;18的约数和为1236939 #include <stdio.h>int getsum(int n){int i,sum0;for(i1;i<n;i)if(n%i0)sumi;return sum; } int main(){int sum getsum(18);printf("%d",sum); …

PCB检测,基于YOLOV8NANO

PCB检测&#xff0c;基于YOLOV8NANO&#xff0c;训练得到PT模型&#xff0c;转换成ONNX&#xff0c;只需要OPENCV&#xff0c;支持C/PYTHON/ANDROID开发PCB检测&#xff0c;基于YOLOV8NANO&#xff0c;只需要OPENCV

每日一题leetcode第2834:找出美丽数组的最小和

目录 一.题目描述 二.思路及优化 三.C代码 一.题目描述 二.思路及优化 首先我们看到这个题&#xff0c;就是根据给出的数组元素个数N&#xff0c;从[1&#xff0c;N]找出N个元素&#xff0c;使得N个元素的和最小&#xff0c;其中随便抽两个数出来&#xff0c;两个数之和不能为…

BC134 蛇形矩阵

一&#xff1a;题目 二&#xff1a;思路分析 2.1 蛇形矩阵含义 首先&#xff0c;这道题我们要根据这个示例&#xff0c;找到蛇形矩阵是怎么移动的 这是&#xff0c;我们可以标记一下每次移动到方向 我们根据上图可以看出&#xff0c;蛇形矩阵一共有两种方向&#xff0c;橙色…