Pytorch手写数字识别

目录

一.数据处理知识

1.torchvision.transforms方法

2.torchvision.transforms.Normalize:

3.torchvision.transforms.Compose:

二.准备数据

三.构建模型

1.模型形状

2.激活函数

3.模型中的数据形状

四.模型的损失函数

五.模型的训练

六.模型的保存和加载

七.模型的加载

八.模型评估

九.完整代码:

都看到这里了,点个赞把!!!


一.数据处理知识

使用torch中自带的数据MNIST,里面的数据是图形数据,返回一个image对象

MNIST数据是图片中有1-9的数字,而我们要做的就是训练一个模型来识别这些数字

1.torchvision.transforms方法

这个方法是处理图形数据的,里面主要有:

torchvision.transforms.ToTensor:把一个取值范围为[0,255]的PIL.Image或者shape为(H,W,C)的numpy.ndarray转化为[C,H,W]。其中H代表高,W代表宽,C代表通道数(一个通道代表一种颜色,黑白的就一个通道就可用代表了,而彩色的要三个通道RGB来表示)。

from torchvision import transforms
import numpy as np

# 生成12个在0-255之间的数据
data=np.random.randint(0,255,size=12)
# 将数据形状改变成三维的
img=data.reshape(2,2,3)
print(img)
# 通过ToTensor改变后
img_tensor=transforms.ToTensor()(img)
print(img_tensor)
# 运行后可用看到本来的(2,2,3)变成了(3,2,2)

2.torchvision.transforms.Normalize:

规范化处理,给定均值mean,shape,图片的通道数相同(指每个通道的均值相同),方差:std,和图片的通道数相同(指的是每个通道的方差),那么就对Tensor规范化处理。也就是将这个图片中的每个像素点的数据减去均值比上方差,与机器学习中的规则化一样。

from torchvision import transforms
import numpy as np
import torchvision

# 生成12个在0-255之间的数据
data=np.random.randint(0,255,size=12)
# 将数据形状改变成三维的
img=data.reshape(2,2,3)
print(img)
# 通过transforms改变后
img_tensor=transforms.ToTensor()(img)
print(img_tensor)
# 运行后可用看到本来的(2,2,3)变成了(3,2,2)

# Normalize中第一个参数传的是每一列的平均值,第二个是方差
norm_img=transforms.Normalize((10,10,10),(1,1,1))(img_tensor.float())
print(norm_img)

上面代码中平均值和方差需要自己计算,去对应的平均值和方差就可用了,比如平均值是所有的平均值,那么方差也要所有数的方差,如果是一列的那么都是一列的。

3.torchvision.transforms.Compose:

这个的作用就是将多个transforms方法组合起来,比如将上面的1和2方法组合

二.准备数据

准备数据集,其中0.1307,0.3081是MNIST数据的均值和方差

import torch
import torchvision

# 导入数据,train=True表示这是训练集,如果False表示测试集。download表示是否下载
dataset=torchvision.datasets.MNIST('/data',train=True,download=True)
# 将规范化和数据转换合并为一个方法
transform=torchvision.transforms.Compose([
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize((0.1307,),(0.3081,))
])

# 准备数据迭代器
train_dataloader=torch.utils.data.DataLoader(dataset,batch_size=64,shuffle=True)

三.构建模型

1.模型形状

这里构建一个简单的三层神经网络,其中包括两个全连接层和一个输出层,第一个全连接层会经过激活函数的处理,将处理后的结果交到下一个全连接层。

2.激活函数

常见的激活函数有:Relu,sigmoid等,这里选用Relu,因为这里是多分类问题,选这个简单。

Relu激活函数由torch.nn.functional提供,函数原理是将小于0变成0,大于0的不变

3.模型中的数据形状

1.原始的输出形状为[batch_size,1,28,28]----------[样本个数,通道数,宽,高]

2.进行形状修改:[batch_size,28*28],(全连接层要进行矩阵乘法,神经网络介绍哪里讲过。比如由28个数,因为是全链接所以一个要乘全部,所以就成了28*28个了)

3.第一个全连接层的输出形状是[batch_size,28]这里的28是自己设定的。

4.使用激活函数不会修改数据的形状

5.第二个全连接层的输出形状[batch_size,10]因为这个数据里由十个类别,对应十个数字0-9

import torch
from torch import nn
import torch.nn.functional as F
import torchvision

# 导入数据,train=True表示这是训练集,如果False表示测试集。download表示是否下载
dataset=torchvision.datasets.MNIST('/data',train=True,download=True)
# 将规范化和数据转换合并为一个方法
transform=torchvision.transforms.Compose([
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize((0.1307,),(0.3081,))
])

# 准备数据迭代器
train_dataloader=torch.utils.data.DataLoader(dataset,batch_size=64,shuffle=True)

class MnistNet(nn.Module):
    def __init__(self):
        super(MnistNet,self).__init__()
        self.fc1=nn.Linear(28*28*1,28)  # 定义第一层的w和b输入和输出形状
        self.fc1=nn.Linear(28,10)  # 定义第二层的w和b的输入和输出形状

    def forward(self,x):
        x=x.view(-1,28*28*1)  # 该表数据形状,-1表示该位置根据后面的形状自动调整
        x=self.fc1(x)  # 将数据传入后第一层对这个数据进行计算后输出为形状[batch_size,28]
        x=F.relu(x)  # 激活函数
        x=self.fc2(x)  # 将激活函数处理后的数据放到第二层中处理后就得到[batch_size,10]的结果了

可以发现,pytorch在构建模型的时候形状上并不会考虑batch_size(样本数量)

四.模型的损失函数

因为是多分类问题,所以这里使用sotfmax

五.模型的训练

1.实例话模型,蛇者模型为训练模式

2.实例话优化器类,实例话损失函数

3.获取,遍历dataloader

4.梯度置0

5.进行向前计算

6.计算损失

7.反向传播

8.更新参数

# 模型对象
mnist_net = MnistNet()
# 实例化优化器类
optimizer = optim.Adam(mnist_net.parameters(), lr=0.001)

# 创建一个训练函数
def train(epoch):
    # 设置一个bool值来判断是否是训练
    mode = True
    mnist_net.train(mode=mode)  # 模型设置为训练模型
    
    train_dataloader = get_dataloader(train=mode)  # 获取训练数据集
    # 循环迭代
    for idx, (data, target) in enumerate(train_dataloader):
        optimizer.zero_grad()  # 梯度置为0
        output = mnist_net(data)  # 进行向前计算
        loss = F.nll_loss(output, target)  # 计算出带权损失
        loss.backward()  # 进行反向传播,计算梯度
        optimizer.step()  # 参数更新
        # 打印看一下损失变化
        if idx % 10 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, idx * len(data), len(train_dataloader.dataset),
                       100. * idx / len(train_dataloader), loss.item()))

六.模型的保存和加载

# torch自带的save保存函数,第一个参数为需要保存的数据,第二个参数为保存的路径
torch.save(mnist_net.state_dict(),"保存的路径/文件名") #保存模型参数
torch.save(optimizer.state_dict(), '保存的路径/文件名') #保存优化器参数

七.模型的加载

# 因为模型继承了nn.Module,里面有一个方法load_state_dict,传入文件路线即可,注意路径要用torch.load修饰一下
mnist_net.load_state_dict(torch.load("模型参数的文件路径"))
optimizer.load_state_dict(torch.load("优化器的文件路径"))

八.模型评估

1.不需要计算梯度,因为我们只需要计算出预测值和误差就可用对模型评估了

2.准确率的计算:因为我们使用的损失函数是sotfmax,那么最大值就是预测值

3.torch中提供了一个获取最大值的方法torch.max,里面传入数据和一个参数keepdim,=0表示每列最大值,1表示每行最大值,输出返回最大值以及对应的索引值。因此,predict = torch.max(outputs.data, 1)[1] 是计算模型中每个类别的最大值并返回其索引值,即该类别的标签值。

4.找到最大值后与真实值对比

def test():
    # 创建一个变量来记录平均损失
    test_loss = 0
    # 记录预测样本数
    correct = 0
    mnist_net.eval()  #设置模型为评估模式
    test_dataloader = get_dataloader(train=False) #获取测试数据
    with torch.no_grad(): #不计算其梯度
        for data, target in test_dataloader: # 遍历数据和真实值
            output = mnist_net(data)  # 得到我们训练后模型的预测值
            test_loss += F.nll_loss(output, target, reduction='sum').item() # 计算本次的误差
            pred = output.data.max(1, keepdim=True)[1] #获取最大值的位置,[batch_size,1]
            correct += pred.eq(target.data.view_as(pred)).sum()  #预测准备样本数累加
    test_loss /= len(test_dataloader.dataset) #计算平均损失
    print('\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
        test_loss, correct, len(test_dataloader.dataset),
        100. * correct / len(test_dataloader.dataset)))

九.完整代码:

注意要加入文件路径

import torch
from torch import nn, optim
import torch.nn.functional as F
import torchvision

# 创建一个训练集中一组数据中的样本数量,比如一组64张图片
train_batch_size = 64
# 创建一个测试集的样本数量
test_batch_size = 1000
img_size = 28

# 构建一个加载参数和优化器类的方法
def get_dataloader(train=True):
    assert isinstance(train,bool),"train 必须是bool类型"
    # 获取训练数据
    dataset = torchvision.datasets.MNIST('/data', train=train, download=True,
                                         transform=torchvision.transforms.Compose([
                                         torchvision.transforms.ToTensor(),
                                         torchvision.transforms.Normalize((0.1307,), (0.3081,)),]))
    #准备数据迭代器
    batch_size = train_batch_size if train else test_batch_size
    dataloader = torch.utils.data.DataLoader(dataset,batch_size=batch_size,shuffle=True)
    return dataloader

# 模型类
class MnistNet(nn.Module):
    def __init__(self):
        super(MnistNet,self).__init__()
        self.fc1=nn.Linear(28*28*1,28)  # 定义第一层的w和b输入和输出形状
        self.fc1=nn.Linear(28,10)  # 定义第二层的w和b的输入和输出形状

    def forward(self,x):
        x=x.view(-1,28*28*1)  # 该表数据形状,-1表示该位置根据后面的形状自动调整

        x=self.fc1(x)  # 将数据传入后第一层对这个数据进行计算后输出为形状[batch_size,28]

        print(x)
        x=self.fc2(x)  # 将激活函数处理后的数据放到第二层中处理后就得到[batch_size,10]的结果了
        return F.log_softmax(x,dim=-1)

# 模型对象
mnist_net=MnistNet()
# 实例化优化器类
optimizer=optim.Adam(mnist_net.parameters(),lr=0.001)
# 训练集的损失列表
train_loss_list=[]
def train(epoch):
    # 设置一个bool值来决定是训练还是测试
    mode = True
    mnist_net.train(mode=mode)
    train_dataloader = get_dataloader(train=mode) #得到训练数据迭代器
    print(len(train_dataloader.dataset))
    print(len(train_dataloader))
    for idx,(data,target) in enumerate(train_dataloader):
        optimizer.zero_grad()  # 将梯度置为0
        output = mnist_net(data) # 接收这次的训练后的数据
        loss = F.nll_loss(output,target) #对数似然损失
        loss.backward()  # 反向传播计算梯度
        optimizer.step() # 跟新参数
        if idx % 10 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, idx * len(data), len(train_dataloader.dataset),
                       100. * idx / len(train_dataloader), loss.item()))
            # 保存这次的损失
            train_loss_list.append(loss.item())
            # 保存本次的参数数据w和b等等
            torch.save(mnist_net.state_dict(),"文件路径/文件名")
            # 保存优化器类
            torch.save(optimizer.state_dict(), '文件路径/文件名')
# 模型评估方法
def test():
    # 创建一个变量来记录平均损失
    test_loss = 0
    # 记录预测样本数
    correct = 0
    mnist_net.eval()  #设置模型为评估模式
    test_dataloader = get_dataloader(train=False) #获取测试数据
    with torch.no_grad(): #不计算其梯度
        for data, target in test_dataloader: # 遍历数据和真实值
            output = mnist_net(data)  # 得到我们训练后模型的预测值
            test_loss += F.nll_loss(output, target, reduction='sum').item() # 计算本次的误差
            pred = output.data.max(1, keepdim=True)[1] #获取最大值的位置,[batch_size,1]
            correct += pred.eq(target.data.view_as(pred)).sum()  #预测准备样本数累加
    test_loss /= len(test_dataloader.dataset) #计算平均损失
    print('\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
        test_loss, correct, len(test_dataloader.dataset),
        100. * correct / len(test_dataloader.dataset)))

if __name__ == '__main__':

    test()
    for i in range(5): # 这个循环代表模拟几轮,因为每次的参数都是保存读取的,所以模拟的越多越精确
        train(i)
        test()

都看到这里了,点个赞把!!!

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值