在PyTorch中有两个核心的包,分别是torch和torchvision。在前面的文章中介绍了torch包的一些情况,现在介绍一下torchvision包的一些情况:主要功能是实现数据的处理、导入和预览等,主要处理计算机视觉相关问题的处理工作。

下面进入手写数字识别的主要介绍:

1.从 torchvison 中导入两个子包 datasets和transforms

import torch
import numpy
import torchvision
import matplotlib.pyplot as plt
%matplotlib inline
from torchvision import datasets, transforms 
from torch.autograd import Variable

2.导入对应数据 

此时如果我们想使用MNIST就从datasets中导入MNIST,同理其他的数据集例如COCO、ImageNet、CIFCAR等都可以通过这个方法进行下载和载入。

data_train = datasets.MNIST(root = './data/',
                           transform = transform,
                           train = True,
                           download = True)
data_test = datasets.MNIST(root = './data/',
                          transform = transform,
                          train = False)

3.数据增强处理torch.transforms

torch.transforms中有大量的数据变换类,其中有很大一部分可以 用于实现数据增强(Data Argumentation)。如果我们参与到模型中的图片数据有限,就可以对有限的 图片数据进行变换,生成新的训练集。

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

torchvision.transforms.Compose类看作一种容器,它能够同时对多种数据变换进行组合。传入的参数是一个列表, 列表中的元素就是对载入的数据进行的各种变换操作。

transforms.ToTensor是一个类型的转换变换,将对应类型转变为Tensor类型。

transforms.Normalize是一个数据标准换变换。具体的变换为:

使用原始数据的均值(Mean)和标准差(Standard Deviation)来进行数据的标准化,在经过标准化变换之后,数据全部符 合均值为0、标准差为1的标准正态分布。

计算公式如下:

手写数字识别MLP代码pytorch pycharm手写数字识别_手写数字识别MLP代码pytorch

4.数据预览与数据装载

在数据下载完成并且载入后,我们还需要对数据进行装载。在装载时通过batch_size的值来确认每个包的大小,通过shuffle的值 来确认是否在装载的过程中打乱图片的顺序。

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

torch.utils.data.DataLoader类用于装载数据

dataset参数用于指定我们载入的数据集名称

batch_size参数设置了包中图片的数据个数,64代表64张图片

shuffle表示在装载过程是否打乱顺序

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

images,labels = next(iter(data_loader_train)) #images 的顺序为 (batch_size,channel,height,weight,)
img = torchvision.utils.make_grid(images)#img 输出的顺序为(channel,height,weight)

img = img.numpy().transpose(1,2,0)  # 转为(height,weight,channel)
std = [0.5,0.5,0.5]
mean = [0.5,0.5,0.5]
img = img * std + mean              # 将上面Normalize后的图像还原
print([labels[i].item for i in range(64)]) #labels为tensor,使用item显示对应值
plt.imshow(img)

使用iter和next来获取一个批次的图片数据和其对应 的图片标签

使用torchvision.utils中的make_grid类方法将一个批次的图片构造成网格模式

make_grid传入的参数为(数据个数,图片通道数,图片高度,图片宽度)--->会将图片维度转变为(通道数,高度,宽度)

完整代码:

import torch
import numpy
import torchvision
import matplotlib.pyplot as plt
%matplotlib inline
from torchvision import datasets, transforms   # torchvision包的主要功能是实现数据的处理、导入和预览等
from torch.autograd import Variable

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

data_loader_train = torch.utils.data.DataLoader(dataset = data_train,
                                               batch_size = 64,
                                               shuffle = True)
data_loader_test = torch.utils.data.DataLoader(dataset = data_test, 
                                               batch_size = 64, 
                                               shuffle = True)
images,labels = next(iter(data_loader_train)) #images 的顺序为 (batch_size,channel,height,weight,)
img = torchvision.utils.make_grid(images)#img 输出的顺序为(channel,height,weight)

img = img.numpy().transpose(1,2,0)  # 转为(height,weight,channel)
std = [0.5,0.5,0.5]
mean = [0.5,0.5,0.5]
img = img * std + mean              # 将上面Normalize后的图像还原
print([labels[i].item() for i in range(64)]) #labels为tensor,使用item()显示对应值
plt.imshow(img)

 输出结果:

[2, 9, 4, 0, 2, 1, 1, 2, 7, 0, 0, 5, 5, 2, 2, 7, 6, 2, 7, 7, 8, 7, 3, 6, 9, 2, 9, 3, 4, 3, 1, 4, 2, 7, 7, 9, 8, 8, 8, 0, 6, 3, 8, 0, 7, 0, 1, 3, 9, 9, 2, 1, 8, 9, 1, 4, 4, 7, 7, 6, 6, 7, 4, 7]

手写数字识别MLP代码pytorch pycharm手写数字识别_ide_02

5.模型搭建

完成数据装载后,我们就可以开始编写卷积神经网络模型的 搭建和参数优化的代码了。

具体代码如下:

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(p=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:

用于搭建卷积神经网络卷积层,主要参数为(输入通道、输出通道、卷积核大小、步长、填充)其中所有都是整形,

输入通道数-->确定输入数 据的层数

输出通道数-->确定输出数据的层数

卷积核kernel_size-->确定卷积核大小

移动步长stride-->确定每次移动的步长

填充padding-->添加层数大小,值为0时表示不进行边界像素的填充,如果值大于0,那么增加数字所对应的边界像素层数。

(2)torch.nn.MaxPool2d:

用于实现卷积神经网络中的最大池化 层,主要的输入参数是(移动步长、填充大小)。

池化窗口步长stride-->于确定池化窗口每次移动的步长

填充大小padding-->添加层数大小

(3)torch.nn.Dropout:

用于防止卷积神经网络 在训练的过程中发生过拟合,其工作原理简单来说就是在模型训练的过 程中,以一定的随机概率将卷积神经网络模型的部分参数归零,以达到减少相邻两层神经连接的目的。

手写数字识别MLP代码pytorch pycharm手写数字识别_2d_03

上图中打叉的神经节点就是被随机抽中并丢弃的神经连接,由于每次打乱都是随机的,因此可以保证最后训练出来的模型对各部分的权 重参数不产生过度依赖,从而防止过拟合。如果不做任何设置,就使用默认的概率值0.5。

(4)forward函数中的内容

        x = self.conv1(x)  #对数据进行卷积处理
        x = x.view(-1,14*14*128) #对参数实现扁平化
        x= self.dense(x)  #紧接着的就是全连接层

如果不进行扁平化,则 全连接层的实际输出的参数维度和其定义输入的维度将不匹配,程序会报错;最后,通过self.dense定义的全连接进行最后的分类。

6.参数优化

具体代码:

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

损失函数使用的是交叉熵,优化函数使用的是Adam自适应优化算法

搭建整体代码:

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(p=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

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)
    (3): Linear(in_features=1024, out_features=10, bias=True)
  )
)