动手学深度学习-19

敖炜 Lv5

卷积层

卷积神经网络(convolutional neural network,CNN)是一类强大的、为处理图像数据而设计的神经网络。基于卷积神经网络架构的模型在计算机视觉领域中已经占主导地位,当今几乎所有的图像识别、目标检测或语义分割相关的学术竞赛和商业应用都以这种方法为基础。

现代卷积神经网络的设计得益于生物学、群论和一系列的补充实验。卷积神经网络需要的参数少于全连接架构的网络,而且卷积也很容易用GPU并行计算。因此卷积神经网络除了能够高效地采样从而获得精确的模型,还能够高效地计算。久而久之,从业人员越来越多地使用卷积神经网络。即使在通常使用循环神经网络的一维序列结构任务上(例如音频、文本和时间序列分析),卷积神经网络也越来越受欢迎。通过对卷积神经网络一些巧妙的调整,也使它们在图结构数据和推荐系统中发挥作用。

从全连接层到卷积

多层感知机十分适合处理表格数据,其中行对应样本,列对应特征。对于表格数据,我们寻找的模式可能涉及特征之间的交互,但是我们不能预先假设任何与特征交互相关的先验结构。对于高维感知数据,多层感知机这种缺少结构的网络可能会变得不实用。

卷积神经网络(convolutional neural networks,CNN)是机器学习利用自然图像中一些已知结构的创造性方法。

不变性

想象一下,假设我们想从一张图片中找到某个物体。合理的假设是:无论哪种方法找到这个物体,都应该和物体的位置无关。理想情况下,我们的系统应该能够利用常识:猪通常不在天上飞,飞机通常不在水里游泳。但是,如果一只猪出现在图片顶部,我们还是应该认出它。我们可以从儿童游戏”沃尔多在哪里”中得到灵感:在这个游戏中包含了许多充斥着活动的混乱场景,而沃尔多通常潜伏在一些不太可能的位置,读者的目标就是找出他。尽管沃尔多的装扮很有特点,但是在眼花缭乱的场景中找到他也如大海捞针。然而沃尔多的样子并不取决于他潜藏的地方,因此我们可以使用一个“沃尔多检测器”扫描图像。该检测器将图像分割成多个区域,并为每个区域包含沃尔多的可能性打分。卷积神经网络正是将空间不变性(spatial invariance)的这一概念系统化,从而基于这个模型使用较少的参数来学习有用的表示。

现在,我们将上述想法总结一下,从而帮助我们设计适合于计算机视觉的神经网络架构。

  1. 平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应,即为“平移不变性”。
  2. 局部性(locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进行预测。

让我们看看这些原则是如何转化为数学表示的。

多层感知机的限制

首先,多层感知机的输入是二维图像,其隐藏表示在数学上是一个矩阵,在代码中表示为二维张量。其中具有相同的形状。为了方便理解,我们可以认为,无论是输入还是隐藏表示都拥有空间结构。

使用$[\mathbf{X}]{i, j}[\mathbf{H}]{i, j}ij使\mathsf{W}\mathbf{U}$包含偏置参数,我们可以将全连接层形式化地表示为

$$ \begin{aligned} \left[\mathbf{H}\right]{i, j} &= [\mathbf{U}]{i, j} + \sum_k \sum_l[\mathsf{W}]{i, j, k, l} [\mathbf{X}]{k, l}\ &= [\mathbf{U}]{i, j} +
\sum_a \sum_b [\mathsf{V}]
{i, j, a, b} [\mathbf{X}]_{i+a, j+b}.\end{aligned} $$

其中,从的转换只是形式上的转换,因为在这两个四阶张量的元素之间存在一一对应的关系。
我们只需重新索引下标,使,由此可得$[\mathsf{V}]{i, j, a, b} = [\mathsf{W}]{i, j, i+a, j+b}abij[\mathbf{H}]{i, j}x(i, j)使[\mathsf{V}]{i, j, a, b}$。

平移不变性

现在引用上述的第一个原则:平移不变性。
这意味着检测对象在输入

中的平移,应该仅导致隐藏表示

中的平移。也就是说,



实际上不依赖于

的值,即
$[\mathsf{V}]{i, j, a, b} = [\mathbf{V}]{a, b}\mathbf{U}u\mathbf{H}$定义为:

$[\mathbf{H}]{i, j} = u + \sum_a\sum_b [\mathbf{V}]{a, b} [\mathbf{X}]_{i+a, j+b}.$

这就是卷积(convolution)。我们是在使用系数
$[\mathbf{V}]{a, b}(i, j)(i+a, j+b)[\mathbf{H}]{i, j}[\mathbf{V}]{a, b}[\mathsf{V}]{i, j, a, b}$少很多,因为前者不再依赖于图像中的位置。这就是显著的进步!

局部性

现在引用上述的第二个原则:局部性。如上所述,为了收集用来训练参数
$[\mathbf{H}]{i, j}(i, j)|a|> \Delta|b| > \Delta[\mathbf{V}]{a, b} = 0[\mathbf{H}]_{i, j}$重写为

$$[\mathbf{H}]{i, j} = u + \sum{a = -\Delta}^{\Delta} \sum_{b = -\Delta}^{\Delta} [\mathbf{V}]{a, b} [\mathbf{X}]{i+a, j+b}.$$

简而言之,上式是一个卷积层(convolutional layer),而卷积神经网络是包含卷积层的一类特殊的神经网络。
在深度学习研究社区中,被称为卷积核(convolution kernel)或者滤波器(filter),亦或简单地称之为该卷积层的权重,通常该权重是可学习的参数。
当图像处理的局部区域很小时,卷积神经网络与多层感知机的训练差异可能是巨大的:以前,多层感知机可能需要数十亿个参数来表示网络中的一层,而现在卷积神经网络通常只需要几百个参数,而且不需要改变输入或隐藏表示的维数。
参数大幅减少的代价是,我们的特征现在是平移不变的,并且当确定每个隐藏活性值时,每一层只包含局部的信息。
以上所有的权重学习都将依赖于归纳偏置。当这种偏置与现实相符时,我们就能得到样本有效的模型,并且这些模型能很好地泛化到未知数据中。
但如果这偏置与现实不符时,比如当图像不满足平移不变时,我们的模型可能难以拟合我们的训练数据。

卷积

在进一步讨论之前,我们先简要回顾一下为什么上面的操作被称为卷积。在数学中,两个函数(比如)之间的“卷积”被定义为

也就是说,卷积是当把一个函数“翻转”并移位时,测量之间的重叠。
当为离散对象时,积分就变成求和。例如,对于由索引为的、平方可和的、无限维向量集合中抽取的向量,我们得到以下定义:

对于二维张量,则为的索引的索引上的对应加和:

这看起来类似于卷积层,但有一个主要区别:这里不是使用,而是使用差值。然而,这种区别是表面的,因为我们可以变换符号

通道

然而这种方法有一个问题:我们忽略了图像一般包含三个通道/三种原色(红色、绿色和蓝色)。
实际上,图像不是二维张量,而是一个由高度、宽度和颜色组成的三维张量,比如包含个像素。
前两个轴与像素的空间位置有关,而第三个轴可以看作每个像素的多维表示。
因此,我们将索引为$[\mathsf{X}]{i, j, k}[\mathsf{V}]{a,b,c}[\mathbf{V}]_{a,b}$。

此外,由于输入图像是三维的,我们的隐藏表示也最好采用三维张量。
换句话说,对于每一个空间位置,我们想要采用一组而不是一个隐藏表示。这样一组隐藏表示可以想象成一些互相堆叠的二维网格。
因此,我们可以把隐藏表示想象为一系列具有二维张量的通道(channel)。
这些通道有时也被称为特征映射(feature maps),因为每个通道都向后续层提供一组空间化的学习特征。
直观上可以想象在靠近输入的底层,一些通道专门识别边缘,而一些通道专门识别纹理。

为了支持输入和隐藏表示中的多个通道,我们可以在中添加第四个坐标(可以理解为提取不同特征的卷积核参数占一个维度),即。综上所述,

$$[\mathsf{H}]{i,j,d} = \sum{a = -\Delta}^{\Delta} \sum_{b = -\Delta}^{\Delta} \sum_c [\mathsf{V}]{a, b, c, d} [\mathsf{X}]{i+a, j+b, c},$$

其中隐藏表示中的索引表示输出通道,而随后的输出将继续以三维张量作为输入进入下一个卷积层。
所以,上式可以定义具有多个通道的卷积层,而其中是该卷积层的权重。

小结

  • 图像的平移不变性使我们以相同的方式处理局部图像,而不在乎它的位置。
  • 局部性意味着计算相应的隐藏表示只需一小部分局部图像像素。
  • 在图像处理中,卷积层通常比全连接层需要更少的参数,但依旧获得高效用的模型。
  • 卷积神经网络(CNN)是一类特殊的神经网络,它可以包含多个卷积层。
  • 多个输入和输出通道使模型在每个空间位置可以获取图像的多方面特征。

图像卷积

互相关运算

严格来说,卷积层是个错误的叫法,因为它所表达的运算其实是互相关运算(cross-correlation),而不是卷积运算。
根据前面的描述,在卷积层中,输入张量和核张量通过互相关运算产生输出张量。

输出大小等于输入大小减去卷积核大小,即:

这是因为我们需要足够的空间在图像上“移动”卷积核。稍后,我们将看到如何通过在图像边界周围填充零来保证有足够的空间移动卷积核,从而保持输出大小不变。接下来,我们在corr2d函数中实现如上过程,该函数接受输入张量X和卷积核张量K,并返回输出张量Y

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import torch
from torch import nn
from d2l import torch as d2l

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函数实现二维卷积层。在__init__构造函数中,将weightbias声明为两个模型参数。前向传播函数调用corr2d函数并添加偏置。

1
2
3
4
5
6
7
8
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

高度和宽度分别为的卷积核可以被称为卷积或卷积核。我们也将带有卷积核的卷积层称为卷积层。

图像中目标的边缘检测

下面是卷积层的一个简单应用:通过找到像素变化的位置,来检测图像中不同颜色的边缘。首先,我们构造一个像素的黑白图像。中间四列为黑色(),其余像素为白色()。

1
2
3
X = torch.ones((6, 8))

X[:, 2:6] = 0

接下来,我们构造一个高度为、宽度为的卷积核K。当进行互相关运算时,如果水平相邻的两元素相同,则输出为零,否则输出为非零。

1
K = torch.tensor([[1.0, -1.0]])

我们对参数X(输入)和K(卷积核)执行互相关运算。如下所示,输出Y中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘,其他情况的输出为

1
Y = corr2d(X, K)

但是当前这个卷积核K只能检测垂直边缘,无法检测水平边缘

学习卷积核

如果我们只需寻找黑白边缘,那么以上[1, -1]的边缘检测器足以。然而,当有了更复杂数值的卷积核,或者连续的卷积层时,我们不可能手动设计滤波器。那么我们是否可以学习由X生成Y的卷积核呢?

现在让我们看看是否可以通过仅查看“输入-输出”对来学习由X生成Y的卷积核。我们先构造一个卷积层,并将其卷积核初始化为随机张量。接下来,在每次迭代中,我们比较Y与卷积层输出的平方误差,然后计算梯度来更新卷积核。为了简单起见,我们在此使用内置的二维卷积层,并忽略偏置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 构造一个二维卷积层,它具有一个输出通道(可以理解为只有提取一个特征的卷积核)和形状为(1, 2)的卷积核
conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias = False)

# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度)
# 其中批量大小和通道数都为1

X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))

lr = 3e-2

for i in range(10):
Y_hat = conv2d(X)

l = (Y_hat - Y) ** 2
conv2d.zero_grad()

l.sum().backward()

# 迭代卷积核
conv2d.weight.data[:] -= lr * conv2d.weight.grad
if (i + 1) % 2 == 0:
print(f'epoch {i+1}, loss {l.sum():.3f}')

互相关和卷积

为了得到正式的卷积运算输出,我们需要执行严格卷积运算,而不是互相关运算。幸运的是,它们差别不大,我们只需水平和垂直翻转二维卷积核张量,然后对输入张量执行互相关运算。

值得注意的是,由于卷积核是从数据中学习到的,因此无论这些层执行严格的卷积运算还是互相关运算,卷积层的输出都不会受到影响。为了说明这一点,假设卷积层执行互相关运算并学习互相关运算中的卷积核,该卷积核在这里由矩阵表示。假设其他条件不变,当这个层执行严格的卷积时,学习的卷积核在水平和垂直翻转之后将与相同。也就是说,当卷积层对互相关运算中的输入和执行严格卷积运算时,将得到与互相关运算中相同的输出。

为了与深度学习文献中的标准术语保持一致,我们将继续把“互相关运算”称为卷积运算,尽管严格地说,它们略有不同。此外,对于卷积核张量上的权重,我们称其为元素

特征映射和感受野

在卷积神经网络中,对于某一层的任意元素,其感受野(receptive field)是指在前向传播期间可能影响计算的所有元素(来自所有先前层)。

当一个特征图中的任意元素需要检测更广区域的输入特征时,我们可以构建一个更深的网络。

小结

  • 二维卷积层的核心计算是二维互相关运算。最简单的形式是,对二维输入数据和卷积核执行互相关操作,然后添加一个偏置。
  • 我们可以设计一个卷积核来检测图像的边缘。
  • 我们可以从数据中学习卷积核的参数。
  • 学习卷积核时,无论用严格卷积运算或互相关运算,卷积层的输出不会受太大影响。
  • 当需要检测输入特征中更广区域时,我们可以构建一个更深的卷积网络。
  • 标题: 动手学深度学习-19
  • 作者: 敖炜
  • 创建于 : 2023-08-19 14:03:47
  • 更新于 : 2024-04-19 09:28:37
  • 链接: https://ao-wei.github.io/2023/08/19/动手学深度学习-19/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论