Pytorch基础-04-实战手写数字识别

我们现在已经学会了基于PyTorch深度学习框架高效、快捷地搭建一个神经网络,并对模型进行训练和对参数进行优化的方法,接下来让我们小试牛刀,基于PyTorch框架使用神经网络来解决一个关于手写数字识别的计算机视觉问题,评价我们搭建的模型的标准是它能否准确地对手写数字图片进行识别。

其具体过程是:先使用已经提供的训练数据对搭建好的神经网络模型进行训练并完成参数优化;然后使用优化好的模型对测试数据进行预测,对比预测值和真实值之间的损失值,同时计算出结果预测的准确率。在将要搭建的模型中会用到卷积神经网络模型,下面让我们开始吧。

  • 1. torchvision.datasets

torchvision包的主要功能是实现数据的处理、导入和预览等,所以如果需要对计算机视觉的相关问题进行处理,就可以借用在torchvision包中提供的大量的类来完成相应的工作。

使用 torchvision.datasets可以轻易实现对这些数据集的训练集和测试集的下 载,只需要使用 torchvision.datasets再加上需要下载的数据集的名称就可 以了,比如在这个问题中我们要用到手写数字数据集,它的名称是 MNIST,那么实现下载的代码就是torchvision.datasets.MNIST。其他常 用的数据集如COCO、ImageNet、CIFCAR等都可以通过这个方法快速 下载和载入。代码如下:

import torch
import torchvision
from torchvision import datasets,transforms
from torch.autograd import Variable
transform = transforms.Compose(
   [transforms.ToTensor(),transforms.Normalize(mean=[0.5],std=[0.5])]
)
data_train = datasets.MNIST(
    root = './data/',
    transform = transform,
    train = True,
    download = True
)
data_test = datasets.MNIST(
    root = './data/',
    transform = transform,
    train = False
)

其中,root用于指定数据集在下载之后的存放路径,这里存放在根 目录下的data文件夹中;transform用于指定导入数据集时需要对数据进 行哪种变换操作,在后面会介绍详细的变换操作类型,注意,要提前定 义这些变换操作;train用于指定在数据集下载完成后需要载入哪部分数据,如果设置为True,则说明载入的是该数据集的训练集部分;如果设置为False,则说明载入的是该数据集的测试集部分。

  • 2. torch.transforms

在前面讲到过,在torch.transforms中提供了丰富的类对载入的数据 进行变换,现在让我们看看如何进行变换。我们知道,在计算机视觉中 处理的数据集有很大一部分是图片类型的,而在PyTorch中实际进行计 算的是Tensor数据类型的变量,所以我们首先需要解决的是数据类型转 换的问题,如果获取的数据是格式或者大小不一的图片,则还需要进行 归一化和大小缩放等操作,庆幸的是,这些方法在torch.transforms中都能找到。

在torch.transforms中有大量的数据变换类,其中有很大一部分可以用于实现数据增强(Data Argumentation)。若在我们需要解决的问题上能够参与到模型训练中的图片数据非常有限,则这时就要通过对有限的图片数据进行各种变换,来生成新的训练集了,这些变换可以是缩小或者放大图片的大小、对图片进行水平或者垂直翻转等,都是数据增强的方法。不过在手写数字识别的问题上可以不使用数据增强的方法,因为可用于模型训练的数据已经足够了。对数据进行载入及有相应变化的代码如下:

transform = transforms.Compose(
   [transforms.ToTensor(),transforms.Normalize(mean=[0.5],std=[0.5])]
)

torchvision.transforms.Compose中只使用了一个类型的转换变换transforms.ToTensor和一个数据标准化变换transforms.Normalize。这里使用的标准化变换也叫作标准差变换法,这种方法需要使用原始数据的均值(Mean)和标准差(StandardDeviation)来进行数据的标准化,在经过标准化变换之后,数据全部符合均值为0、标准差为1的标准正态分布。

下面看看在torchvision.transforms中常用的数据变换操作。
(1)torchvision.transforms.Resize:用于对载入的图片数据按我们需求的大小进行缩放。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列,其中,h代表高度,w代表宽度,但是如果使用的是一个整型数据,那么表示缩放的宽度和高度都是这个整型数据的值。
(2)torchvision.transforms.Scale:用于对载入的图片数据按我们 需求的大小进行缩放,用法和torchvision.transforms.Resize类似。
(3)torchvision.transforms.CenterCrop:用于对载入的图片以图 片中心为参考点,按我们需要的大小进行裁剪。传递给这个类的参数可 以是一个整型数据,也可以是一个类似于(h,w)的序列。
(4)torchvision.transforms.RandomCrop:用于对载入的图片按 我们需要的大小进行随机裁剪。传递给这个类的参数可以是一个整型数 据,也可以是一个类似于(h,w)的序列。
(5)torchvision.transforms.RandomHorizontalFlip:用于对载入 的图片按随机概率进行水平翻转。我们可以通过传递给这个类的参数自 定义随机概率,如果没有定义,则使用默认的概率值0.5。
(6)torchvision.transforms.RandomVerticalFlip:用于对载入的 图片按随机概率进行垂直翻转。我们可以通过传递给这个类的参数自定 义随机概率,如果没有定义,则使用默认的概率值0.5。
(7)torchvision.transforms.ToTensor:用于对载入的图片数据进 行类型转换,将之前构成PIL图片的数据转换成Tensor数据类型的变 量,让PyTorch能够对其进行计算和处理。
(8)torchvision.transforms.ToPILImage:用于将Tensor变量的数 据转换成PIL图片数据,主要是为了方便图片内容的显示。

  • 3.数据预览和数据装载

在数据下载完成并且载入后,我们还需要对数据进行装载。我们可 以将数据的载入理解为对图片的处理,在处理完成后,我们就需要将这 些图片打包好送给我们的模型进行训练了,而装载就是这个打包的过 程。在装载时通过batch_size的值来确认每个包的大小,通过shuffle的值 来确认是否在装载的过程中打乱图片的顺序。装载图片的代码如下:

data_loader_train = torch.utils.data.DataLoader(
    dataset=data_train,
    batch_size = 16,
    shuffle = True
)
data_loader_test = torch.utils.data.DataLoader(
    dataset= data_test,
    batch_size = 16,
    shuffle = True
)

对数据的装载使用的是torch.utils.data.DataLoader类,类中的dataset参数用于指定我们载入的数据集名称,batch_size参数设置了每个包中的图片数据个数,代码中的值是64,所以在每个包中会包含16张图片。将shuffle参数设置为True,在装载的过程会将数据随机打乱顺序并进行打包。

在装载完成后,我们可以选取其中一个批次的数据进行预览。进行数据预览的代码如下:

import matplotlib.pyplot as plt
images,labels = next(iter(data_loader_train))
img = torchvision.utils.make_grid(images)
#(channel,height,width)转为(height,width,channel)
img = img.numpy().transpose(1,2,0)
std = [0.5]
mean = [0.5]
img = img*std + mean
print ([labels[i].item() for i in range(16)])
plt.imshow(img)

[5, 2, 7, 0, 0, 2, 9, 9, 5, 0, 1, 7, 7, 9, 5, 4]
在这里插入图片描述

在以上代码中使用了iter和next来获取一个批次的图片数据和其对应的图片标签,然后使用torchvision.utils中的make_grid类方法将一个批次的图片构造成网格模式。需要传递给torchvision.utils.make_grid的参数就是一个批次的装载数据,每个批次的装载数据都是4维的,维度的构成从前往后分别为batch_size、channel、height和weight,分别对应一个批次中的数据个数、每张图片的色彩通道数、每张图片的高度和宽度。在通过torchvision.utils.make_grid之后,图片的维度变成了(channel,height,weight),这个批次的图片全部被整合到了一起,所以在这个维度中对应的值也和之前不一样了,但是色彩通道数保持不变。
若我们想使用Matplotlib将数据显示成正常的图片形式,则使用的 数据首先必须是数组,其次这个数组的维度必须是(height,weight,channel),即色彩通道数在最后面。所以我们要通过numpy和transpose完成原始数据类型的转换和数据维度的交换,这样才 能够使用Matplotlib绘制出正确的图像。

  • 4.模型搭建和参数优化

实现卷积神经网络模型搭建的代码如下:

class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(stride=2, kernel_size=2)
        )

        self.dense = torch.nn.Sequential(
            torch.nn.Linear(14 * 14 * 128, 1024),
            torch.nn.ReLU(),
            torch.nn.Dropout(0.5),
            torch.nn.Linear(1024, 10)
        )

    def forward(self, x):
        x = self.conv1(x)
        x = x.view(-1, 14 * 14 * 128)
        x = self.dense(x)
        return x

(1)torch.nn.Conv2d:用于搭建卷积神经网络的卷积层,主要的输入参数有输入通道数、输出通道数、卷积核大小、卷积核移动步长和Paddingde值。其中,输入通道数的数据类型是整型,用于确定输入数据的层数;输出通道数的数据类型也是整型,用于确定输出数据的层数;卷积核大小的数据类型是整型,用于确定卷积核的大小;卷积核移动步长的数据类型是整型,用于确定卷积核每次滑动的步长;Paddingde 的数据类型是整型,值为0时表示不进行边界像素的填充,如果值大于0,那么增加数字所对应的边界像素层数。
(2)torch.nn.MaxPool2d:用于实现卷积神经网络中的最大池化 层,主要的输入参数是池化窗口大小、池化窗口移动步长和Paddingde 值。同样,池化窗口大小的数据类型是整型,用于确定池化窗口的大 小。池化窗口步长的数据类型也是整型,用于确定池化窗口每次移动的 步长。Paddingde值和在torch.nn.Conv2d中定义的Paddingde值的用法和意 义是一样的。
(3)torch.nn.Dropout类用于防止卷积神经网络 在训练的过程中发生过拟合,其工作原理简单来说就是在模型训练的过 程中,以一定的随机概率将卷积神经网络模型的部分参数归零,以达到 减少相邻两层神经连接的目的。对于torch.nn.Dropout类,我 们可以对随机概率值的大小进行设置,如果不做任何设置,就使用默认 的概率值0.5。

最后看看代码中前向传播 forward函数中的内容。首先,经过 self.conv1进行卷积处理;然后进行x.view(-1, 1414128),对参数实现 扁平化,因为之后紧接着的就是全连接层,所以如果不进行扁平化,则 全连接层的实际输出的参数维度和其定义输入的维度将不匹配,程序会报错;最后,通过self.dense定义的全连接进行最后的分类。

在编写完搭建卷积神经网络模型的代码后,我们就可以开始对模型进行训练和对参数进行优化了。首先,定义在训练之前使用哪种损失函数和优化函数:

model = Model()
cost = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
print (model)

Model(
(conv1): Sequential(
(0): Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU()
(2): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(3): ReLU()
(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(dense): Sequential(
(0): Linear(in_features=25088, out_features=1024, bias=True)
(1): ReLU()
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=1024, out_features=10, bias=True)
)
)

最后,卷积神经网络模型进行模型训练和参数优化的代码如下:

n_epochs = 5

for epoch in range(n_epochs):
    running_loss = 0.0
    running_correct = 0
    print('Epoch{}/{}'.format(epoch, n_epochs))
    print('--' * 40)
    for data in data_loader_train:
        x_train, y_train = data
        x_train, y_train = Variable(x_train), Variable(y_train)
        outputs = model.forward(x_train)
        _, pred = torch.max(outputs.data, 1)
        optimizer.zero_grad()
        loss = cost(outputs, y_train)

        loss.backward()
        optimizer.step()
        running_loss += loss.data
        running_correct += torch.sum(pred == y_train.data)
    testing_correct = 0
    for data in data_loader_test:
        x_test, y_test = data
        x_test, y_test = Variable(x_test), Variable(y_test)
        outputs = model.forward(x_test)
        _, pred = torch.max(outputs.data, 1)
        testing_correct += torch.sum(pred == y_test.data)
    print('Loss is {},Train Accuarcy is {},Test Accuarcy is {}'.format(running_loss / len(data_train),
                                                                       100 * running_correct / len(data_train),
                                                                       100 * testing_correct / len(data_test)))

总的训练次数是5次,训练中的大部分代码和之前相比没有大的改 动,增加的内容都在原来的基础上加入了更多的打印输出,其目的是更 好地显示模型训练过程中的细节,同时,在每轮训练完成后,会使用测 试集验证模型的泛化能力并计算准确率。

训练速度真的慢!!!

如果朋友们有需要,可以去这个链接查看,另一种代码写法:https://blog.csdn.net/Galen_xia/article/details/108689164

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值