11、卷积神经网络:结构、卷积/汇集层
层、空间布局、层模式、层大小模式、AlexNet/ZFNet/VGGNet 案例研究、计算考虑
卷积神经网络 (CNNs / ConvNets)
卷积神经网络与前一章的神经网络非常相似:它们是由具有可学习的权值和偏置的神经元组成的。每个神经元接收一些输入,执行点积,跟随一个非线性计算(可选项)。整个网络仍然表示一个可微的得分函数:从一端的原始图像像素到另一个的分数。并且它们在最后(完全连接)层上仍然具有损失函数(例如SVM /Softmax),我们在之前章节开发的用于学习规则神经网络的所有技巧/技巧仍然适用。
那有什么不一样呢?ConvNet 架构明确地假设输入是图像,这允许我们将某些属性编码到体系结构中。从而,使得前向功能更有效地实现,并且极大地减少了网络中的参数量。
11.1 体系结构概述
回忆:规则的神经网络。正如我们在前一章看到的,神经网络接收一个输入(一个向量),并通过一系列隐藏层来转换它。每个隐藏层由一组神经元组成,其中每个神经元完全连接到前一层中的所有神经元,其中单个层中的神经元完全独立地运行,并且不共享任何连接。最后一个完全连接的层被称为“输出层”,在分类场景中,它代表类分数。
规则的神经网络不能很好地扩展到完整的图像。在CIFAR-10中,图像仅为大小为32×32×3的(32个宽、32个高、3个颜色通道),因此在规则的神经网络的第一隐藏层中的单个完全连接的神经元将具有32×32×3=3072的权重。这一数量似乎仍然是可控的,但显然这种完全连接的结构不会扩展到更大的图像。例如,正常大小的图像,例如,200×200×3,将导致具有200×200×3=120000权重的神经元。此外,我们几乎肯定希望有几个这样的神经元,所以参数会增加得很快!显然,这种完全连通性是浪费的,大量的参数会很快导致过度拟合。
三维容量的神经元。卷积神经网络利用了输入是由图像这一事实,以更合理的方式约束了体系结构。特别地,不同于规则的神经网络,卷积层具有3个维排列的神经元:宽度、高度、深度。(注意这里的单词深度指的是**体积的第三个维度,而不是一个完整的神经网络的深度,它可以指网络中的层的总数。)例如,CIFAR-10中的输入图像是**的输入量,并且体积具有维度32x32×3(分别为宽度、高度、深度)。正如我们将很快看到的,一个层中的神经元将只连接到它之前的层的一个小区域,而不是以完全连接的方式连接所有的神经元。此外,CIFAR-10的最终输出层的维度为1x1x10,因为在ConvNet架构的最后,我们将把整个图像缩减为沿depth维度排列的单个等级分数向量。这是可视化:
左:一个规则的三层神经网络。右:一个卷积神经网络,其神经元在三个维度排列(宽度,高度,深度),如在一个图层中被可视化那样。ConvNet 的每一层将3D输入体积转换成神经元**的3D输出体积。在这个例子中,红色输入层代表图像,因此它的宽度和高度将是图像的尺寸,深度将为3(红色、绿色、蓝色通道)。
ConvNet 是由多层组成的。每一层都有一个简单的API:它将输入的3D体积转换成输出3D体积,并具有一些可能或可能不具有参数的可微函数。
11.2 构建卷积神经网络的层
如上所述,一个简单的ConvNet 是一层接一层的序列,网络中的每一层通过一个可微函数将一个体积的**转换为另一个。我们使用三种主要类型的层来构建ConvNet 结构:卷积层、汇集层和全连接层(与规则神经网络中所述的一样)。我们将堆叠这些层以形成完整的ConvNet 体系结构。
示例架构:概述。我们将在下面介绍更多的细节,但是一个简单的CIFAR-10分类的ConvNet 可能的有架构是:输入- CONV -RELU -POOL -FC]:
- 输入[32×32×3]将保持图像的原始像素值,在这种情况下,图像宽度为32,高度为32,并且具有三个颜色通道R、G、B
- 卷积层中,神经元与输入层中的一个局部区域相连,每个神经元都计算自己与输入层相连的小区域与自己权重的点积。卷积层会计算所有神经元的输出。如果我们使用12个滤波器(也叫作核),得到的输出数据体的维度就是[32x32x12]
- RELU 层将会逐个元素地进行**函数操作,例如使用以0为阈值的max(0,x)**函数。该层对数据尺寸没有改变,还是[32x32x12]。
- 汇聚层在在空间维度(宽度和高度)上进行降采样(downsampling)操作,数据尺寸有可能变为[16x16x12]。
- FC(即全连接)层将计算类得分,结果大小为[1x1x10],其中10个数字中的每一个对应于类得分,例如在10个类别的CIFAR-10中。全连接层与常规神经网络一样,顾名思义,这个层中的每个神经元都将连接到前一个卷中的所有神经元。
以这种方式,ConvNets 将原始图像逐层地从原始像素值转换为最终类分数。注意,一些层包含参数,有的层则没有。具体说来,CONV/FC层对输入执行变换操作的时候,不仅会用到**函数,还会用到很多参数(神经元的突触权值和偏差)。另一方面,RELU/POOL层将实现固定的函数操作。CONV/FC层中的参数将以梯度下降的方式进行训练,从而使神经网络计算的类得分与每个图像的训练集中的标签一致。
小结:
- ConvNet 架构在最简单的情况下是将图像转换为输出(例如保持类分数)的层列表
- 其中有几种不同类型的层(例如CONV/FC/RELU/POOL是目前最流行的)
- 每个层的输入是3D数据,然后使用一个可导的函数将其变换为3D的输出数据
- 每个层可能有或可能没有参数(例如CONV/FC层有参数,RELU/POOL就没有)
- 每个层可能有或可能没有额外的超参数(例如CONV/FC/POOL有,RELU 层没有)
一个卷积神经网络的**输出例子。左边的输入层存有原始图像像素,右边的输出层存有类别分类评分。在处理流程中的每个**数据体是铺成一列来展示的。因为对3D数据作图比较困难,我们就把每个数据体切成层,然后铺成一列显示。最后一层装的是针对不同类别的分类得分,这里只显示了得分最高的5个评分值和对应的类别。完整的网页演示在我们的课程主页。本例中的结构是一个小的VGG网络,VGG网络后面会有讨论
下面描述各个层和它们的超参数及连通性的细节。
11.2.1 卷积层
Conv 层是卷积网络的核心构建块,它承担了网络中大部分的计算量。
概述和直觉。首先讨论的是,在没有大脑和生物意义上的神经元之类的比喻下,CONV 层到底在计算什么。卷积层的参数由一些可学习的滤波器组成。每个滤波器在空间上(宽度和高度)都比较小,但是深度和输入数据一致。举例来说,卷积神经网络第一层的一个典型的滤波器的尺寸可以是5x5x3(宽高都是5像素,深度是3。因为图像为RGB颜色通道,所以有3的深度)。在前向传播的时候,让每个滤波器都在输入数据的宽度和高度上滑动(更精确地说是卷积),然后计算整个滤波器和输入数据任一处的内积。当滤波器沿着输入数据的宽度和高度滑过后,会生成一个2维的**图(activation map),**图给出了在每个空间位置处滤波器的反应。直观地来说,网络会让滤波器学习到当它看到某些类型的视觉特征时就**,具体的视觉特征可能是某些方位上的边界,或者在第一层上某些颜色的斑点,甚至可以是网络更高层上的蜂巢状或者车轮状图案。在每个卷积层上,我们会有一整个集合的滤波器(比如12个),每个都会生成一个不同的二维**图。将这些**映射在深度方向上层叠起来就生成了输出数据。
以大脑做比喻:如果你喜欢用大脑和生物神经元来做比喻,那么输出的3D数据中的每个数据项可以被看做是神经元的一个输出,而该神经元只观察输入数据中的一小部分,并且和空间上左右两边的所有神经元共享参数(因为这些数字都是使用同一个滤波器得到的结果)。现在开始讨论神经元的连接,它们在空间中的排列,以及它们参数共享的模式。
局部连接:在处理图像这样的高维度输入时,让每个神经元都与前一层中的所有神经元进行全连接是不现实的。相反,我们让每个神经元只与输入数据的一个局部区域连接。该连接的空间大小叫做神经元的接收域,它的尺寸是一个超参数(其实就是滤波器的空间尺寸)。在深度方向上,这个连接的大小总是和输入量的深度相等。需要再次强调的是,我们对待空间维度(宽和高)与深度维度是不同的:连接在空间(宽高)上是局部的,但是在深度上总是和输入数据的深度一致。
例1。例如,假设输入卷大小为[32×32×3],(例如RGB CIFAR-10图像)。如果接收域(或滤波器大小)为5x5,那么Conv 层中的每个神经元将在输入卷中具有[5x5x3]区域的权重,总共为5×5×3=75权重(和1个偏置参数)。请注意,沿着深度轴的连通度必须是3,因为这是输入卷的深度。
例2。假设输入卷的大小[16x16x20]。然后使用3x3的接收域,Conv 层中的每个神经元现在总共有3×3×20=180个连接到输入卷。注意,同样地,连通性在空间中是局部的(例如3x3),但与输入的深度(20)一样。
左:红色的示例输入体(例如,32×32×3 CIFAR-10图像),以及第一卷积层中的神经元的示例体积。卷积层中的每个神经元在空间上只连接到输入空间中的局部区域,但连接到全深度(即所有颜色通道)。注意,沿着深度有多个神经元(在这个例子中有5个),它们都在输入中的同一个区域——参见下面文本中的深度列的讨论。右:神经网络章节中介绍的神经元保持不变,它们还是计算权重和输入的内积,然后进行非线性**函数运算,只是它们的连接被限制在一个局部空间
空间布局。我们已经解释了Conv 层中每个神经元与输入体积的连接性,但是我们还没有讨论输出神经元中有多少神经元,或者它们如何排列。三个超参数控制输出量的大小:深度、步幅和零填充。我们接下来讨论:
- 首先,输出量的深度是一个超参数:它对应于我们想要使用的滤波器的数量,每个滤波器学习在输入中寻找不同的东西。比如,如果第一个卷积层的输入是原始图像,那么在深度维度上的不同神经元将可能被不同方向的边界,或者是颜色斑点**。我们将这些沿着深度方向排列、感受域相同的神经元集合称为深度列(depth column),也有人使用纤维(fibre)来称呼它们。
- 其次,我们必须指定我们滑动滤波器的步幅。当步幅为1时,我们一次移动滤波器一个像素。当步幅为2(或不寻常的3或更多,虽然这在实践中是罕见的),则滤波器每次跳跃2像素。这个操作会让输出数据体在空间上变小。
- 正如我们将很快看到的那样,有时在边框上用零来填充输入卷是很方便的。这个零填充的大小是一个超参数。零填充的优良特性是,它将允许我们控制输出卷的空间大小(最常见的是,我们将很快看到,我们将使用它来精确地保持输入体积的空间大小,使得输入和输出宽度和高度是相同的)。
我们可以用输入体积大小(W*W)、Conv 层神经元(F*F)的接收域大小、应用它们的步幅(S)、以及在边界上使用的零填充量(P)计算输出体积的空间大小。你可以证明,正确的公式来计算多少神经元“适合”是由(W - F+ 2P)/S + 1给出的。例如,对于一个7x7输入和一个3x3滤波器与步幅1和边界填充0,我们将得到一个5x5输出。若步幅为2,我们将得到一个3x3输出。让我们再看一个图形例子:
空间布局示意图。在这个例子中,只有一个空间维度(x轴),一个神经元具有接收域大小F=3,输入大小是W=5,并且零填充P=1。左:神经元以S=1的步幅跨过输入,给出大小(5 - 3+2)/ 1 + 1=5的输出。右:神经元使用S=2的步幅,输出大小(5 - 3+2)/ 2+1=3。注意,步幅S=3不能使用,因为它无法整齐地穿过数据体。用计算公式也可以看出,因为(5 - 3+2)=4不能被3整除。
本例中,神经元的权重是[1,0,-1],显示在图的右上角,偏差值为0。这些权重是被所有黄色的神经元共享的(参数共享的内容看下文相关内容)。
使用零填充。在上面左边的例子中,注意输入维度是5,输出维度是相等的:也就是5。这是因为我们的接受域是3,我们使用的零填充为1。如果没有使用零填充,那么输出体积的空间维数就只有3。因为这就是滤波器整齐滑过并覆盖原始数据需要的数目。一般来说,当步长为S=1时,将零填充设为P=(F−1)/ 2,可确保输入体积和输出体积在空间上具有相同的大小。这样使用零填充是非常常见的,我们将在讨论更多关于ConvNet 架构的充分理由时讨论。
对步幅的限制。再次注意,空间布置超参数相互制约。例如,当输入的大小为W=10时,不使用零填充,P=0,滤波器大小为F=3,那么就不可能使用步长S=2,因为(W−F+2P)/S+1=(10−3+0)/2+1=4.5,即不是整数,这就是说神经元不能整齐对称地滑过输入数据体。因此,这个超参数的设置被认为是无效的,一个卷积神经网络库可能会报出一个错误,或者修改零填充值来让设置合理,或者修改输入数据体尺寸来让设置合理,或者其他什么措施。正如我们将在ConvNet 体系结构部分中看到的那样,可以看到合理地设置网络的尺寸让所有的维度都能正常工作,这件事可是相当让人头痛的。而使用零填充和遵守其他一些设计策略将会有效解决这个问题。
真实的例子。Krizhevsky等,赢得了2012年的ImageNet挑战,其输入图像的尺寸是[227x227x3]。在第一卷积层上,它使用具有接收域大小F=11的神经元,步幅S=4,并且没有零填充P=0。由于(227 - 11)/ 4+1=55,卷积层的深度 k=96,所以Conv 层输出体积具有[55×55×96]的大小。这个55x55x96个神经元中,每个都和输入数据体中一个尺寸为[11x11x3]的区域全连接。在深度列上的96个神经元都是与输入数据体中同一个[11x11x3]区域连接,但是权重不同。作为一个有趣的旁白,如果你阅读实际文件,它声称输入图像是224x224,这肯定是不正确的,因为(224 - 11)/4 + 1显然不是整数。这使许多人在历史上迷茫,对所发生的事情知之甚少。我自己最好的猜测是,Alex 使用了3个额外像素的零填充,这是他在论文中没有提到的。
参数共享。卷积层采用参数共享方案控制参数个数。使用上面的真实例子,我们看到在第一个Conv 层中有55×55×96=290400个神经元,并且每个都具有11×11×3=363个权重和1个偏置。加一起,这第一层的ConvNet就是290400×364 = 105705600个参数 。显然,这个数字非常高。
实践证明,可以通过做出一个合理的假设来显著地减少参数的数量:如果一个特征在某些空间位置(x,y)上是有用的,那么在不同的位置(x2,y2)计算也是有用的。换句话说,就是将深度维度上一个单独的2维切片看做深度切片(depth slice),比如一个数据体尺寸为[55x55x96]的就有96个深度切片,每个尺寸为[55x55]。在每个深度切片上的神经元都使用同样的权重和偏差。在这样的参数共享下,例子中的第一个卷积层就只有96个不同的权重集了,一个权重集对应一个深度切片,共有96x11x11x3=34,848个不同的权重,或34,944个参数(+96个偏差)。在每个深度切片中的55x55个权重使用的都是同样的参数。在反向传播的时候,都要计算每个神经元对它的权重的梯度,但是需要把同一个深度切片上的所有神经元对权重的梯度累加,这样就得到了对共享权重的梯度。这样,每个切片只更新一个权重集。
注意,如果单个深度切片中的所有神经元都使用相同的权重向量,那么在每个深度切片中的CONV 层的正向传递可以被计算为神经元的权重与输入体积的卷积(convolution ,因此就有了 卷积层 这个名称)。这也是为什么总是将这些权重集合称为滤波器(filter)(或卷积核(kernel)),因为它们和输入进行了卷积。
Krizhevsky 等人学习得到的示例滤波器。这里所示的96个滤波器中的每一个都是大小[11x11x3],并且每一个由一个深度切片中的55×55神经元共享。注意,参数共享假设是相对合理的:如果检测水平边缘在图像中的某个位置是重要的,那么由于图像的平移不变结构,它也应该直观地在一些其他位置有用。这是因为图像结构具有平移不变性。所以在卷积层的输出数据体的55x55个不同位置中,就没有必要重新学习去探测一个水平边界了。
请注意,有时参数共享假设可能没有意义。尤其是当输入到ConvNet 的图像具有特定的中心结构时,我们可能需要,例如,在图像的一边应该学习与另外一边完全不同的特征。一个实际的例子是当输入是以人脸为中心的图像,您可能需要不同的眼睛特定或头发特定的特征可以(也应该)在不同的空间位置学习。在这种情况下,放宽参数共享方案是常见的,将层称为局部连接层(Locally-Connected Layer)。
Numpy 举例. 为了使上面的讨论更具体,让我们用代码和一个特殊的例子来表达上述意思。假设输入卷是一个numpy 数组X:
- 一个位于(x,y)的深度列(或纤维)将会是 X[x,y,:]。
- 在深度为d处的深度切片,或**图应该是X[:,:,d]。
Conv 层实例。假设输入体积X
具有形状 X.shape: (11,11,4)
。进一步假设我们不使用零填充(P=0),滤波器大小是F=5,并且步幅是S=2。因此,输出体积将具有空间大小(11-5)/ 2 + 1=4,给出具有宽度和高度4的体积。输出卷中的**映射(称为 V)将看起来如下(在本示例中仅计算了一些元素):
V[0,0,0] = np.sum(X[:5,:5,:] * W0) + b0
V[1,0,0] = np.sum(X[2:7,:5,:] * W0) + b0
V[2,0,0] = np.sum(X[4:9,:5,:] * W0) + b0
V[3,0,0] = np.sum(X[6:11,:5,:] * W0) + b0
记住,在 numpy 中,上面的 *
操作表示数组之间的逐元素相乘。还注意,权重向量W0是该神经元的权重向量,而B0是偏置。这里,W0被假定为形状 W0.shape: (5,5,4)
,因为滤波器的尺寸为5,输入体积的深度为4。请注意,在每一点上,我们都在计算普通神经网络中所见的点积。此外,我们看到,我们使用相同的重量和偏置(由于参数共享),并且沿着宽度的尺寸在2步(即步幅)中增加。为了在输出音量中构造第二个**图,我们将有:
V[0,0,1] = np.sum(X[:5,:5,:] * W1) + b1
V[1,0,1] = np.sum(X[2:7,:5,:] * W1) + b1
V[2,0,1] = np.sum(X[4:9,:5,:] * W1) + b1
V[3,0,1] = np.sum(X[6:11,:5,:] * W1) + b1
-
V[0,1,1] = np.sum(X[:5,2:7,:] * W1) + b1
(在y方向上) -
V[2,3,1] = np.sum(X[4:9,6:11,:] * W1) + b1
(或同时在两个方向上)
在这里,我们访问的是V的深度维度上的第二层(即位置1),因为我们正在计算第二个**映射,并且现在使用了一组不同的参数(W1)。在上面的例子中,我们为了简洁而忽略了卷积层将执行的一些其他操作来填充输出阵列的其他部分。还有,要记得这些卷积操作通常后面接的是**函数,对**图中的每个元素做**函数运算,比如ReLU。这里没有显示。
小结: 我们总结一下卷积层:
- 输入数据体的尺寸为 W1×H1×D1
- 需要4个超参数:
- 滤波器数量 K,
- 滤波器的空间尺寸 F,
- 步幅S,
- 零填充数量P.
- 输出数据体的尺寸为 W2×H2×D2,其中:
- W2=(W1−F+2P)/S+1
- H2=(H1−F+2P)/S+1 (宽度和高度的计算方法对称相同 )
- D2=K
- 由于参数共享,每个滤波器包含 F⋅F⋅D1个权重,卷积层一共有(F⋅F⋅D1)⋅K个权重和K 个偏置
- 在输出数据体中,第d个深度切片(空间尺寸是W2×H2),用第d个滤波器和输入数据进行有效卷积运算的结果(使用步长S),最后在加上第d个偏置
通常将超参数设置为F=3,S=1,P=1。也有共同的惯例和经验法则来设置这些超参数。参阅下面的 ConvNet architectures 部分。
卷积演示。下面是一个CONV 层的运行演示。由于3D体积很难可视化,所有的卷(输入(蓝色),权重(红色),输出(绿色))被可视化,每个深度切片以行堆叠。输入体积的大小为W1=5,H1=5,D1=3,而CONV层参数为K=2,F=3,S=2,P=1。也就是说,我们有两个大小为3×3的过滤器,它们的步幅为2。因此,输出体积大小具有空间大小(5 - 3+2)/2+1=3。此外,注意到对输入卷施加P=1的填充,使得输入卷的外部边界为零。下面的可视化迭代输出**(绿色),并显示每个元素是通过元素化计算突出显示的输入(蓝色)与滤波器(红色)相乘,求和,最后加上偏差得来。
用矩阵乘法实现。注意到卷积运算就是在输入的滤波器和局部区域之间的点积操作。卷积层的常用实现方式就是利用这一点,将卷积层的前向传播变成一个巨大的矩阵乘法:
- 输入图像的局部区域被通常称为“im2col”的操作中拉伸为列。比如,如果输入是[227x227x3],要与尺寸为11x11x3的滤波器以步长为4进行卷积,就取输入中的[11x11x3]数据块,然后将其拉伸为长度为11x11x3=363的列向量。重复进行这一过程,因为步长为4,所以输出的宽高为(227-11)/4+1=55,所以得到im2col操作的输出矩阵
X_col
的尺寸是[363x3025],其中每列是拉伸的感受域,共有55x55=3,025个。注意因为感受域之间有重叠,所以输入数据体中的数字在不同的列中可能有重复。 - 卷积层的权重同样伸展成行。例如,如果有大小为[11x11x3]的96个滤波器,这将给出一个大小为[96×363 ]矩阵
W_row
。 - 卷积的结果现在相当于执行一个大矩阵乘法的
np.dot(W_row, X_col)
,它评估每个滤波器和每个接收域位置之间的点积。在我们的示例中,这个操作的输出将是[96×3025 ],给出每个位置的每个滤波器的点积的输出。 - 最终的结果必须重新调整到其正确的输出尺寸[55 x55 x96]
这种方法的缺点在于它会使用大量的内存,因为输入量中的一些值在 X_col
中被复制了多次。然而,好处是矩阵乘法有很多非常有效的实现方式(例如,通常使用 BLAS API。此外,同样的im2col 思想可以被重用来执行汇聚操作,这是我们接下来讨论的。
反向传播。卷积运算(对于数据和权值)的反向传递也是卷积(但在空间上翻转的滤波器)。使用一个1维的例子比较容易演示。
1x1卷积。作为一个旁白,一些论文使用1x1卷积,最先提出的论文是 Network in Network。人们刚开始看见这个1x1卷积的时候比较困惑,尤其是那些具有信号处理专业背景的人。因为信号是2维的,所以1x1卷积就没有意义。但是,在卷积神经网络中不是这样,因为这里是对3个维度进行操作,滤波器和输入数据体的深度是一样的。比如,如果输入是[32x32x3],那么1x1卷积就是在高效地进行3维点积(因为输入深度是3个通道)。
扩张卷积。最近的发展(例如,见 Fisher Yu and Vladlen Koltun 的论文)是将一个更多的超参数引入到称为扩张的CONV 层。到目前为止,我们只讨论了连续的CONV滤波器。然而,有可能在每个细胞之间有空间,称为扩张。作为一个例子,在一维中,大小为3的滤波器W将在输入X上计算:w[0]*x[0] + w[1]*x[1] + w[2]*x[2]
。这是0的膨胀。对于膨胀1,滤波器将计算w[0]*x[0] + w[1]*x[2] + w[2]*x[4]
;换句话说,应用之间存在1的间隙。在某些设置中,扩张卷积与正常卷积结合起来非常有用,因为在很少的层数内更快地汇集输入图片的大尺度特征。例如,如果你将两个3x3 CONV层叠加在上面,那么你可以说服自己,第二层上的神经元是输入的5x5补丁的函数(我们会说这些神经元的有效感受域是5x5)。如果我们使用扩张的卷积,那么这个有效的感受域会生长得更快。
11.2.2 汇聚层
在卷积网络体系结构中,在连续卷积层之间周期性地插入汇集层是常见的。它的功能是逐步减少表示的空间大小,以减少网络中的参数和计算量,从而也控制过拟合。汇集层在输入的每个深度切片上独立操作,并使用MAX操作在空间上重新调整大小。最常见的形式是具有2x2大小的滤波器的汇集层,在宽度和高度两个输入中,每一个深度切片施加2个步幅的降采样,丢弃75%的**。在这种情况下,每个MAX操作是从4个数字中取最大值(也就是在深度切片中某个2x2的区域)。深度维度保持不变。更一般地说,汇集层:
- 输入数据尺寸 W1×H1×D1
- 需要两个超参数:
- 滤波器大小F
- 步幅S
- 输出大学 W2×H2×D2 其中:
- W2=(W1−F)/S+1
- H2=(H1−F)/S+1
- D2=D1
- 因为对输入进行的是固定函数计算,所以没有引入参数
- 在汇聚层中很少使用零填充
值得注意的是,在实践中发现只有两个常见的最大汇聚层的超参数取值:一个池组,F=3,S=2(也称为重叠池),更常见的是F=2,S=2。具有较大接收域的汇聚尺寸的破坏性太强。
常用汇聚。除了最汇聚之外,汇聚单元还可以执行其他功能,例如平均汇聚或甚至L2范数汇聚。过去往往使用平均汇聚,但最近失宠了,现在常用最大汇聚操作,实践证明最大汇聚操作能地工作。
汇集层在输入卷的每个深度切片中独立地空间地对体积进行采样。左:在这个例子中,输入大小的大小[224x224x64 ]与滤波器大小2合并,跨步2到大小[112x112x64 ]的输出音量。请注意,深度不变。右:最常见的下采样操作是max,产生最大池,这里显示的步幅为2。也就是说,每个max操作都是从4个数字中选取(即2x2的方块区域中)。
反向传播。回顾反向传播章节,max(x,y)操作的反向传播有一个简单的解释,即只将梯度传递到在正向传递中具有最高值的输入。因此,在汇集层的正向传递期间,通常会记下最大**的索引(有时也被称为开关),这样在反向传播期间就能高效地计算梯度。
摆脱汇聚。许多人不喜欢汇聚操作,认为没有它我们也可以。例如,力求简单:所有卷积网络 建议丢弃汇聚层,而只支持由重复的CONV层构成的体系结构。为了减少表示的大小,他们建议在CONV层中偶尔使用更大的步幅。在训练良好的生成模型,如变分自动编码器(VAES)或生成对抗网络(GANS)时,丢弃汇聚层也被发现是重要的。很可能未来的架构将很少有没有汇聚层的特征。
11.2.3 归一化层
在ConvNet 架构中,人们提出了有许多类型的归一化层,有时意图实现在生物脑中观察到的抑制方案。然而,这些层已经失去了青睐,因为在实践中,他们的贡献已被证明是最小的,如果有的话。对于各种类型的规范化,请参见 Alex Krizhevsky的 cuda-convnet library API 中的讨论。
11.2.4 全连接层
完全连接层中的神经元与前一层中的所有**具有完全连接,如在常规神经网络中所见。因此,它们的**可以用矩阵乘法计算,然后是偏置偏移。有关更多信息,请参阅笔记部分的神经网络。
11.2.5 全连接层到卷积层的转换
值得注意的是,FC和CONV层之间的唯一区别是CONV层中的神经元仅连接到输入中的局部区域,并且CONV卷中的许多神经元共享参数。然而,两层神经元仍然计算点积,因此它们的函数形式是相同的。因此,可以在FC和CONV层之间转换:
- 对于任一个卷积层,都存在一个能实现和它一样的前向传播函数的全连接层。权重矩阵是一个巨大的矩阵,除了某些特定块(这是因为有局部连接),其余部分都是零。而在其中大部分块中,元素都是相等的(因为参数共享)。
- 相反,任何FC层都可以转换为CONV层。例如,一个具有深度K=4096的FC层,即在某个输入量为7×7×512的情况下,可以等价地表示为一个具有滤波器尺寸F=7、零填充的值P= 0、步幅S=1、卷积层的深度K= 4096的CONV层。换言之,我们将滤波器的大小设置为输入体积的大小,因此输出仅为1×1×4096,因为只有一个单独的深度列覆盖并滑过输入数据体,这样其结果就与普通的FC层的结果相同的结果。
FC -> CONV转换。在这两种转换中,将FC层转换为CONV层的能力在实践中特别有用。考虑一个使用224x224x3图像的ConvNet 架构,然后使用一系列CONV层和汇聚层将图像缩小到7X7X512大小的**卷(在AlexNet中就是这样,通过使用5个汇聚层来对输入数据进行空间上的降采样,每次尺寸下降一半,所以最终空间尺寸为224/2/2/2/2/2=7)。从这里可以看到,AlexNet使用了两个尺寸为4096的全连接层,最后一个有1000个神经元的全连接层用于计算分类评分。我们可以将这3个全连接层中的任意一个转化为卷积层:
- 将第一个连接区域是[7x7x512]的全连接层,转换为滤波器尺寸为F=7的CONV层,这样输出数据体就为[1x1x4096]了
- 将第二个全连接层,转换为滤波器尺寸为F=7的CONV 层,这样输出数据体就为[1x1x4096]
- 同样,将第三个FC层,转换为滤波器尺寸为F=7的CONV 层,这样最终输出数据体就是[1x1x1000]
这些转换中的每一个在实践中可以包括操纵(例如整形)每个FC层中的权重矩阵W到CONV层滤波器。事实证明,这种转换允许我们在一个更大的图像中非常有效地“滑动”原始的对流,在一个单一的向前通过。
实际操作中,每次这样的变换都需要把全连接层的权重W重塑成卷积层的滤波器。那么这样的转化有什么作用呢?它在下面的情况下可以更高效:让卷积网络在一张更大的输入图片上滑动(译者注:即把一张更大的图片的不同区域都分别带入到卷积网络,得到每个区域的得分),得到多个输出,这样的转化可以让我们在单个向前传播的过程中完成上述的操作。
例如,如果我们想让224x224尺寸的浮窗,以步长为32在384x384的图片上滑动,把每个经停的位置都带入卷积网络,最后得到6x6个位置的类别得分。上述的把全连接层转换成卷积层的做法会更简便。如果224x224的输入图片经过卷积层和汇聚层之后得到了[7x7x512]的数组,那么,384x384的大图片直接经过同样的卷积层和汇聚层之后会得到[12x12x512]的数组(因为途径5个汇聚层,尺寸变为384/2/2/2/2/2 = 12)。然后再经过上面由3个全连接层转化得到的3个卷积层,最终得到[6x6x1000]的输出(因为(12 - 7)/1 + 1 = 6)。这个结果正是浮窗在原图经停的6x6个位置的得分!(译者注:这一段的翻译与原文不同,经过了译者较多的修改,使更容易理解)
对于384x384的图像,让(含全连接层)初始ConvNet 以32像素的步幅独立对图像中的224x224块进行多次评价,其效果和使用把全连接层变换为卷积层后的卷积神经网络进行一次前向传播是一样的。
在36个评估共享计算的情况下,将转换后的ConvNet 转发一次比在所有36个位置上迭代原始的ConvNet 更有效。这一技巧经常在实践中被用来获得更好的性能,例如,通常调整图像大小以使其变大,使用转换的神经网络来评估许多空间位置的类得分,然后平均类分数。
最后,如果我们想用步长小于32的浮窗怎么办?用多次的向前传播就可以解决。比如我们想用步长为16的浮窗。那么先使用原图在转化后的卷积网络执行向前传播,然后分别沿宽度,沿高度,最后同时沿宽度和高度,把原始图片分别平移16个像素,然后把这些平移之后的图分别带入卷积网络。(译者注:这一段的翻译与原文不同,经过了译者较多的修改,使更容易理解)
- Net Surgery 上面的代码展示了如何进行转换 (使用 Caffe)
11.3 卷积神经网络体系结构
我们已经看到卷积网络通常只由三个层类型组成:CONV,POOL (默认指最大值汇聚)和FC(全连接)。ReLU**函数也应该算是一层,它逐元素地进行**函数操作。在这一节中,我们将讨论这些是如何堆叠在一起形成整个ConvNets的。
11.3.1 层模式
ConvNet架构的最常见形式是堆切几个CONV-RELU层,之后是POOL层,并重复此模式,直到图像在空间上合并为小尺寸。在某种程度上,过渡到完全连接的层是常见的。最后一个完全连接的层保持输出,例如类分数。换言之,最常见的CONNETE架构遵循以下模式:
INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC
* 号表示重复,POOL? 表示
可选的汇聚层。此外,n>=0(通常为n<=3),m>0,k>=0(通常为k<3)。常见的ConvNet架构有:
-
INPUT -> FC
, 这就是一个线性分类器,N = M = K = 0
. INPUT -> CONV -> RELU -> FC
-
INPUT -> [CONV -> RELU -> POOL]*2 -> FC -> RELU -> FC
. 此处在每个汇聚层之前都有一个卷积层 -
INPUT -> [CONV -> RELU -> CONV -> RELU -> POOL]*3 -> [FC -> RELU]*2 -> FC
此处每个汇聚层前有两个卷积层,这个思路适用于更大更深的网络,因为在执行具有破坏性的汇聚操作前,多重的卷积层可以从输入数据中学习到更多的复杂特征。
几个小滤波器卷积层的组合比一个大滤波器卷积层好:假设你一层一层地重叠了3个3x3的卷积层(层与层之间有非线性**函数)。在这个排列下,第一个卷积层中的每个神经元都对输入数据体有一个3x3的视野。第二个卷积层上的神经元对第一个卷积层有一个3x3的视野,也就是对输入数据体有5x5的视野。同样,在第三个卷积层上的神经元对第二个卷积层有3x3的视野,也就是对输入数据体有7x7的视野。假设不采用这3个3x3的卷积层,而是使用一个单独的有7x7的感受域的卷积层,那么所有神经元的感受域也是7x7,这会就有一些缺点。首先,多个卷积层与非线性的**层交替的结构,比单一卷积层的结构更能提取出深层的更好的特征。其次,假设所有的数据有个通道,那么单独的7x7卷积层将会包含C×(7×7×C)=49C*C个参数,而3个3x3的卷积层的组合仅有3×(C×(3×3×C))=27C*C个参数。直观说来,最好选择带有小滤波器的卷积层组合,而不是用一个带有大的滤波器的卷积层。前者可以表达出输入数据中更多个强力特征,使用的参数也更少。唯一的不足是,在进行反向传播时,中间的卷积层可能会导致占用更多的内存。
最新进展:传统的将层按照线性进行排列的方法已经受到了挑战,挑战来自谷歌的Inception结构和微软亚洲研究院的残差网络(Residual Net)结构。这两个网络(下文案例学习小节中有细节)的特征更加复杂,连接结构也不同。
在实践中:使用任何对ImageNet最有效的方案。如果您在思考架构决策时感到有点疲惫,那么您将很高兴地知道,在90%或更多的应用程序中,您不必担心这些。我想把这一点总结为“不要成为英雄”:你应该看看ImageNet上目前工作最好的体系结构,下载一个预先准备好的模型,然后在你的数据上精简它,而不是为某个问题滚动你自己的架构。你很少需要从头开始训练一个战车,或者从头开始设计一个。我也在深学习学校做了这一点。
11.3.2 层大小模式
到现在为止,我们还没有谈及在一个ConvNet中的每个层中使用的常用超参数的说明。我们将首先陈述调整体系结构大小的一般经验法则,然后按照这些规则讨论符号:
输入层(包含图像)应该能被2整除很多次。常用数字包括32(比如CIFAR-10),64,96(比如STL-10)或224(比如ImageNet卷积神经网络),384和512。
conv层应该使用小的滤波器(例如,3x3或最多5x5),使用S=1的步长,并且至关重要的是,以零填充输入体积,使得conv层不改变输入的空间维度。也就是说,当F=3时,然后使用P=1将保留输入的原始大小。F=5时,p=2。对于一般的F,可以看出,P=(F - 1)/ 2 能保留输入大小。较大的过滤器大小(例如7x7左右),也只有在输入图像的第一个conv层上才用。
汇聚层负责对输入的空间维数进行降维处理。最常见的设置是使用2x2接收字段(即F=2)的最大值汇聚,并以2的步长(即S=2)。请注意,这完全丢弃输入量中的75%的**(由于在宽度和高度上向下采样2)。另一个不太常见的设置是使用3x3接收域,步幅为2。很少将最大值汇聚的接收字段大小设置为大于3,因为该汇聚过于有损和具有攻击性。这通常会导致性能下降。
减少尺寸设置的问题。上述方案是令人满意的,因为所有CONV层都保留其输入的空间大小,而POOL层单独负责在空间上对卷进行降维处理。如果使用的步长大于1并且不对卷积层的输入数据使用零填充,那么就必须非常仔细地监督输入数据体通过整个卷积神经网络结构的过程,确认所有的步长和滤波器都尺寸互相吻合,ConvNet的结构美妙对称地联系在一起。
为什么在CONV中使用1的步幅?更小的步伐在实践中能更好地工作。此外,如已经提到的,步长1允许我们将所有空间降采样留给POOL层,其中CONV层仅对输入体积进行深度转换。
为什么要使用填充?除了在CONV之后保持空间大小恒定的上述好处之外,这样做实际上提高了性能。如果CONV层不是对输入进行零填充,而只执行有效的卷积,那么在每次CONV之后,卷的大小将减少少量,并且边界处的信息将被快速“洗掉”。
基于内存限制折衷。在某些情况下(特别是在早期的神经网络体系结构中),根据以上规则,内存的使用量可能迅速提升。例如,用三个3x3 CONV层过滤224x224x3图像,每层64个过滤器,零填充为1,将创建三个大小为[224x224x64 ]的**体积。这相当于总共大约1000万个**,或者72MB的内存(每个图像,用于**和梯度)。由于GPU经常被内存所限制,所以可能需要妥协。在实践中,人们更倾向于只在网络的第一个CONV层做出妥协。例如,一个折衷方案可能是使用具有7x7和步幅2的滤波器尺寸的第一CONV层(如ZF网络中所见)。作为另一个例子,AlexNet 使用11x11的滤波器大小和4的步长。
11.3.3 案例学习 (LeNet / AlexNet / ZFNet / GoogLeNet / VGGNet)
卷积网络中有几个有名的体系结构。最常见的是:
- LeNet. 第一个成功应用是由Yann LeCu在1990年代开发的,其中最著名的是用于读取邮政编码、数字等的LeNet架构。
- AlexNet. 在计算机视觉中普及卷积网络的第一件工作是由Alex Krizhevsky、Ilya Sutskever和Geoff Hinton开发的AlexNet。AlexNet在2012年被提交到ImageNet ILSVRC竞赛中,并且显著优于第二名(16%的top5错误率,第二名是26%的top5错误率)。该网络具有与LeNet非常相似的体系结构,但是更深、更大,并且具有彼此叠加的卷积层(以前通常只有一个CONV层紧接着一个POOL层)。
- ZF Net. ILSVRC 2013年的赢家是来自Matthew Zeiler和Rob Fergus的卷积网络。它被称为 ZFNet (Zever和FrgUs网络的简称)。它改进了AlexNet,调整了体系结构超参数,特别是通过扩展中间卷积层的大小并使第一层的步幅和滤波器大小更小。
- GoogLeNet. ILSVRC 2014年赢家是来自谷歌的 Szegedy 等人的卷积网络。它的主要贡献是开发了一个初始模块,大大减少了网络中的参数数量(4M,而AlexNet有60M)。此外,还使用平均汇聚来代替ConvNet顶部的全连接层,从而消除了大量似乎无关紧要的参数。GoogLeNet中还有几个后续版本,最新的版本是 Inception-v4。
- VGGNet. ILVRC 2014中的亚军是来自Karen Simonyan和Andrew Zisserman的网络,后来称为 VGGNet。它的主要贡献在于表明了网络的深度是良好性能的关键部件。它们最后的最佳网络包含16个CONV/FC层,并且吸引人的是,具有极其同质的架构,从头到尾只执行3x3卷积和2x2池。他们的预训练模型适用于Caffe中的即插即用。VGNET的缺点是评估更昂贵和使用更多的内存和参数(140M)。这些参数中的大多数在第一个完全连接的层中,并且自从发现这些FC层可以在不降低性能的情况下被去除,从而显著地减少了参数的数量。
- ResNet. 由 何恺明等人开发的残差网络。是ILVRC 2015的获胜者。它具有特殊的跳跃连接和大量使用批处理归一化。该架构在网络的末端也没有使用完全连接的层。读者可以参考何恺明的介绍(视频、PPT)以及最近在Torch中再现这些网络的一些实验。ResNets是目前为止最先进的卷积神经网络模型,并且是在实践中使用ConvNets的默认选择(截至2016年5月10日)。何开明等最近的工作是对原始结构做一些优化,可以看论文Identity Mappings in Deep Residual Networks(2016年3月出版)。
VGGNet 的细节。让我们更详细地分解VGGNET,作为一个案例研究。整个VGGNet由CONV层和POOL层组成,CONV层用步长1和零填充为1,3x3的卷积核,POOL层用步长2执行2x2 最大值汇聚(没有零填充)。可以写出处理过程中每一步数据体尺寸的变化,然后对数据尺寸和整体权重的数量进行查看:
INPUT: [224x224x3] memory: 224*224*3=150K weights: 0
CONV3-64: [224x224x64] memory: 224*224*64=3.2M weights: (3*3*3)*64 = 1,728
CONV3-64: [224x224x64] memory: 224*224*64=3.2M weights: (3*3*64)*64 = 36,864
POOL2: [112x112x64] memory: 112*112*64=800K weights: 0
CONV3-128: [112x112x128] memory: 112*112*128=1.6M weights: (3*3*64)*128 = 73,728
CONV3-128: [112x112x128] memory: 112*112*128=1.6M weights: (3*3*128)*128 = 147,456
POOL2: [56x56x128] memory: 56*56*128=400K weights: 0
CONV3-256: [56x56x256] memory: 56*56*256=800K weights: (3*3*128)*256 = 294,912
CONV3-256: [56x56x256] memory: 56*56*256=800K weights: (3*3*256)*256 = 589,824
CONV3-256: [56x56x256] memory: 56*56*256=800K weights: (3*3*256)*256 = 589,824
POOL2: [28x28x256] memory: 28*28*256=200K weights: 0
CONV3-512: [28x28x512] memory: 28*28*512=400K weights: (3*3*256)*512 = 1,179,648
CONV3-512: [28x28x512] memory: 28*28*512=400K weights: (3*3*512)*512 = 2,359,296
CONV3-512: [28x28x512] memory: 28*28*512=400K weights: (3*3*512)*512 = 2,359,296
POOL2: [14x14x512] memory: 14*14*512=100K weights: 0
CONV3-512: [14x14x512] memory: 14*14*512=100K weights: (3*3*512)*512 = 2,359,296
CONV3-512: [14x14x512] memory: 14*14*512=100K weights: (3*3*512)*512 = 2,359,296
CONV3-512: [14x14x512] memory: 14*14*512=100K weights: (3*3*512)*512 = 2,359,296
POOL2: [7x7x512] memory: 7*7*512=25K weights: 0
FC: [1x1x4096] memory: 4096 weights: 7*7*512*4096 = 102,760,448
FC: [1x1x4096] memory: 4096 weights: 4096*4096 = 16,777,216
FC: [1x1x1000] memory: 1000 weights: 4096*1000 = 4,096,000
TOTAL memory: 24M * 4 bytes ~= 93MB / image (only forward! ~*2 for bwd)
TOTAL params: 138M parameters
通常,卷积网络的大部分内存(以及计算时间)都用在早期CONV层中,并且大部分参数都在最后的FC层中。在这个例子中,全部参数有140M,但第一个全连接层就包含了100M的参数。
11.3.4 计算考量
构建ConvNet 架构时要注意的最大瓶颈是内存瓶颈。许多现代GPU的内存限制为3/4/6GB,最好的GPU内存约为12GB。要注意三种内存占用来源:
- 来源于中间数据体尺寸::卷积神经网络中的每一层中都有**数据体的原始数值,以及损失函数对它们的梯度(和**数据体尺寸一致)。通常,大部分**数据都是在网络中靠前的层中(比如第一个卷积层)。在训练时,这些数据需要放在内存中,因为反向传播的时候还会用到。但是在测试时可以聪明点:让网络在测试运行时候每层都只存储当前的**数据,然后丢弃前面层的**数据,这样就能减少巨大的**数据量。
- 来自参数尺寸:即整个网络的参数的数量,在反向传播时它们的梯度值,以及使用momentum、Adagrad或RMSProp等方法进行最优化时每一步计算的缓存。因此,存储参数向量的内存通常需要在参数向量的容量基础上乘以3或者更多。
- ConvNet的实现还必须维护零散内存,例如图像数据批量、扩充的数据等。
一旦粗略估计了值的总数(对于**数据体、梯度和各种零散数据),这个数字应该转换为GB的大小。取值的数量,乘以4以获得原始的字节数量(因为每个浮点数是4字节,或者可能乘以8以获得双精度),然后多次除以1024以获得KB、MB和最终GB的内存量。如果您的网络不适合,一个常见的“使它适合”的启发式方法是减少批处理大小,因为大多数内存通常被**数据体所消耗。
11.4延伸阅读
- Soumith benchmarks for CONV performance
- ConvNetJS CIFAR-10 demo 允许您调试ConvNet 架构,在浏览器中实时查看结果和计算
- Caffe, 最流行的卷积神经网络库之一
- State of the art ResNets in Torch7
斯坦福大学计算机视图课程,青星大学 翻译整理
推荐阅读
-
11、卷积神经网络:结构、卷积/汇集层
-
全网最全经典卷积神经网络架构汇总—ILSVRC竞赛(ImageNet竞赛)优胜网络结构
-
深度之眼Pytorch打卡(十五):Pytorch卷积神经网络部件——转置卷积操作与转置卷积层(对转置卷积操作全网最细致分析,转置卷积的stride与padding,转置与反卷积名称论证)
-
pytorch(2) ---实现二层卷积神经网络
-
【卷积神经网络结构专题】一文详解LeNet(附代码实现)
-
深度学习笔记11:利用numpy搭建一个卷积神经网络
-
Lenet-5卷积神经网络结构详解(一)——原理解析
-
两层卷积神经网络训练mnist数据集
-
编写C语言版本的卷积神经网络CNN之二:CNN网络的总体结构
-
卷积神经网络:定义卷积层