PyTorch
实现神经网络图像分类
本文章主要参考《深度学习与图像识别 原理与实践一书》
上篇文章介绍了KNN
分类算法,并用其来实现MNIST
和Cifar10
数据集的分类任务,这篇文章同样是做这两个数据集的分类,但是使用的Pytorch
构建神经网络算法来完成实验,是我自己对Pytorch
的一个初步学习。
之前一直是使用Keras
,之所以学习Pytorch
是因为,Keras
比较适合作为练习使用的深度学习框架,因为其过度的封装导致学习时无法理解深度学习的真正内涵。
PyTorch
的使用
Tensor
Tensor是Pytorch
中的基础组件,Tensor与Numpy
中的ndarrays
非常类似,但是Tensor可以使用GPU
加速而ndarrays
不可以。在pytorch
下使用如下命令来进行GPU
运算:
1 | import torch |
x和y为Tensor类型的数据,如果电脑无GPU
,则无法进行GPU
运算。
Terson
与Numpy
互相转换
1 | import torch |
Tensor做矩阵运算(矩阵相乘)
1 | import torch |
Variable
简单理解,Variable是对Tensor的一种封装,每个Variable中包含三个属性(data、grad以及creator):Variable中的Tensor本身(通过.data进行访问)、对应Tensor的梯度(通过.grad进行访问)以及创建这个Variable的Function引用(通过.grad_fn
进行访问),该引用可用于回溯整个创建链路,如果是用户自己创建Variable,则其grad_fn
为None。
1 | from torch.autograd import Variable |
激活函数
之前的版本通过import torch.nn.functional as F
来加载激活函数,随着pytorch
版本的更新,如今通过torch可以直接加载。
1 | import matplotlib.pyplot as plt |
具体这些激活函数都是啥,有什么用,前面的文章已经介绍了,感兴趣可以查看。
损失函数
均方差损失函数
1 | torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean') |
参数说明:
- size_average: 弃用
- reduce:弃用
- reduction(string, optional):输出元素包含3种操作方式,即none、mean和sum。
交叉熵损失函数
1 | torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean') |
参数说明:
- weight(Tensor, optional):多分类任务中,手动给出每个类别权重的缩放量。如果给出,则其是一个大小等于类别个数的张量。
- size_average:弃用
- reduce:弃用
- ignore_index(int, optional):指定被忽略且不对输入梯度做贡献的目标值。
- reduction:输出元素包含3种操作方式,即none、mean和sum。
示例代码:
1 | loss = torch.nn.CrossEntropyLoss() |
注意:CrossEntropyLoss
不支持one-hot编码类型,输入的都是真实的target,所以如果输入的真实分类是one-hot编码的话需要自行转换,即将target ont-hot
编码格式转换为每个样本的类别,再传给CrossEntropyLoss
PyTorch
实战
MNIST
分类
首先,定义神经网络:自定义神经网络模型在Pytorch
中需要继承torch.nn.Module
,然后自己重写Forward方法完成前向计算。
1 | from torch.utils.data import DataLoader |
网络结构打印的输出结果具体如下:
1 | NeuralNetwork( |
接下来使用Pytorch
加载数据集,需要注意的是要将数据集转换成tensor张量,可以直接使用transform。
1 | class MNIST: |
然后编写训练代码
1 | learning_rate = 1e-1 # 学习率 0.1 |
初步学习pytorch
确实感觉比Keras
要自己做的操作多一些,比如构建网络,自己定义forward方法,然后这里计算损失,反向传播和更新参数等,更符合神经网络的一种学习流程。
最后预测准确率
1 | total = 0 |
最后的准确率大约在96%左右,比使用KNN
算法得到的95%效果还要更好些。
这里有以下几个方法可能需要简单解释下:
images.view()
类似于numpy
的reshape
方法,用来改变tensor的形状,比如这里images的原始形状是[100, 28, 28],这里view之后变成了[100, 784],-1表示根据后面设置的列自适应调整。
然后这里引用一段关于view与reshape不同的地方:view()方法只适用于满足连续性条件的tensor,并且该操作不会开辟新的内存空间,只是产生了对原存储空间的一个新别称和引用,返回值是视图。
torch.max(input, dim)
input
是softmax
函数输出的一个tensor
dim
是max函数索引的维度0/1
,0
是每列的最大值,1
是每行的最大值- 函数会返回两个
tensor
,第一个tensor
是每行/列(根据dim参数决定)的最大值;第二个tensor
是每行/列最大值的索引。
在多分类任务中一般我们并不需要知道各类别的预测概率,所以返回值的第一个tensor
对分类任务没有帮助,而第二个tensor
包含了预测最大概率的索引,所以在实际使用中我们仅获取第二个tensor
即可。
size()
类似于numpy
的shape
,返回tensor的形状,并且可以在括号中设置索引,表示返回该维的大小,如images.size()==>[100, 3, 32, 32]
,则images.size(0)==>100、images.size(2)==>32
。
sum()
类似于numpy
的sum()
,求和,相应的有一个ndim
参数,ndim
=1计算的是行的和,ndim
=0计算的是列的和。这里predict==lables
返回的tensor,相同值的位置为1,不同值的位置为0,求和就表示预测对了多少个。
Cifar10
分类
Cifar10
数据集是彩色图像,同样也包含10个类别,图片尺寸为3*32*32。
首先定义神经网络,一个两层的网络(不算输出层),比上面的多一层。
1 | import torch |
加载Cifar10
数据集
1 | class Cifar10: |
训练数据
1 | num_epoch = 5 |
测试数据
1 | total = 0 |
我在自己的电脑上测试准确率只有20%,可以看出,浅层神经网络可以解决一部分简单的问题,但对于稍微复杂一些的彩色数据集表现并不理想,后面会进一步使用深度卷积神经网络实现Cifar10
分类。