使用pytorch实现手写数字识别(代码片段)

WXiujie123456 WXiujie123456     2022-12-30     775

关键词:

使用Pytor## 标题ch实现手写数字识别

思路和流程分析

  1. 准备数据,这些需要准备DataLoader
  2. 构建模型,这里可以使用torch构造一个深层的神经网络
  3. 模型的训练
  4. 模型的保存,保存模型,后续持续使用
  5. 模型的评估,使用测试集,观察模型的好坏

准备训练集和测试集

准备数据集的方法前面已经讲过,但是通过前面的内容可知,调用MNIST返回的结果中图像数据是一个image对象,需要对其进行处理。

为了进行数据的处理,我们需要学习torchvision.transform的方法

torchvision.transform的图形数据处理API

torchvision.transform.ToTensor

把一个取值范围是[0,255]的PIL.Image或者shape为(H,W,C)的numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]的torch.FloatTensor

其中(H,W,C)意思为(高,宽,通道数),黑白图片的通道数只有1,其中每个像素点的取值为[0,255],彩色图片的通道数为[R,G,B],每个通道的每个像素点的取值为[0,255],三个通道的颜色互相叠加,形成了各种颜色

实例如下:

from torchvision import transforms
import numpy as np

data = np.random.randint(0,255,size=12)#随机生成12个0-255的数字
img = data.reshape(2,2,3)#将这12个数字的形状改成2 2 3
print(img.shape)
img_tensor = transforms.ToTensor()(img)#转化成tensor
print(img_tensor)
print(img_tensor.shape)

输出如下:

(2, 2, 3)
tensor([[[ 22,  78],
         [ 13, 167]],

        [[153, 107],
         [102, 100]],

        [[ 10,  64],
         [  9,  89]]], dtype=torch.int32)
torch.Size([3, 2, 2])

可见,使用transforms.ToTensor()(img)转化成tensor类型后,这个新对象的形状变成[3,2,2](原来是(2,2,3)),相当于torch.tensor(img).permute(2,0,1)

即:

from torchvision import transforms
import numpy as np
import torch

data = np.random.randint(0,255,size=12)
img = data.reshape(2,2,3)
print(img)
print(img.shape)
img_tensor = transforms.ToTensor()(img)#转化成tensor
print(img_tensor)
print(img_tensor.shape)
img_t = torch.tensor(img)
print(img_t.permute(2,0,1))
print(img_t.permute(2,0,1).shape)

对应输出如下:

[[[154 131   6]
  [113 149   7]]

 [[ 11  19 163]
  [112 111  97]]]
(2, 2, 3)
tensor([[[154, 113],
         [ 11, 112]],

        [[131, 149],
         [ 19, 111]],

        [[  6,   7],
         [163,  97]]], dtype=torch.int32)
torch.Size([3, 2, 2])
tensor([[[154, 113],
         [ 11, 112]],

        [[131, 149],
         [ 19, 111]],

        [[  6,   7],
         [163,  97]]], dtype=torch.int32)
torch.Size([3, 2, 2])

对应应用于MNIST中:

import torchvision
from torchvision import transforms
dataset = torchvision.datasets.MNIST(root='./data',train=True,download=True,transform=None)
print(dataset[0])
ret = transforms.ToTensor()(dataset[0][0])
print(ret.size())
#print(ret) 输出这个1*28*28的数组,内容太多不展示了

输出如下:

(<PIL.Image.Image image mode=L size=28x28 at 0x19FF2B2C7B8>, 5)
torch.Size([1, 28, 28])

可见通过transforms.ToTensor方法,把dataset[0]元组中第一个img对象转换成了[1,28,28]的tensor类型的数组(对应[通道,高,宽])

注意:transforms.ToTensor对象中有__calll__方法,所以可以对其示例能传入数据获取结果。

torchvision.transform.Normalize(mean,std)

给定均值:mean,shape(形状)和图片的通道数相同(指的是每个通道的均值)。

方差:std,和图片的通道数相同(指的是每个通道的方差)

将会把Tensor规范化处理,即:Normalize_image = (image - mean) / std

例如:

from torchvision import transforms
import numpy as np
import torchvision
data = np.random.randint(0,255,size=12)
img = data.reshape(2,2,3)
img = transforms.ToTensor()(img) #转化成tensor
print(img)
print('*' * 50)
norm_img = transforms.Normalize((10,10,10),(1,1,1))(img)#进行规范化处理
print(norm_img)

输出如下:

tensor([[[103,   6],
         [157, 226]],

        [[ 17, 119],
         [176,  37]],

        [[  6, 137],
         [173, 193]]], dtype=torch.int32)
**************************************************
tensor([[[ 93,  -4],
         [147, 216]],

        [[  7, 109],
         [166,  27]],

        [[ -4, 127],
         [163, 183]]], dtype=torch.int32)

其中,93=(103-10)/1,10是均值,1是方差

torchvision.transforms.Compose(transforms)

将多个transform组合起来使用

  1. 传入一个list
  2. 数据经过list中的每一个方法挨个进行处理

例如:

transforms.Compose([
    torchvision.transforms.ToTensor(),#先转化为Tensor
    torchvision.transforms.Normalize(mean,std) #再进行正则化
])
准备训练集和测试集的代码实现
from torch.utils.data import DataLoader
from torchvision.transforms import Compose,ToTensor,Normalize
from torchvision.datasets import MNIST

#准备数据集
transform_fn = Compose([ #定义数据处理函数,完成对数据的totorch处理和标准化
    ToTensor(),
    Normalize(mean=(0.1307,),std=(0.3081,)) #mean 和std的形状要和数据通道数相同
])
dataset = MNIST(root='./data',train=True,transform=transform_fn) #设置数据集
data_Loader = DataLoader(dataset,batch_size=2,shuffle=True)#设置加载器

for i in enumerate(data_Loader):
    print(i)

构建模型

补充:全连接层:当前一层的神经元和前一层的神经元相互链接,其核心操作就是y=wx,即矩阵的乘法,实现对前一层数据的变换。

模型的构建使用了一个三层的神经网络,其中包含两个全连接层和一个输出层,第一个全连接层会经过激活函数的处理,将处理后的结果交给下一个全连接层,进行变换后输出结果。

那么在这个模型中有三个地方需要注意:

  • 激活函数如何使用

  • 每一层数据的形状

  • 模型的损失函数

激活函数如何使用

常见的激活函数Relu,它实现对数据中所有的负数置为零,其余0和正数原样输出的效果,它由import torch.nn.functional as F提供(这个包提供了很多激活函数),F.relu(x)即可对x进行处理。

例如:

b = torch.tensor([-2,-1,0,1,2])
print(F.relu(b))

运行结果:

tensor([0, 0, 0, 1, 2])

模型中数据的形状(【添加形状变化图形】)

  1. 原数输入数据的形状:[batch_size,1,28,28]
  2. 进行数据的修改:[batch_size,28*28](全连接层是在进行矩阵的乘法操作)
  3. 第一个全连接层的输出形状:[batch_size,28],这里的28因个人设定,也可以设置为别的
  4. 激活函数不会修改数据的形状
  5. 第二个全连接层的输出形状:[batch_size,10],因为手写数字有十个类别

构建模型的代码如下:

class MnistModel(nn.Module):
    def __init__(self):
        super(MnistModel,self).__init__()
        # 定义f1方法,使用Linear方法,Linear(输入的形状,输出的形状),将输入的28*28输出为28
        self.fc1 = nn.Linear(in_features=28*28,out_features=28)
        # 将输入的28输出为10,因为预期输出是10个数字
        self.fc2 = nn.Linear(28,10)


    def forward(self,input):
        '''
        :param input: [batch_size,1,28*28] 我们获得的原始数据的样子,即input[0]=batchsize,input[1]=1,input[2]=28*28
        :return: 
        '''
        #更改形状,view函数相当于resize的功能,将原来的tensor变换成指的维度,input.size(0)指batchsize的值
        x = input.view(input.size(0),28*28) #这里实现把形状转化为[batch_size,28*28]
        # x = input.view(-1,28*28) #二者实现效果相同
        # x = input.view(input.size(0),-1)#实现效果与未注释的那句等价
        #进行全连接操作
        x = self.fc1(x)
        #使用激活函数处理数据,不会使形状发生变化
        x = F.relu(x)
        #输出层
        out = self.fc2(x)
        return out

可见,pytorch在构建模型的时候形状上并不会考虑batch_size。

补充知识

view()函数的功能根reshape类似,用来转换size大小。x = x.view(batchsize, -1)中batchsize指转换后有几行,而-1指在不告诉函数有多少列的情况下,根据原tensor数据和batchsize自动分配列数。x = x.view(x.size(0), -1)相当于2x = x.view(batchsize, -1)。

模型的损失函数

需要知道,手写数字识别是一个多分类问题,所谓多分类是对比之前的二分类。

回顾一下二分类:

sigmoid函数表达式如下:
1 1 + e − Z \\frac11+e^-Z 1+eZ1

图像如下:

可以看到在趋于正无穷或负无穷时,函数趋近平滑状态,sigmoid函数因为输出范围(0,1),所以二分类的概率常常用这个函数,特点:

  1. 值域在0和1之间

  2. 函数具有非常好的对称性

  3. 函数对输入超过一定范围就会不敏感

现在我们使用多分类应该如何处理呢?

  • 多分类应该使用softmax模型,而不是继续使用二分类的sigmoid模型
  • softmax和sigmoid的区别在于我们需要去计算样本属于每个类别的概率,需要计算多次,而sigmoid只需要计算一次。

softmax的公式如下:

例如下图:

我们把softmax概率传入对数似然损失的损失函数称为交叉熵损失

在pytorch中有两种方法实现交叉熵损失

criterion = nn.CrossEntropyLoss()
loss = criterion(input.taget)
#对输出值计算softmax和取对数
output = = F.log_softmax(x,dim=-1)
#使用torch中带权损失
loss = F.nll_loss(output,target)

带权损失定义为:
l n = − ∑ w i x i l_n=-\\sum w_ix_i ln=wixi
其实就是把log(P)作为x_i,把真实值Y作为权重

模型的训练

训练流程:

  1. 实例化模型,设置模型为训练模式
  2. 实例化优化器模型,实例化损失函数
  3. 获取、遍历dataloader
  4. 梯度置为0
  5. 进行前向计算
  6. 计算损失
  7. 反向传播
  8. 更新参数
model = MnistModel()#实例化模型,设置模型为训练模式(默认)
optimizer = Adam(model.parameters(),lr=0.001)#实例化优化器模型

def train(epoch):#epoch 轮的意思
    '''实现训练的过程'''
    data_loader = get_dataLoader()#获取dataloader
    for idx,(input,target) in enumerate(data_loader):
        optimizer.zero_grad()#梯度置为零
        out_put = model(input)#进行前向计算,调用模型,得到预测值
        loss = F.nll_loss(out_put,target)#带权损失
        loss.backward()#反向传播(记得梯度置为0),计算梯度
        optimizer.step()#梯度更新
        if idx%100 == 0:
            print(loss.item())

模型的保存和加载

模型的保存
#模型的保存
if idx%100 ==0:
    torch.save(model.state_dict(),'./model/model.pkl')
    torch.save(optimizer.state_dict(), './model/optimizer.pkl')
模型的加载
if os.path.exists('./model/model.pkl'):#判断路径是否存在
    model.load_state_dict(torch.load('./model/model.pkl'))
    optimizer.load_state_dict(torch.load('./model/optimizer.pkl'))

模型的评估

评估的过程和训练的过程相似,但是:

  1. 不需要计算梯度
  2. 需要收集损失和转化率,用来计算平均损失和平均准确率
  3. 损失的计算和训练时候损失的计算方法相同
  4. 准确率的计算:
    • 模型的输出为[batch_size]的形状
    • 其中最大值的位置就是其预测的目标值(预测值进行过softmax后为概率,softmax中分母都是相同的,分子越大,概率越大)
    • 最大值的位置的获取方法可以使用torch.max返回最大值和最大值的位置
    • 返回最大值的位置后,和真实值([batch_size])进行对比,相同表示预测成功。
def test():
    loss_list = []
    acc_list = []
    test_dataLoader = get_dataLoader(train=False,batch_size=TEST_BATCH_SIZE)
    for idx,(input,target) in enumerate(test_dataLoader):
        with torch.no_grad():
            output = model(input)
            cur_loss = F.nll_loss(output,target)
            loss_list.append(cur_loss)
            #计算准确率
            # output [batch_size] target:[batch_size]
            pred = output.max(dim=-1)[-1] #第一个-1表示在最后一个维度(行上)取得最大值,第二个-1表示同时输出对应位置
            cur_acc = pred.eq(target).float().mean()
            acc_list.append(cur_acc)
    print('平均准确率:',np.mean(acc_list),'平均损失',np.mean(loss_list))

Pytorch实现手写识别完整代码

'''
该算法的核心思想是通过对比训练值和测试值中的最大值是否相同,来评估该项目的好坏
'''
import numpy as np
import os
from torch.utils.data import DataLoader
from torchvision.transforms import Compose,ToTensor,Normalize
from torchvision.datasets import MNIST
import torch
import torch.nn.functional as F
import torch.nn as nn
from torch.optim import Adam

BATCH_SIZE = 128
TEST_BATCH_SIZE = 1000

#准备数据集
def get_dataLoader(train = True,batch_size = BATCH_SIZE):
    transform_fn = Compose([  # 定义数据处理函数,完成对数据的totorch处理和标准化
        ToTensor(),
        Normalize(mean=(0.1307,), std=(0.3081,))  # mean 和std的形状要和数据通道数相同
    ])
    dataset = MNIST(root='./data', train=True, transform=transform_fn)  # 设置数据集
    data_Loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)  # 设置加载器
    return data_Loader

#构建数据模型
class MnistModel(nn.Module):
    def __init__(self):
        super(MnistModel,self).__init__()
        # 定义f1方法,使用Linear方法,Linear(输入的形状,输出的形状),将输入的28*28输出为28
        self.fc1 = nn.Linear(in_features=28*28,out_features=28)
        # 将输入的28输出为10,因为预期输出是10个数字
        self.fc2 = nn.Linear(28,10)


    def forward(self,input):
        '''
        :param input: [batch_size,1,28*28] 我们获得的原始数据的样子,即input[0]=batchsize,input[1]=1,input[2]=28*28
        :return: 
        '''
        #更改形状,view函数相当于resize的功能,将原来的tensor变换成指的维度,input.size(0)指batchsize的值
        x = input.view(input.size(0),28*28) #这里实现把形状转化为[batch_size,28*28]
        # x = input.view(-1,28*28) #二者实现效果相同
        # x = input.view(input.size(0),-1)#实现效果与未注释的那句等价
        #进行全连接操作
        x = self.fc1(x)
        #使用激活函数处理数据,不会使形状发生变化
        x = F.relu(x)
        #输出层
        out = self.fc2(x)
        return F.log_softmax(out,dim=-1) #在最后一个维度上进行操作,dim是维度的意思

model = MnistModel()#实例化模型,设置模型为训练模式(默认)
optimizer = Adam(model.parameters(),lr=0.001)#实例化优化器模型
if os.path.exists('./model/model.pkl'):#判断路径是否存在
    model.load_state_dict(torch.load('./model/model.pkl'))
    optimizer.load_state_dict(torch.load('./model/optimizer.pkl'))

def train(epoch):#epoch 轮的意思
    '''实现训练的过程'''
    data_loader = get_dataLoader()#获取dataloader
    for idx,(input,target) in enumerate(data_loader):
        optimizer.zero_grad()#梯度置为零
        out_put = model(input)#进行前向计算,调用模型,得到预测值
        loss = F.nll_loss(out_put,target)#带权损失
        loss.backward()#反向传播(记得梯度置为0),计算梯度
        optimizer.step()#梯度更新
        # if idx%100 == 0:
        #     print(loss.item())
        #模型的保存
        if idx%100 ==0:
            torch.save(model.state_dict(),'./model/model.pkl')
            torch.save(optimizer.state_dict(), './model/optimizer.pkl')

def test():
    loss_list = []
    acc_list = []
    test_dataLoader = get_dataLoader(train=False,batch_size=TEST_BATCH_SIZE)
    for idx,(input,target) in enumerate(test_dataLoader):
        with torch.no_grad():
            output = model(input)
            cur_loss = F.nll_loss(output,target)
            loss_list.append(cur_loss)
            #计算准确率
            # output [batch_size] target:[batch_size]
            pred = output.max(dim=-1)[-1] #第一个-1表示在最后一个维度(行上)取得最大值,第二个-1表示同时输出对应位置
            cur_acc = pred.eq(target).float().mean()
            acc_list.append(cur_acc)
    print('平均准确率:',np.mean(acc_list),'平均损失',np.mean(loss_list))

if __name__ == '__main__':
    # for i in range(3):#训练三轮
    #     train(i)

    # loader = get_dataLoader(False)
    # for input,lable in loader:
    #     print(lable.size())
    #     break

    test()
    for i  in range(5):
        train(i)
        test()

更多Pytorch知识梳理,请参考: pytorch学习笔记

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

使用pytorch实现手写数字识别(代码片段)

使用Pytor##标题ch实现手写数字识别思路和流程分析准备数据,这些需要准备DataLoader构建模型,这里可以使用torch构造一个深层的神经网络模型的训练模型的保存,保存模型,后续持续使用模型的评估,使用测... 查看详情

图像分类基于pytorch搭建lstm实现mnist手写数字体识别(双向lstm,附完整代码和数据集)(代码片段)

...sdn.net/AugustMe/article/details/128969138文章中,我们使用了基于PyTorch搭建LSTM实现MNIST手写数字体识别,LSTM是单向的,现在我们使用双向LSTM试一试效果,和之前的单向LSTM模型稍微有差别,请注意查看代码的变化。1.导入依赖库这些依赖... 查看详情

pytorch实战|第p1周:实现mnist手写数字识别(代码片段)

🍨本文为🔗365天深度学习训练营中的学习记录博客🍦参考文章:365天深度学习训练营-第P1周:实现mnist手写数字识别(训练营内部成员可读)🍖原作者:K同学啊|接辅导、项目定制🏡我的... 查看详情

pytorch实战|第p1周:实现mnist手写数字识别(代码片段)

🍨本文为🔗365天深度学习训练营中的学习记录博客🍦参考文章:365天深度学习训练营-第P1周:实现mnist手写数字识别(训练营内部成员可读)🍖原作者:K同学啊|接辅导、项目定制🏡我的... 查看详情

365计划-1pytorch实现mnist手写数字识别(代码片段)

🍨本文为🔗365天深度学习训练营中的学习记录博客🍦参考文章地址:365天深度学习训练营-第P1周:mnist手写数字识别🍖作者:K同学啊###本项目来自K同学在线指导###importtorch.nnasnnimportmatplotlib.pyplotasplti... 查看详情

使用循环神经网络做手写数字识别(代码片段)

文章目录使用循环神经网络做手写数字识别思路分析PyTorch实现版权声明:本文为博主原创文章,转载请注明原文出处!写作时间:2019-03-0221:36:12使用循环神经网络做手写数字识别思路分析做图像识别的使用卷积神... 查看详情

ai常用框架和工具丨13.pytorch实现基于cnn的手写数字识别

代码实例,PyTorch实现基于CNN的手写数字识别,希望对您有所帮助。文章目录环境说明一、模型训练1.1导入相关依赖1.2选择使用的硬件1.3超参数配置1.4准备训练集和测试集1.5模型创建1.6模型评估指标1.7模型训练1.8模型测试1.9模型... 查看详情

ai常用框架和工具丨13.pytorch实现基于cnn的手写数字识别

代码实例,PyTorch实现基于CNN的手写数字识别,希望对您有所帮助。文章目录环境说明一、模型训练1.1导入相关依赖1.2选择使用的硬件1.3超参数配置1.4准备训练集和测试集1.5模型创建1.6模型评估指标1.7模型训练1.8模型测试1.9模型... 查看详情

ai常用框架和工具丨13.pytorch实现基于cnn的手写数字识别

代码实例,PyTorch实现基于CNN的手写数字识别,希望对您有所帮助。文章目录环境说明一、模型训练1.1导入相关依赖1.2选择使用的硬件1.3超参数配置1.4准备训练集和测试集1.5模型创建1.6模型评估指标1.7模型训练1.8模型测试1.9模型... 查看详情

[pytorch系列-29]:神经网络基础-全连接浅层神经网络实现10分类手写数字识别(代码片段)

作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:https://blog.csdn.net/HiWangWenBing/article/details/120607797目录前言深度学习模型框架第1章业务领域分析1.1 步骤1-1:... 查看详情

pytorch基于cnn的手写数字识别(在mnist数据集上训练)(代码片段)

最终成果http://pytorch-cnn-mnist.herokuapp.com/GITHUBhttps://github.com/XavierJiezou/pytorch-cnn-mnist本文以最经典的mnist数据集为例,讲述了使用pytorch做机器学习的一整套流程,文中所提到的所有代码都可以到github中查看。项目场景简单的... 查看详情

用pytorch实现多层感知机(mlp)(全连接神经网络fc)分类mnist手写数字体的识别(代码片段)

1.导入必备的包1importtorch2importnumpyasnp3fromtorchvision.datasetsimportmnist4fromtorchimportnn5fromtorch.autogradimportVariable6importmatplotlib.pyplotasplt7importtorch.nn.functionalasF8fromtorch.utils.da 查看详情

第三节,tensorflow使用cnn实现手写数字识别(代码片段)

上一节,我们已经讲解了使用全连接网络实现手写数字识别,其正确率大概能达到98%,着一节我们使用卷积神经网络来实现手写数字识别,其准确率可以超过99%,程序主要包括以下几块内容[1]:导入数据,即测试集和验证集[2]:引... 查看详情

pytorch入门实战|第p1周:实现mnist手写数字识别

查看详情

识别手写数字增强版-pytorch从入门到入道(代码片段)

1、快速开始1.1定义神经网络类,继承torch.nn.Module,文件名为digit_recog.py1importtorch.nnasnn234classNet(nn.Module):5def__init__(self):6super(Net,self).__init__()7self.conv1=nn.Sequential(nn.Conv2d(1,6,5,1,2)8,nn.ReLU()9, 查看详情

手把手教你使用labviewopencvdnn实现手写数字识别(含源码)(代码片段)

...型,但是它可以载入别的深度学习框架(例如TensorFlow、pytorch、Caffe等等)训练好的模型,然后使用该模型做inference(预测)。而且OpenCV在载入模型时会使用自己的DNN模块对模型重写,使得模型的运行效率更高。所以如果你想在Op... 查看详情

【pytorch+torchvision】mnist手写数字识别(代码附最详细注释)

参考技术APyTorch是一个非常流行的深度学习框架。但是与其他框架不同的是,PyTorch具有动态执行图,意味着计算图是动态创建的。如果在matplotlib使用上出错时,可加上会使错误消失,具体我也不知道是为什么,但是百度得到的... 查看详情

pytorch深度学习实践_p9_多分类问题_pytorch手写实现数字辨识(代码片段)

pytorch手写实现数字辨识知识点补充view()在PyTorch中view函数作用为重构张量的维度,相当于numpy中的resize()的功能torch.nn.CrossEntropyLoss()求交叉熵,并且其中嵌套了log和softmax函数所以i神经网络最后一层不用再用softmax激活torch.m... 查看详情