我们创建一个高度和宽度为3的二维卷积层,并在所有侧边填充1个像素。给定高度和宽度为8的输入,则输出的高度和宽度也是8。
import torch
from torch import nn
# 为了方便起见,我们定义了一个计算卷积层的函数。
# 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数
def comp_conv2d(conv2d,X):
# 这里的(1,1)表示批量大小和通道数都是1
X = X.reshape((1, 1) + X.shape)
Y = conv2d(X) # 输出是4维的
# 省略前两个维度:批量大小和通道
return Y.reshape(Y.shape[2:])
# 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列
# 对于nn框架来说,padding传入的是一边填充的数字
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1) # kernel是3*3
X = torch.rand(size=(8, 8))
comp_conv2d(conv2d, X).shape
运行结果如下:
当卷积核的高度和宽度不同时,我们可以填充不同的高度和宽度,使输出和输入具有相同的高度和宽度。在如下示例中,我们使用高度为5,宽度为3的卷积核,高度和宽度两边的填充分别为2和1。
# 对于kernel_size,padding,第一个参数是高度(行),第二个参数是宽(列)
conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape
我们将高度和宽度的步幅设置为2,从而将输入的高度和宽度减半。
接下来,看一个稍微复杂的例子。
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
comp_conv2d(conv2d, X).shape
Q1:核大小、填充、步幅影响重要程度排序如何?
A1:一般使用填充是为了让输入和输出的大小一样
,即通常取 填充=核大小-1
(这里的填充大小是包括左右相加 或者 上下相加)。
通常来说,步幅等于1比较好,每一次挪一点看的东西更多,但是也存在不选步幅为1的情况,例如计算量太大的时候:当输入的图片很大,需要输出的图片很小,也不想用太多层去做这件事
,就会选用步幅,通常步幅取2,每次减半。
例如:输入图片为 224 * 224,用一个3 * 3的卷积核,padding为1,将5个步幅为2的层均匀地插在神经网络的中间(5层卷积后),最后得到的图像是 7 * 7
核大小是最关键的,填充一般取默认,步幅就根据自己想将模型复杂度控制在什么程度。
Q2:一般卷积处理完,输出维度都要减半,为什么这里提到输入输出保持不变?
A2:不是的,绝大多数情况都是让输入和输出维度保持一致。
Q3:步幅和填充这两个超参数,为什么实际中一般不怎么调节?
A3:因为步幅、填充、核大小以及通道数都是一个神经网络架构的一部分,是一个网络怎么设计的一部分。一般我们使用ResNet、AlexNet等,都会告诉说应该怎么样怎么设置这些参数。
Q4:如果多层卷积后输出和输入形状相同,特征会丢失吗?
A4:从信息论的角度看,信息永远会丢失的。机器学习本质上是压缩算法,例如把一个图片压缩到一个很小的值上面,这个值有语义信息,所以,机器学习算法永远是会丢失信息的。