Deep Learning with Pytorch 中文简明笔记 第八章 Using convolutions to generalize
Pytorch作为深度学习框架的后起之秀,凭借其简单的API和简洁的文档,收到了越来越多人的关注和喜爱。本文主要总结了 Deep Learning with Pytorch 一书第八章[Using convolutions to generalize]的主要内容,并加以简单明了的解释,作为自己的学习记录,也供大家学习和参考。
文章目录
主要内容
- 理解卷积
- 建立卷积神经网络
- 创建定制nn.Module的子类
- module和API的不同
- 设计神经网络的建议
1. 卷积
主要介绍了什么是卷积,卷积的运算操作
卷积有一些特性:
- 对相邻像素的局部操作
- 平移不变性
- 相对于其他方式有更少的参数
2. 卷积的实现
在Pytorch中,torch.nn模型提供了一维、二维和三维卷积,其中nn.Conv1d用于序列,nn.Conv2d用于图像,nn.Conv3d用于立方体或者视频。
对于CIFAR-10数据集,应当使用nn.Conv2d。在nn.Conv2d中需要传递的参数有,输入特征数目(通道数目),输出特征数目和kernel的大小。例如定义一个输入为3,输出为16,kernel为3×3的卷积层。
# In[11]:
conv = nn.Conv2d(3, 16, kernel_size=3)
conv
# Out[11]:
Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1))
实际上有多少个卷积核呢,应该16×3个。相当于对于原图片的每一个通道,分别用16个不同卷积核去做卷积。也可以理解为原来的每一个channel,运算完之后编程16个channel。那么权重的数目即为16×3×3×3。bias的数目为16。
# In[12]:
conv.weight.shape, conv.bias.shape
# Out[12]:
(torch.Size([16, 3, 3, 3]), torch.Size([16]))
尝试对图片做一下卷积。
# In[13]:
img, _ = cifar2[0]
output = conv(img.unsqueeze(0))
img.unsqueeze(0).shape, output.shape
# Out[13]:
(torch.Size([1, 3, 32, 32]), torch.Size([1, 16, 30, 30]))
# In[15]:
plt.imshow(output[0, 0].detach(), cmap='gray')
plt.show()
2.1 卷积的Padding
由于卷积运算的特殊性,会导致图片变小。对于奇数大小的kernel,图片的高度和宽度会减小(kernel_size -1) / 2,在上面的例子中即减小1个像素。这里可以使用padding来解决这个问题。
# In[16]:
conv = nn.Conv2d(3, 1, kernel_size=3, padding=1)
output = conv(img.unsqueeze(0))
img.unsqueeze(0).shape, output.shape
# Out[16]:
(torch.Size([1, 3, 32, 32]), torch.Size([1, 1, 32, 32]))
使用padding的原因有两个,一是避免卷积操作影响图片尺寸,二是对于一些网络结构,我们希望图片经过卷积前后的大小一致,以便进行两者的加减操作。
2.2 卷积的简单理解
对卷积核手动设置权重和偏置
# In[17]:
with torch.no_grad():
conv.bias.zero_()
with torch.no_grad():
conv.weight.fill_(1.0 / 9.0)
# In[18]:
output = conv(img.unsqueeze(0))
plt.imshow(output[0, 0].detach(), cmap='gray')
plt.show()
简单来看,就是对卷积核所在范围内的图像取平均
下面换另一个卷积核
# In[19]:
conv = nn.Conv2d(3, 1, kernel_size=3, padding=1)
with torch.no_grad():
conv.weight[:] = torch.tensor([[-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0]])
conv.bias.zero_()
从卷积核来看,卷积核右侧的权重大,左侧的权重小,对于垂直的边缘响应应该更大,
结果确实如此,实际上上面的卷积核就是垂直边缘检测的卷积核。而卷积神经网络,从一定程度上就是学习到不同的卷积核,来从图片中提取并组合各种特征,来达到最终的目的。
2.3 更进一步:深度和池化(pooling)
对于小的图片,小的卷积核就足够提取到一些特征。而当图片变大时,小的卷积核就显得不够了,但是使用更大的卷积核意味着更大的参数量,更臃肿的模型。有没有什么办法可以在大图像时依旧使用小卷积核呢?解决问题的方法就是池化。
池化是的基本思想就是下采样,将周围几个像素合并为一个像素。大致可分为:
- 平均值池化(average pooling),周围像素取平均值作为新像素。
- 最大值池化(max pooling),周围像素取最大值作为新像素。
此外,可以以采用有间隔的卷积核。这行方法都会损失一些信息,但是更重要是保留图片最重要信息,供下层卷积层进一步提取高级特征。图中展示了最大值池化的详细步骤。
# In[21]:
pool = nn.MaxPool2d(2)
output = pool(img.unsqueeze(0))
img.unsqueeze(0).shape, output.shape
# Out[21]:
(torch.Size([1, 3, 32, 32]), torch.Size([1, 3, 16, 16])