《动手学深度学习》——卷积神经网络
从全连接层到卷积
不变性
- 平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应
- 局部性(locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系。最终,在后续神经网络,整个图像级别上可以集成这些局部特征用于预测
限制多层感知机
- 使用和分别表示输入图像和隐藏表示中位置(, )处的像素,将参数从权重矩阵替换为四届权重张量,假设包含偏置参数,可将全连接层形式化为:
平移不变性
- 平移不变性意味着检测对象在输入中的平移应仅导致隐藏表示中的平移。也就是和不依赖于(,)的值,即,并且是一个常数,如:
局部性
- 为收集用来训练参数的相关信息,不应偏离到距(,)很远的地方。这意味着在或的范围之外,我们可以设置:
- 上式是一个卷积层,被称为卷积核或滤波器。
- 以前,多层感知机可能需要数十亿个参数来表示网络中的一层,而现在卷积神经网络通常只需要几百个参数,而且不需要改变输入或隐藏表示的维数。 参数大幅减少的代价是,我们的特征现在是平移不变的,并且当确定每个隐藏激活的值时,每一层只能包含局部的信息。
- 以上所有的权重学习都将依赖于归纳偏置。当这种偏置与现实相符时,我们就能得到样本有效的模型,并且这些模型能很好地泛化到未知数据中。 但如果这偏置与现实不符时,比如当图像不满足平移不变时,我们的模型可能难以拟合我们的训练数据。
通道
- 图像一般包含三个通道/三种原色,。因此,我们将索引为。由此卷积相应地调整为 ,而不是。此外,由于输入图像是三维的,我们的隐藏表示也最好采用三维张量:
其中隐藏表示中的索引表示输出通道,而随后的输出将继续以三维张量作为输入进入下一个卷积层。 所以可以定义具有多个通道的卷积层,而其中是该卷积层的权重。
图像卷积
互相关运算
- 严格来说,卷积层是个错误的叫法,因为它所表达的运算其实是 互相关运算 (cross-correlation),而不是卷积运算
- 二维互相关运算实现:
def corr2d(X, K): #@save """计算二维互相关运算。""" h, w = K.shape Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)) for i in range(Y.shape[0]): for j in range(Y.shape[1]): Y[i, j] = (X[i:i + h, j:j + w] * K).sum() return Y
卷积层
- 基于上面定义的 corr2d 函数实现二维卷积层:
class Conv2D(nn.Module): def __init__(self, kernel_size): super().__init__() self.weight = nn.Parameter(torch.rand(kernel_size)) self.bias = nn.Parameter(torch.zeros(1)) def forward(self, x): return corr2d(x, self.weight) + self.bias
感受野
- 在CNN中,对于某一层的任意元素 ,其 感受野 (Receptive Field)是指在前向传播期间可能影响计算的所有元素(来自所有先前层)
填充和步幅
填充
- 在应用多层卷积时,常常丢失边缘像素,解决这个问题的简单方法即为填充(padding):在输入图像的边界填充元素(通常填充元素0)
- 卷积神经网络中卷积核的高度和宽度通常为奇数,例如 1、3、5 或 7。 选择奇数的好处是,保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。
- 创建一个高度和宽度为3的二维卷积层,并在所有侧边填充1个像素:
# 为了方便起见,我们定义了一个计算卷积层的函数。 # 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数 def comp_conv2d(conv2d, X): # 这里的(1,1)表示批量大小和通道数都是1 X = X.reshape((1, 1) + X.shape) Y = conv2d(X) # 省略前两个维度:批量大小和通道 return Y.reshape(Y.shape[2:]) # 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列 conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1) X = torch.rand(size=(8, 8)) comp_conv2d(conv2d, X).shape
步幅
- 有时为了高效计算或时减缩采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素,即步幅
- 输出形状:
多输入多输出通道
多输入通道
- 我们可以对每个通道输入的二维张量和卷积核的二维张量进行互相关运算,再对通道求和得到二维张量
多输出通道
- 用和分别表示输入和输出通道的数目,并让和为卷积核的高度和宽度。为获得多个通道的输出,可为每个输出通道创建一个形状为的卷积核张量,这样卷积核的形状为
卷积层
- 该卷积层失去了卷积层特有能力——在高度和宽度维度上,识别相邻元素间相互作用的能力。它的作用主要是调整网络层的通道数量和控制模型复杂性
池化层
它具有双重目的:降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性
最大池化层和平均池化层
- 实现:
def pool2d(X, pool_size, mode='max'): p_h, p_w = pool_size Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1)) for i in range(Y.shape[0]): for j in range(Y.shape[1]): if mode == 'max': Y[i, j] = X[i: i + p_h, j: j + p_w].max() elif mode == 'avg': Y[i, j] = X[i: i + p_h, j: j + p_w].mean() return Y - 在处理多通道输入数据时,池化层在每个输入通道上单独运算,而不是像卷积层一样在通道上对输入进行汇总。 这意味着池化层的输出通道数与输入通道数相同。