欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

[完整项目] 基于 Mnist 的手写数字识别 - Pytorch 版

最编程 2024-05-19 13:08:31
...

mnist数据集其实是机器学习的一个入门数据集,其训练集有60000张0-9的数字,测试集有10000张0-9的手写数字

MNIST 数据集来⾃美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来⾃

250 个不同⼈⼿写的数字构成, 其中 50% 是⾼中学⽣, 50% 来⾃⼈⼝普查局 (the Census Bureau) 的⼯作⼈员. 测试集(test set) 也是同

样⽐例的⼿写数字数据.

一.首先我们先使用pytorch读取mnist中的图片

代码语言:javascript
复制
from torchvision.datasets import MNIST
from torchvision import transforms
import numpy

import matplotlib.pyplot as plt

mnist_train=MNIST(root="mnist",train=True,download=False,transform=transforms.ToTensor())

image,label=mnist_train[0]
image=image.numpy().transpose(1,2,0)
plt.imshow(image)
plt.show()
print("标签是",label)

关于图片转置的代码也可以换成下面两种解决办法:

代码语言:javascript
复制
image=image.reshape(28,28)

或者

代码语言:javascript
复制
image=numpy.squeeze(image)

mnist的像素都是28*28的,所以最原始的image.shape是这样的:

代码语言:javascript
复制
torch.Size([1, 28, 28])

转置之后或者说预处理之后,就成为:

代码语言:javascript
复制
torch.Size([28, 28])

这样方便我们用matplotlib绘图

关于以上代码还有一处比较重要的是第7行的数据加载类

代码语言:javascript
复制
MNIST(root="mnist",train=True,download=False,transform=transforms.ToTensor())

MNSIT是torchvision提供的一个mnist的数据集的加载类

我们可以在官网看到关于这个加载类的详细信息

-root 是数据集下载的目录

-train 若为True则使用或者下载训练数据集,若为False则会使用测试数据集

-download 如果为True则会自动将Mnist数据集下载到root变量中的路径

-transform 转换器,复制将图片转换为我们需要的形式,例如tensor

二.训练模型

我们本次训练使用Adam优化器,由于Adam优化器的学习率默认是0.001 也即1e-3,所以我们可以直接省略lr的参数值的传递

模型选择:双层全连接层加上Relu激活函数

损失函数:交叉熵损失

数据加载使用torchvision提供的Dataloader类

代码语言:javascript
复制
from torchvision.datasets import MNIST
import torchvision.transforms as transforms
import numpy as np
import torch.nn as nn
from torch.utils.data import DataLoader
from torch import optim
from tqdm import tqdm
from torch import save,load
import os
from mn import test



class MnistModel(nn.Module):
    def __init__(self):
        super(MnistModel,self).__init__()
        self.fc1=nn.Linear(1*28*28,100)
        self.relu=nn.ReLU()
        self.fc2=nn.Linear(100,10)
    def forward(self, image):
        image_viewed=image.view(-1,1*28*28)
        out_1=self.fc1(image_viewed)
        fc1=self.relu(out_1)
        out_2=self.fc2(fc1)
        return out_2


model=MnistModel()

optimizer=optim.Adam(model.parameters())

loss_fun=nn.CrossEntropyLoss()

my_transforms=transforms.Compose(
    [
        transforms.ToTensor(),
        transforms.Normalize(mean=(0.3107,),std=(0.3081,))
    ]
)

mnist_train=MNIST(root="mnist",train=True,download=True,transform=my_transforms)


def train(epoch):
    total_loss = []
    dataloader=DataLoader(mnist_train,batch_size=8,shuffle=True)

    dataloader=tqdm(dataloader,total=len(dataloader))

    model.train()
    for (images,lebels) in dataloader:
        optimizer.zero_grad()
        output=model(images)
        loss=loss_fun(output,lebels)
        total_loss.append(loss.item())
        loss.backward()
        optimizer.step()

    save(model.state_dict(),'./models/model.pkl')
    save(optimizer.state_dict(),'./models/optimizer.pkl')
    print("第{}个epoch训练的准确率:".format(epoch),test.test_success())
    print("loss:{}".format(np.mean(total_loss)))



for i in range(10):
    train(i)

epoch为10 所以我们训练十轮

关于37行代码的均值和方差是通过借鉴各种大佬的数值。

这里我们引用了tqdm这个库

一个进度条的python第三方库,这样我们在训练的时候就可以看到训练的进度了。

关于50行的train,我在****上找到了类似的说法

我们写了这一句其实是开启了batchnormalization和drop,这样可以在每一轮的训练中用到我们给的方差和均值

关于51至57行的代码,是机器学习的经典三剑客,就不过多赘述了

然后我们开始训练,因为使用了tqdm库所以会有一个可视化的进度条

第一轮的准确率不是很理想,因为我们刚刚开始训练,平均损失值也不是很低

但是第3轮的训练就好一些了

loss在下降 accuracy在上升

最终的结果还算满意吧

93%的准确率和 0.043的loss

然后我们换到测试数据集上

再测试一下:

在测试的代码中我们有两点需要注意

  1. 在训练的时候我们写了一行model.train()的代码,与之相反,在测试中我们就需要写成model.eval() 关于eval 方法我也在****上找到了相关的说法:

也就是说在测试的时候需要写eval这条语句也就是表明了,现在是测试的状态,我们不需要再去训练,直接使用训练好的模型和参数即可。

2.在MNIST类中将train参数的值置为False,这样就自动使用了测试数据集

代码语言:javascript
复制
mnist_test = MNIST(root="mnist", train=False, download=True, transform=my_transforms)

三.测试自己的手写图片

条件有限,我们就上美图秀秀新建一个28*28像素的黑色画布,然后使用画笔写一个数字,然后保存到本地,然后编写对应的测试代码即可实现预测

识别成功,bingo,大功告成