Pytorch实现手写数字识别【基于全连接神经网络】

使用pytorch实现手写数字识别的主要步骤如下:

(1) 导入需要的各类包

(2) 定义代码中用到的各个超参数

(3) 对数据进行预处理

(4) 下载和分批加载数据集

(5) 利用nn工具箱构建神经网络模型,实例化模型,并定义损失函数及优化器

(6) 对模型进行训练

(7) 运用训练好的模型在测试集上检验效果

(8) 通过可视化的方法输出模型性能结果

神经网络结构设计如下:
四层神经网络:输入层 + 隐藏层1+ 隐藏层2 + 输出层
(实际上我们激活函数均使用的ReLU)

Pytorch实现手写数字识别【基于全连接神经网络】

; 前言

MNIST数据集

torchvision提供的mnist数据集几乎是每个深度学习新手的入门数据集,所以我们先来了解一下它:
MNIST 包括6万张图像和标签的训练集,1万张图像和标签的测试集,每张为28×28大小的灰度图片(784个像素点,每个点用一个浮点数表示其亮度),其中包含一个0-9的数字。我们的任务就是训练一个模型尽可能的准确识别出图像中的数字。

案例说明

本次案例仅简单设计了全连接层,并不包含卷积层、池化层等,因此最终的识别准确率相对来说没有那么高,这样做对于新手来说,一来可以及时巩固神经网络的学习成果,二来可以简化模型复杂度而更加注重了解一个实际项目的完整工作流程,三来也可以后续在此模型基础上添加卷积池化等,进一步感受模型性能的提升。

注: 案例中包含大量注释以便理解其含义

1、导入各类需要的包

import torch
import numpy as np

from torchvision.datasets import mnist

import torchvision.transforms as transforms

from torch.utils.data import DataLoader

from torch import nn

import torch.nn.functional as F

import torch.optim as optim

import matplotlib.pyplot as plt

2、定义超参数


train_batch_size = 64
test_batch_size = 128
num_epoches = 20
lr = 0.01
momentum = 0.5

3、预处理数据

由于pytorch读取数据集minst中的图像时默认使用python中的PIL,所以我们首先需要把PIL图像转化为更加适合pytorch计算使用的图像张量,其次需要把原始0 ~ 255之间的像素值通过归一化处理成0 ~ 1之间的值,这两步预处理的目的都是欲使数据在神经网络中运算更高效。

transforms.ToTensor()
作用就是将PIL中28×28的灰度图像转化为tensor张量其维度为1x28x28(CxWxH)其中1的含义为单通道(彩色图像时调整为三通道)

transforms.Normalize([0.1307], [0.3081])
使用minst数据集的均值和标准差将数据标准化处理

Pytorch实现手写数字识别【基于全连接神经网络】

transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.1307], [0.3081])])

4、加载数据集

之所以要分批加载数据集,是因为虽然我们的mnist数据集只有几十兆完全可以一次性加载进内存以供训练模型使用,但是当我们的训练的模型需要的数据集大小远超我们内存大小时,分批加载数据就可以解决这一问题。


train_dataset = mnist.MNIST('.\data', train=True, transform=transform, download=True)
test_dataset = mnist.MNIST('.\data', train=False, transform=transform,download=True)

train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)

5、定义神经网络模型并实例化


class Net(nn.Module):
    def __init__(self,in_dim,n_hidden_1,n_hidden_2,out_dim):
        super(Net,self).__init__()
        self.layer1=nn.Sequential(nn.Linear(in_dim,n_hidden_1),nn.ReLU(True))
        self.layer2=nn.Sequential(nn.Linear(n_hidden_1,n_hidden_2),nn.ReLU(True))
        self.layer3=nn.Linear(n_hidden_2,out_dim)
    def forward(self,x):
        x=self.layer1(x)
        x=self.layer2(x)
        x=self.layer3(x)
        return x

device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

model=Net(28*28,300,100,10)

model.to(device)

criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameters(),lr=lr,momentum=momentum)

其中:CrossEntropyLoss() == LogSoftmax() + NLLLoss()
Softmax: 在K分类问题中,此函数运算公式如下所示,可将K个输入值经过运算后得到的K个输出值拥有两个特性:(1)每个输出都大于0(2)K个输出之和等于1,由此K个输出即可代表每个分类的概率大小。

Pytorch实现手写数字识别【基于全连接神经网络】

NLLLoss: 负对数似然损失函数,公式如下所示,其中Y帽即为Softmax中输出的概率,Y为图像真实标签值

Pytorch实现手写数字识别【基于全连接神经网络】

6、模型训练及测试


losses = []
acces = []
eval_losses = []
eval_acces = []

for epoch in range(num_epoches):
    train_loss = 0
    train_acc = 0
    model.train()

    if epoch%5==0:
        optimizer.param_groups[0]['lr'] *= 0.9
    for img, label in train_loader:
        img = img.to(device)
        label = label.to(device)
        img = img.view(img.size(0), -1)

        out = model(img)
        loss = criterion(out, label)

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

        train_loss += loss.item()

        _, pred = out.max(1)

        num_correct = (pred == label).sum().item()

        acc = num_correct / img.shape[0]
        train_acc += acc

    losses.append(train_loss / len(train_loader))
    acces.append(train_acc / len(train_loader))

    eval_loss = 0
    eval_acc = 0

    model.eval()

    for img, label in test_loader:
        img=img.to(device)
        label = label.to(device)
        img = img.view(img.size(0), -1)
        out = model(img)
        loss = criterion(out, label)

        eval_loss += loss.item()

        _, pred = out.max(1)
        num_correct = (pred == label).sum().item()
        acc = num_correct / img.shape[0]
        eval_acc += acc

    eval_losses.append(eval_loss / len(test_loader))
    eval_acces.append(eval_acc / len(test_loader))

    print('epoch: {}, Train Loss: {:.4f}, Train Acc: {:.4f}, Test Loss: {:.4f}, Test Acc: {:.4f}'
          .format(epoch, train_loss / len(train_loader), train_acc / len(train_loader),
                     eval_loss / len(test_loader), eval_acc / len(test_loader)))

训练结果如下:

Pytorch实现手写数字识别【基于全连接神经网络】

; 7、模型训练结果可视化


plt.title('train loss')
plt.plot(np.arange(len(losses)), losses)
plt.legend(['Train Loss'], loc='best')

输出结果如下:

如下图所示,比较直观的看出模型的损失函数趋于收敛

Pytorch实现手写数字识别【基于全连接神经网络】

Original: https://blog.csdn.net/weixin_44851176/article/details/125858319
Author: consult_
Title: Pytorch实现手写数字识别【基于全连接神经网络】

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/706401/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球