1. 卷积神经网络结构介绍
如果用全连接神经网络处理大尺寸图像具有三个明显的缺点:
(1)首先将图像展开为向量会丢失空间信息;
(2)其次参数过多效率低下,训练困难;
(3)同时大量的参数也很快会导致网络过拟合。
而使用卷积神经网络可以很好地解决上面的三个问题。
与常规神经网络不同,卷积神经网络的各层中的神经元是3维排列的:宽度、高度和深度。其中的宽度和高度是很好理解的,因为本身卷积就是一个二维模板,但是在卷积神经网络中的深度指的是激活数据体的第三个维度,而不是整个网络的深度,整个网络的深度指的是网络的层数。举个例子来理解什么是宽度,高度和深度,假如使用CIFAR-10中的图像是作为卷积神经网络的输入,该输入数据体的维度是32x32x3(宽度,高度和深度)。我们将看到,层中的神经元将只与前一层中的一小块区域连接,而不是采取全连接方式。对于用来分类CIFAR-10中的图像的卷积网络,其最后的输出层的维度是1x1x10,因为在卷积神经网络结构的最后部分将会把全尺寸的图像压缩为包含分类评分的一个向量,向量是在深度方向排列的。下面是例子:
![](https://i-blog.csdnimg.cn/blog_migrate/c11708d85a2b0dbfec7c2c9aba24340e.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/a38123ce015eeb61b453d6503a0cf92a.jpeg)
图1中左侧是一个3层的神经网络;右侧是一个卷积神经网络,将它的神经元在成3个维度(宽、高和深度)进行排列。卷积神经网络的每一层都将3D的输入数据变化为神经元3D的激活数据并输出。在图1的右侧,红色的输入层代表输入图像,所以它的宽度和高度就是图像的宽度和高度,它的深度是3(代表了红、绿、蓝3种颜色通道),与红色相邻的蓝色部分是经过卷积和池化之后的激活值(也可以看做是神经元) ,后面是接着的卷积池化层。
2. 构建卷积神经网络的各种层
卷积神经网络主要由这几类层构成:输入层、卷积层,ReLU层、池化(Pooling)层和全连接层(全连接层和常规神经网络中的一样)。通过将这些层叠加起来,就可以构建一个完整的卷积神经网络。在实际应用中往往将卷积层与ReLU层共同称之为卷积层,所以卷积层经过卷积操作也是要经过激活函数的。具体说来,卷积层和全连接层(CONV/FC)对输入执行变换操作的时候,不仅会用到激活函数,还会用到很多参数,即神经元的权值w和偏差b;而ReLU层和池化层则是进行一个固定不变的函数操作。卷积层和全连接层中的参数会随着梯度下降被训练,这样卷积神经网络计算出的分类评分就能和训练集中的每个图像的标签吻合了。
2.1 卷积层
卷积层是构建卷积神经网络的核心层,它产生了网络中大部分的计算量。注意是计算量而不是参数量。
2.1.1 卷积层作用
1. 滤波器的作用或者说是卷积的作用。卷积层的参数是有一些可学习的滤波器集合构成的。每个滤波器在空间上(宽度和高度)都比较小,但是深度和输入数据一致(这一点很重要,后面会具体介绍)。直观地来说,网络会让滤波器学习到当它看到某些类型的视觉特征时就激活,具体的视觉特征可能是某些方位上的边界,或者在第一层上某些颜色的斑点,甚至可以是网络更高层上的蜂巢状或者车轮状图案。
2. 可以被看做是神经元的一个输出。神经元只观察输入数据中的一小部分,并且和空间上左右两边的所有神经元共享参数(因为这些数字都是使用同一个滤波器得到的结果)。
3. 降低参数的数量。这个由于卷积具有“权值共享”这样的特性,可以降低参数数量,达到降低计算开销,防止由于参数过多而造成过拟合。
2.1.2 感受野(重点理解)
在处理图像这样的高维度输入时,让每个神经元都与前一层中的所有神经元进行全连接是不现实的。相反,我们让每个神经元只与输入数据的一个局部区域连接。该连接的空间大小叫做神经元的感受野(receptive field),它的尺寸是一个超参数(其实就是滤波器的空间尺寸)。在深度方向上,这个连接的大小总是和输入量的深度相等。需要再次强调的是,我们对待空间维度(宽和高)与深度维度是不同的:连接在空间(宽高)上是局部的,但是在深度上总是和输入数据的深度一致,这一点会在下面举例具体说明。
![](https://i-blog.csdnimg.cn/blog_migrate/2b2d7d2f433e5d8c2d87b8c3f0ca0d53.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d13db3698f64006dea2e8fa46e22d6f7.png)
在图 2 中展现的卷积神经网络的一部分,其中的红色为输入数据,假设输入数据体尺寸为[32x32x3](比如CIFAR-10的RGB图像),如果感受野(或滤波器尺寸)是5x5,那么卷积层中的每个神经元会有输入数据体中[5x5x3]区域的权重,共5x5x3=75个权重(还要加一个偏差参数)。注意这个连接在深度维度上的大小必须为3,和输入数据体的深度一致。其中还有一点需要注意,对应一个感受野有75个权重,这75个权重是通过学习进行更新的,所以很大程度上这些权值之间是不相等(也就对于同一个卷积核,它对于与它连接的输入的每一层的权重都是独特的,不是同样的权重重复输入层层数那么多次就可以的)。在这里相当于前面的每一个层对应一个传统意义上的卷积模板,每一层与自己卷积模板做完卷积之后,再将各个层的结果加起来,再加上偏置,注意是一个偏置,无论输入输入数据是多少层,一个卷积核就对应一个偏置。
2.1.3 神经元的空间排列
感受野讲解了卷积层中每个神经元与输入数据体之间的连接方式,但是尚未讨论输出数据体中神经元的数量,以及它们的排列方式。3个超参数控制着输出数据体的尺寸:深度(depth),步长(stride)和零填充(zero-padding)。
(1) 输出数据体的深度:它是一个超参数,和使用的滤波器的数量一致,而每个滤波器在输入数据中寻找一些不同的东西,即图像的某些特征。如图2 所示,将沿着深度方向排列、感受野相同的神经元集合称为深度列(depth column),也有人使用纤维(fibre)来称呼它们。
(2) 在滑动滤波器的时候,必须指定步长。当步长为1,滤波器每次移动1个像素;当步长为2,滤波器滑动时每次移动2个像素,当然步长也可以是不常用的3,或者更大的数字,但这些在实际中很少使用)。这个操作会让输出数据体在空间上变小。
(3) 有时候将输入数据体用0在边缘处进行填充是很方便的。这个零填充(zero-padding)的尺寸是一个超参数。零填充有一个良好性质,即可以控制输出数据体的空间尺寸(最常用的是用来保持输入数据体在空间上的尺寸,使得输入和输出的宽高都相等)。
输出数据体在空间上的尺寸 可以通过输入数据体尺寸
,卷积层中神经元的感受野尺寸(F),步长(S),滤波器数量(K)和零填充的数量(P)计算输出出来。
![](https://i-blog.csdnimg.cn/blog_migrate/23359ee2563a34185815f899816a8365.png)
![](https://i-blog.csdnimg.cn/blog_migrate/66770fa97fc985464c5789ff4ff40469.png)
一般说来,当步长S=1时,零填充的值是P=(F-1)/2,这样就能保证输入和输出数据体有相同的空间尺寸。
步长的限制:注意这些空间排列的超参数之间是相互限制的。举例说来,当输入尺寸W=10,不使用零填充 P=0,滤波器尺寸 F=3,此时步长 S=2 是行不通,因为 (W-F+2P)/S+1=(10-3+0)/2+1=4.5,结果不是整数,这就是说神经元不能整齐对称地滑过输入数据体。因此,这些超参数的设定就被认为是无效的,一个卷积神经网络库可能会报出一个错误,通过修改零填充值、修改输入数据体尺寸,或者其他什么措施来让设置合理。在后面的卷积神经网络结构小节中,读者可以看到合理地设置网络的尺寸让所有的维度都能正常工作,是相当让人头痛的事;而使用零填充和遵守其他一些设计策略将会有效解决这个问题。
2.1.4 权值共享
在卷积层中权值共享是用来控制参数的数量。假如在一个卷积核中,每一个感受野采用的都是不同的权重值(卷积核的值不同),那么这样的网络中参数数量将是十分巨大的。
权值共享是基于这样的一个合理的假设:如果一个特征在计算某个空间位置 (x1,y1)(x1,y1) 的时候有用,那么它在计算另一个不同位置 (x2,y2)(x2,y2) 的时候也有用。基于这个假设,可以显著地减少参数数量。换言之,就是将深度维度上一个单独的2维切片看做深度切片(depth slice),比如一个数据体尺寸为[55x55x96]的就有96个深度切片,每个尺寸为[55x55],其中在每个深度切片上的结果都使用同样的权重和偏差获得的。在这样的参数共享下,假如一个例子中的第一个卷积层有96个卷积核,那么就有96个不同的权重集了,一个权重集对应一个深度切片,如果卷积核的大小是 11x11的,图像是RGB 3 通道的,那么就共有96x11x11x3=34,848个不同的权重,总共有34,944个参数(因为要+96个偏差),并且在每个深度切片中的55x55 的结果使用的都是同样的参数。
在反向传播的时候,都要计算每个神经元对它的权重的梯度,但是需要把同一个深度切片上的所有神经元对权重的梯度累加,这样就得到了对共享权重的梯度。这样,每个切片只更新一个权重集。这样做的原因可以通过下面这张图进行解释
![](https://i-blog.csdnimg.cn/blog_migrate/f839bc49bcf185aaecd3c8625c23d95e.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/383af46acc8ba00edd18364bb6dd9031.jpeg)
如上图所示,左侧的神经元是将每一个感受野展开为一列之后串联起来(就是展开排成一列,同一层神经元之间不连接)。右侧的 Deep1i 是深度为1的神经元的第 i 个, Deep2i 是深度为2的神经元的第 i 个,同一个深度的神经元的权值都是相同的,黄色的都是相同的(上面4个与下面4个的参数相同),蓝色都是相同的。所以现在回过头来看上面说的卷积神经网络的反向传播公式对梯度进行累加求和也是基于这点考虑(同一深度的不同神经元共用一组参数,所以累加);而每个切片只更新一个权重集的原因也是这样的,因为从图3 中可以看到,不同深度的神经元不会公用相同的权重,所以只能更新一个权重集。
注意,如果在一个深度切片中的所有权重都使用同一个权重向量,那么卷积层的前向传播在每个深度切片中可以看做是在计算神经元权重和输入数据体的卷积(这就是“卷积层”名字由来)。这也是为什么总是将这些权重集合称为滤波器(filter)(或卷积核(kernel)),因为它们和输入进行了卷积。
注意,有时候参数共享假设可能没有意义,特别是当卷积神经网络的输入图像是一些明确的中心结构时候。这时候我们就应该期望在图片的不同位置学习到完全不同的特征(而一个卷积核滑动地与图像做卷积都是在学习相同的特征)。一个具体的例子就是输入图像是人脸,人脸一般都处于图片中心,而我们期望在不同的位置学习到不同的特征,比如眼睛特征或者头发特征可能(也应该)会在图片的不同位置被学习。在这个例子中,通常就放松参数共享的限制,将层称为局部连接层(Locally-Connected Layer)。
2.1.5 卷积层的超参数及选择
由于参数共享,每个滤波器包含 个权重(字符的具体含义在2.1.3中有介绍),卷积层一共有
个权重和 K 个偏置。在输出数据体中,第d个深度切片(空间尺寸是
),用第d个滤波器和输入数据进行有效卷积运算的结果(使用步长S),最后在加上第d个偏差。
对这些超参数,常见的设置是 F=3,S=1,P=1,F=3,S=1,P=1。同时设置这些超参数也有一些约定俗成的惯例和经验,可以在下面的“卷积神经网络结构”中查看。
2.1.6 卷积层演示
因为3D数据难以可视化,所以所有的数据(输入数据体是蓝色,权重数据体是红色,输出数据体是绿色)都采取将深度切片按照列的方式排列展现。输入数据体的尺寸是W1=5,H1=5,D1=3W1=5,H1=5,D1=3,卷积层参数K=2,F=3,S=2,P=1K=2,F=3,S=2,P=1。就是说,有2个滤波器,滤波器的尺寸是3⋅33⋅3,它们的步长是2。因此,输出数据体的空间尺寸是(5−3+2)/2+1=3(5−3+2)/2+1=3。注意输入数据体使用了零填充P=1P=1,所以输入数据体外边缘一圈都是0。下面的例子在绿色的输出激活数据上循环演示,展示了其中每个元素都是先通过蓝色的输入数据和红色的滤波器逐元素相乘,然后求其总和,最后加上偏差得来。
![](https://i-blog.csdnimg.cn/blog_migrate/8ba8a0f75d2a4bf03ecbb3783aae6310.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f65f665a059e9202d9bbe3986ac64462.png)
2.1.7 用矩阵乘法实现卷积
卷积运算本质上就是在滤波器和输入数据的局部区域间做点积。卷积层的常用实现方式就是利用这一点,将卷积层的前向传播变成一个巨大的矩阵乘法。
(1) 输入图像的局部区域被 im2coim2co l操作拉伸为列。比如输入是[227x227x3],要与尺寸为11x11x3的滤波器以步长为4进行卷积,就依次取输入中的[11x11x3]数据块,然后将其拉伸为长度为11x11x3=363的列向量。重复进行这一过程,因为步长为4,所以经过卷积后的宽和高均为(227-11)/4+1=55,共有55x55=3,025个个神经元。因为每一个神经元实际上都是对应有 363 的列向量构成的感受野,即一共要从输入上取出 3025 个 363 维的列向量。所以经过im2col操作得到的输出矩阵 XcolXcol 的尺寸是[363x3025],其中每列是拉伸的感受野。注意因为感受野之间有重叠,所以输入数据体中的数字在不同的列中可能有重复。
(2) 卷积层的权重也同样被拉伸成行。举例,如果有96个尺寸为[11x11x3]的滤波器,就生成一个矩阵WrowWrow,尺寸为[96x363]。
(3) 现在卷积的结果和进行一个大矩阵乘法 np.dot(Wrow,Xcol)np.dot(Wrow,Xcol) 是等价的了,能得到每个滤波器和每个感受野间的点积。在我们的例子中,这个操作的输出是[96x3025],给出了每个滤波器在每个位置的点积输出。注意其中的 np.dotnp.dot计算的是矩阵乘法而不是点积。
(4) 结果最后必须被重新变为合理的输出尺寸[55x55x96]。
这个方法的缺点就是占用内存太多,因为在输入数据体中的某些值在XcolXcol中被复制了多次;优点在于矩阵乘法有非常多的高效底层实现方式(比如常用的BLAS API)。还有,同样的im2col思路可以用在池化操作中。反向传播:卷积操作的反向传播(同时对于数据和权重)还是一个卷积(但是和空间上翻转的滤波器)。使用一个1维的例子比较容易演示。这两部分中,不是很懂如何用矩阵的形式进行汇聚操作和反向传播。
2.1.8 其他形式的卷积操作
1x1卷积:一些论文中使用了1x1的卷积,这个方法最早是在论文Network in Network中出现。人们刚开始看见这个1x1卷积的时候比较困惑,尤其是那些具有信号处理专业背景的人。因为信号是2维的,所以1x1卷积就没有意义。但是,在卷积神经网络中不是这样,因为这里是对3个维度进行操作,滤波器和输入数据体的深度是一样的。比如,如果输入是[32x32x3],那么1x1卷积就是在高效地进行3维点积(因为输入深度是3个通道);另外的一种想法是将这种卷积的结果看作是全连接层的一种实现方式,详见本文2.4.2 部分。
扩张卷积:最近一个研究(Fisher Yu和Vladlen Koltun的论文)给卷积层引入了一个新的叫扩张(dilation)的超参数。到目前为止,我们只讨论了卷积层滤波器是连续的情况。但是,让滤波器中元素之间有间隙也是可以的,这就叫做扩张。如图5 为进行1扩张。
![](https://i-blog.csdnimg.cn/blog_migrate/d79e509d0b7eecffe068db3795b8efb9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/992248fa327fa042eb68b2d367ed4b86.png)
在某些设置中,扩张卷积与正常卷积结合起来非常有用,因为在很少的层数内更快地汇集输入图片的大尺度特征。比如,如果上下重叠2个3x3的卷积层,那么第二个卷积层的神经元的感受野是输入数据体中5x5的区域(可以成这些神经元的有效感受野是5x5,如图5 所示)。如果我们对卷积进行扩张,那么这个有效感受野就会迅速增长。
2.2 池化层
通常在连续的卷积层之间会周期性地插入一个池化层。它的作用是逐渐降低数据体的空间尺寸,这样的话就能减少网络中参数的数量,使得计算资源耗费变少,也能有效控制过拟合。汇聚层使用 MAX 操作,对输入数据体的每一个深度切片独立进行操作,改变它的空间尺寸。最常见的形式是汇聚层使用尺寸2x2的滤波器,以步长为2来对每个深度切片进行降采样,将其中75%的激活信息都丢掉。每个MAX操作是从4个数字中取最大值(也就是在深度切片中某个2x2的区域),深度保持不变。
汇聚层的一些公式:输入数据体尺寸 ,有两个超参数:空间大小FF和步长SS;输出数据体的尺寸
,其中
![](https://i-blog.csdnimg.cn/blog_migrate/abfa5dcaf418c6beba882fbd7e465ad9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7d339ee7588ec9a7b9e1c80e1a970f07.png)
这里面与之前的卷积的尺寸计算的区别主要在于两点,首先在池化的过程中基本不会进行另补充;其次池化前后深度不变。
在实践中,最大池化层通常只有两种形式:一种是F=3,S=2F=3,S=2,也叫重叠汇聚(overlapping pooling),另一个更常用的是F=2,S=2F=2,S=2。对更大感受野进行池化需要的池化尺寸也更大,而且往往对网络有破坏性。
普通池化(General Pooling):除了最大池化,池化单元还可以使用其他的函数,比如平均池化(average pooling)或L-2范式池化(L2-norm pooling)。平均池化历史上比较常用,但是现在已经很少使用了。因为实践证明,最大池化的效果比平均池化要好。
反向传播:回顾一下反向传播的内容,其中max(x,y)函数的反向传播可以简单理解为将梯度只沿最大的数回传。因此,在向前传播经过汇聚层的时候,通常会把池中最大元素的索引记录下来(有时这个也叫作道岔(switches)),这样在反向传播的时候梯度的路由就很高效。(具体如何实现我也不是很懂)。
不使用汇聚层:很多人不喜欢汇聚操作,认为可以不使用它。比如在Striving for Simplicity: The All Convolutional Net一文中,提出使用一种只有重复的卷积层组成的结构,抛弃汇聚层。通过在卷积层中使用更大的步长来降低数据体的尺寸。有发现认为,在训练一个良好的生成模型时,弃用汇聚层也是很重要的。比如变化自编码器(VAEs:variational autoencoders)和生成性对抗网络(GANs:generative adversarial networks)。现在看起来,未来的卷积网络结构中,可能会很少使用甚至不使用汇聚层。
2.3 归一化层
在卷积神经网络的结构中,提出了很多不同类型的归一化层,有时候是为了实现在生物大脑中观测到的抑制机制。但是这些层渐渐都不再流行,因为实践证明它们的效果即使存在,也是极其有限的。
2.4 全连接层
这个常规神经网络中一样,它们的激活可以先用矩阵乘法,再加上偏差。
2.4.1 将卷积层转化成全连接层
对于任一个卷积层,都存在一个能实现和它一样的前向传播函数的全连接层。该全连接层的权重是一个巨大的矩阵,除了某些特定块(感受野),其余部分都是零;而在非 0 部分中,大部分元素都是相等的(权值共享),具体可以参考图3。如果把全连接层转化成卷积层,以输出层的 Deep11 为例,与它有关的输入神经元只有上面四个,所以在权重矩阵中与它相乘的元素,除了它所对应的4个,剩下的均为0,这也就解释了为什么权重矩阵中有为零的部分;另外要把“将全连接层转化成卷积层”和“用矩阵乘法实现卷积”区别开,这两者是不同的,后者本身还是在计算卷积,只不过将其展开为矩阵相乘的形式,并不是”将全连接层转化成卷积层”,所以除非权重中本身有零,否则用矩阵乘法实现卷积的过程中不会出现值为0的权重。
2.4.2 将全连接层转化成卷积层
任何全连接层都可以被转化为卷积层。比如,一个K=4096的全连接层,输入数据体的尺寸是 7×7×5127×7×512,这个全连接层可以被等效地看做一个F=7,P=0,S=1,K=4096,F=7,P=0,S=1,K=4096的卷积层。换句话说,就是将滤波器的尺寸设置为和输入数据体的尺寸设为一致的。因为只有一个单独的深度列覆盖并滑过输入数据体,所以输出将变成1×1×40961×1×4096,这个结果就和使用初始的那个全连接层一样了。这个实际上也很好理解,因为,对于其中的一个卷积滤波器,这个滤波器的的深度为512,也就是说,虽然这个卷积滤波器的输出只有1个,但是它的权重有7×7×5127×7×512,相当于卷积滤波器的输出为一个神经元,这个神经元与上一层的所有神经元相连接,而这样与前一层所有神经元相连接的神经元一共有4096个,这不就是一个全连接网络嘛~
在上述的两种变换中,将全连接层转化为卷积层在实际运用中更加有用。假设一个卷积神经网络的输入是224x224x3的图像,一系列的卷积层和汇聚层将图像数据变为尺寸为7x7x512的激活数据体(在AlexNet中就是这样,通过使用5个汇聚层来对输入数据进行空间上的降采样,每次尺寸下降一半,所以最终空间尺寸为224/2/2/2/2/2=7)。从这里可以看到,AlexNet使用了两个尺寸为4096的全连接层,最后一个有1000个神经元的全连接层用于计算分类评分。我们可以将这3个全连接转化为3个卷积层:
(1) 针对第一个连接区域是[7x7x512]的全连接层,令其滤波器尺寸为F=7,这样输出数据体就为[1x1x4096]了。
(2) 针对第二个全连接层,令其滤波器尺寸为F=1,这样输出数据体为[1x1x4096]。
(3) 对最后一个全连接层也做类似的,令其F=1,最终输出为[1x1x1000]。
这样做的目的是让卷积网络在一张更大的输入图片上滑动,得到多个输出,这样的转化可以让我们在单个向前传播的过程中完成上述的操作。
举个例子,如果我们想让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的图像,让(含全连接层)的初始卷积神经网络以32像素的步长独立对图像中的224x224块进行多次评价,其效果和使用把全连接层变换为卷积层后的卷积神经网络进行一次前向传播是一样的。自然,相较于使用被转化前的原始卷积神经网络对所有36个位置进行迭代计算,使用转化后的卷积神经网络进行一次前向传播计算要高效得多,因为36次计算都在共享计算资源。
这里有几个问题,首先为什么是以32为步长,如果我以64为步长呢?再或者如果我们想用步长小于32(如16)的浮窗怎么办?
首先回答其中的第一个问题。这个是因为其中一个有五个汇聚层,因为25=3225=32,也就是在原始图像上的宽或者高增加 3232 个像素,经过这些卷积和汇聚后,将变为一个像素。现在进行举例说明,虽然例子并没有32那么大的尺寸,但是意义都是一样的。假设原始图像的大小为 4×4,卷积核 F=3,S=1,P=1F=3,S=1,P=1,而较大的图像的尺寸为 8×8,假设对图像进行两层的卷积池化,在较大的图像上以步长为4进行滑动(22=422=4),如图5所示
![](https://i-blog.csdnimg.cn/blog_migrate/c3e25394ce93c24a35a74c63ce28d495.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3be5d54ec4bbf337cefcce5c182e980e.png)
对原始图像(图5左图红框)进行卷积得到的结果是图5右图红色框内的结果,使用步长为4在较大的图像获得的结果为图5中右侧四种颜色加在一起的样子。所以以步长为4在8x8的图片上滑动,把每个经停的位置都带入卷积网络,最后得到2x2个位置的卷积结果,但是如果直接使用卷积核 F=3,S=1,P=1F=3,S=1,P=1进行两次卷积池化的话,得到的结果的大小显然也是4×4的。
所以从获得结果来看,这两者是相同的,但是不同点在哪呢?如图6所示,是在整个图像上进行卷积运算和以步长为4在8x8的图片上滑动所经停的第一个位置,这两种方法使用相同的卷积核进行计算的对比图。
![](https://i-blog.csdnimg.cn/blog_migrate/00efbf4e16ba7b1e15542e53261e3cd4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/abab853968a99e07169cd4d9a9c66a70.png)
如图6所示,左图代表使用整张图像时计算a点处的卷积,右图代表使用滑动的方法第一次经停图像上a点的卷积,两张图中的a点是同一个a点。虽然同一个卷积模板进行计算,但是在计算卷积的过程是不同的!因为在右图中a的右侧及右下侧是0,而在左图中是原始的像素值,所以计算的卷积一定是不同的。但是要怎么理解这样的差别呢?这要从补零的意义讲起,补零是因为如果不补零的话,图像经过卷积之后的尺寸会小于原始的尺寸,补零可以保证图像的尺寸不变,所以归根结底补零实际上是一种图像填充的方法。左图中a的右边及右下角的像素是原始图像的像素,相当于在计算a点的时候,不是用0进行的补充,而是原始像素值进行补充,这样的不仅可以保持卷积前后图像的大小不变,而且可以这种图像填充方法得到的结果显然要比填0更接近与原始图像,保留的信息更多。
小节
(1) 用一整图像进行卷积和在较大的图像上通过滑动窗提取出一个个子图象进行卷积得到的效果是相同的。
(2) 可以这样做的主要原因在于将最后的全连接层改写成了卷积层。
(3) 在一整章图像做卷积的效率要远远高于在图像上滑动的效率,因为前者只需要依次前向传播,而后者需要多次
(4) 用整张图像计算与滑动窗口的方法对比,所补充的零更少(如上所讲,不用零而是用在其旁边的像素代替),提取的信息损失的更少。
即,用整张图像直接计算卷积不仅仅在效率上高于使用滑动窗口的方法,而且更多的保留了图像的细节,完胜!
另外还可以得到另一个结论,当在较大的图像上以步长为 2L2L进行滑动时,其效果与在有直接在大图像上进行卷积得到的结果上以步长为1移动是一样的。如图5中的大红色框对应小红色框,大黄色框对应小黄色框。所以当步长为64时,将相当于以步长为2在大图的卷积结果上移动。
对于第二个问题,如果我非要以12为步长呢?是可以的,只是这个时候所获得结果不再是如图5的那种滑动的计算方式了。还是举例说明,不过为了方便说明改变了一下尺寸。将步长32改为4,将步长16改为2进行分析。假如说原始的输入图像为一个 4×4 的图像,现在将使用一个比原来大的图像,是一个8×8的图像,使用卷积核为 4×4 大小,步长为4,则在图像进行卷积运算的如图6左侧的4个部分(红黄绿蓝),而图6 右侧的是步长为2时与原始相比增加的部分。将图 6中两个部分相加就可以得到步长为2它时所有进行卷积运算的部分了。
![](https://i-blog.csdnimg.cn/blog_migrate/449127c597f04989a0021fb613752f44.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c524b5aa75cdc79449a5e361ff9095fe.png)
获得步长为2的时进行卷积的区域。首先像之前一样对原始图像做以4为步长的卷积,这时进行卷积的部分就是图6中左侧的部分;其次将原始图片沿宽度方向平移2个像素之后,依旧进行步长为4的卷积,这个时候进行卷积的部分为图6中的红色部分和绿色部分;然后沿高度方向平移2个像素之后,按步长为4进行卷积,这个时候进行卷积的部分为图6中的蓝色部分和黄色部分;最后沿高度方向和宽度方向同时移动2个像素,按步长为4进行卷积,这个时候进行卷积的部分为图6中的紫色部分。将这些部分加在一起就是进行卷积运算你得所有区域了。
这个结果明显是无法通过像图5中的那样滑动得到了,这样的方法所需要进行卷积的区域要远远大于以4为步长时所需要就进行卷积运算的区域;后续的卷积都是在这一卷积的结果上进行的,所以后面的都会发生改变。
综上,步长为32的正整数倍只是保证获得结果可以像图5那样滑动的获得的下限值。
3. 卷积神经网络的结构
卷积神经网络通常是由三种层构成:卷积层,汇聚层(除非特别说明,一般就是最大值汇聚)和全连接层(简称FC)。ReLU激活函数也应该算是是一层,它逐元素地进行激活函数操作,常常将它与卷积层看作是同一层。
3.1 层的排列规律
卷积神经网络最常见的形式就是将一些卷积层和ReLU层放在一起,其后紧跟汇聚层,然后重复如此直到图像在空间上被缩小到一个足够小的尺寸,在某个地方过渡成成全连接层也较为常见。最后的全连接层得到输出,比如分类评分等。换句话说,最常见的卷积神经网络结构如下:
![](https://i-blog.csdnimg.cn/blog_migrate/9dbee2e5bb697ea4825924fcba6cbd00.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ba2ce59a0c19b38dadbdf76a889fd6c4.png)
其中*指的是重复次数,POOL?指的是一个可选的汇聚层。其中N >=0,通常N<=3,M>=0,K>=0,通常K<3。例如,下面是一些常见的网络结构规律:
- 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 ,此处每个汇聚层前有两个卷积层,这个思路适用于更大更深的网络(比如说这个思路就和VGG比较像),因为在执行具有破坏性的汇聚操作前,多重的卷积层可以从输入数据中学习到更多的复杂特征。
最新进展:传统的将层按照线性进行排列的方法已经受到了挑战,挑战来自谷歌的Inception结构和微软亚洲研究院的残差网络(Residual Net)结构。这两个网络的特征更加复杂,连接结构也不同。
3.2 卷积层的大小选择
几个小滤波器卷积层的组合比一个大滤波器卷积层好。假设你一层一层地重叠了3个3x3的卷积层(层与层之间有非线性激活函数)。在这个排列下,第一个卷积层中的每个神经元都对输入数据体有一个3x3的视野。第二个卷积层上的神经元对第一个卷积层有一个3x3的视野,也就是对输入数据体有5x5的视野。同样,在第三个卷积层上的神经元对第二个卷积层有3x3的视野,也就是对输入数据体有7x7的视野。假设不采用这3个3x3的卷积层,二是使用一个单独的有7x7的感受野的卷积层,那么所有神经元的感受野也是7x7,但是就有一些缺点。首先,多个卷积层与非线性的激活层交替的结构,比单一卷积层的结构更能提取出深层的更好的特征。其次,假设所有的数据有C个通道,那么单独的7x7卷积层将会包含 个参数,而3个3x3的卷积层的组合仅有
个参数。直观说来,最好选择带有小滤波器的卷积层组合,而不是用一个带有大的滤波器的卷积层。前者可以表达出输入数据中更多个强力特征,使用的参数也更少。唯一的不足是,在进行反向传播时,中间的卷积层可能会导致占用更多的内存。
3.3 层的尺寸设置规律
- 输入层 ,应该能被2整除很多次。常用数字包括32(比如CIFAR-10),64,96(比如STL-10)或224(比如ImageNet卷积神经网络),384和512。
- 卷积层 ,应该使用小尺寸滤波器(比如3x3或最多5x5),使用步长S=1。还有一点非常重要,就是对输入数据进行零填充,这样卷积层就不会改变输入数据在空间维度上的尺寸。比如,当F=3,那就使用P=1来保持输入尺寸。当F=5,P=2,一般对于任意F,当P=(F-1)/2的时候能保持输入尺寸。如果必须使用更大的滤波器尺寸(比如7x7之类),通常只用在第一个面对原始图像的卷积层上。
- 汇聚层 ,负责对输入数据的空间维度进行降采样。最常用的设置是用用2x2感受野(即F=2)的最大值汇聚,步长为2(S=2)。注意这一操作将会把输入数据中75%的激活数据丢弃(因为对宽度和高度都进行了2的降采样)。另一个不那么常用的设置是使用3x3的感受野,步长为2。最大值汇聚的感受野尺寸很少有超过3的,因为汇聚操作过于激烈,易造成数据信息丢失,这通常会导致算法性能变差。
上文中展示的两种设置(卷积层F=3,P=1,汇聚层F=2,P=2)是很好的,因为所有的卷积层都能保持其输入数据的空间尺寸,汇聚层只负责对数据体从空间维度进行降采样。如果使用的步长大于1并且不对卷积层的输入数据使用零填充,那么就必须非常仔细地监督输入数据体通过整个卷积神经网络结构的过程,确认所有的步长和滤波器都尺寸互相吻合,卷积神经网络的结构美妙对称地联系在一起。
为何使用零填充?使用零填充除了前面提到的可以让卷积层的输出数据保持和输入数据在空间维度的不变,还可以提高算法性能。如果卷积层值进行卷积而不进行零填充,那么数据体的尺寸就会略微减小,那么图像边缘的信息就会过快地损失掉。
因为内存限制所做的妥协:在某些案例(尤其是早期的卷积神经网络结构)中,基于前面的各种规则,内存的使用量迅速飙升。例如,使用64个尺寸为3x3的滤波器对224x224x3的图像进行卷积,零填充为1,得到的激活数据体尺寸是[224x224x64]。这个数量就是一千万的激活数据,或者就是72MB的内存(每张图就是这么多,激活函数和梯度都是)。因为GPU通常因为内存导致性能瓶颈,所以做出一些妥协是必须的。在实践中,人们倾向于在网络的第一个卷积层做出妥协。例如,可以妥协可能是在第一个卷积层使用步长为2,尺寸为7x7的滤波器(比如在ZFnet中)。在AlexNet中,滤波器的尺寸的11x11,步长为4。
4. 案例学习
下面是卷积神经网络领域中比较有名的几种结构:
- LeNet ,第一个成功的卷积神经网络应用,是Yann LeCun在上世纪90年代实现的。当然,最著名还是被应用在识别数字和邮政编码等的LeNet结构。
- AlexNet ,AlexNet卷积神经网络在计算机视觉领域中受到欢迎,它由Alex Krizhevsky,Ilya Sutskever和Geoff Hinton实现。AlexNet在2012年的ImageNet ILSVRC 竞赛中夺冠,性能远远超出第二名(16%的top5错误率,第二名是26%的top5错误率)。这个网络的结构和LeNet非常类似,但是更深更大,并且使用了层叠的卷积层来获取特征(之前通常是只用一个卷积层并且在其后马上跟着一个汇聚层)。
- ZF Net ,Matthew Zeiler和Rob Fergus发明的网络在ILSVRC 2013比赛中夺冠,它被称为 ZFNet(Zeiler & Fergus Net的简称)。它通过修改结构中的超参数来实现对AlexNet的改良,具体说来就是增加了中间卷积层的尺寸,让第一层的步长和滤波器尺寸更小。
- GoogLeNet ,ILSVRC 2014的胜利者是谷歌的Szeged等实现的卷积神经网络。它主要的贡献就是实现了一个奠基模块,它能够显著地减少网络中参数的数量(AlexNet中有60M,该网络中只有4M)。还有,这个论文中没有使用卷积神经网络顶部使用全连接层,而是使用了一个平均汇聚,把大量不是很重要的参数都去除掉了。GooLeNet还有几种改进的版本,最新的一个是Inception-v4。
- VGGNet ,ILSVRC 2014的第二名是Karen Simonyan和 Andrew Zisserman实现的卷积神经网络,现在称其为VGGNet。它主要的贡献是展示出网络的深度是算法优良性能的关键部分。他们最好的网络包含了16个卷积/全连接层。网络的结构非常一致,从头到尾全部使用的是3x3的卷积和2x2的汇聚。他们的预训练模型是可以在网络上获得并在Caffe中使用的。VGGNet**不好的一点是它耗费更多计算资源,并且使用了更多的参数,导致更多的内存占用(140M)。其中绝大多数的参数都是来自于第一个全连接层。后来发现这些全连接层即使被去除,对于性能也没有什么影响**,这样就显著降低了参数数量。
- ResNet ,残差网络(Residual Network)是ILSVRC2015的胜利者,由何恺明等实现。它使用了特殊的跳跃链接,大量使用了批量归一化(batch normalization)。这个结构同样在最后没有使用全连接层。读者可以查看何恺明的的演讲(视频,PPT),以及一些使用Torch重现网络的实验。ResNet当前最好的卷积神经网络模型(2016年五月)。何开明等最近的工作是对原始结构做一些优化,可以看论文Identity Mappings in Deep Residual Networks,2016年3月发表。
4.1 VGGNet的细节
我们进一步对VGGNet的细节进行分析学习。整个VGGNet中的卷积层都是以步长为1进行3x3的卷积,使用了1的零填充,汇聚层都是以步长为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
注意,大部分的内存和计算时间都被前面的卷积层占用,大部分的参数都用在后面的全连接层,这在卷积神经网络中是比较常见的。在这个例子中,全部参数有140M,但第一个全连接层就包含了100M的参数。
5. 计算上的考量
在构建卷积神经网络结构时,最大的瓶颈是内存瓶颈,所以如何降低内存消耗量是一个值得思考的问题。三种内存占用来源:
- 1 来自中间数据体尺寸:卷积神经网络中的每一层中都有激活数据体的原始数值,以及损失函数对它们的梯度(和激活数据体尺寸一致)。通常,大部分激活数据都是在网络中靠前的层中(比如第一个卷积层)。在训练时,这些数据需要放在内存中,因为反向传播的时候还会用到。但是在测试时可以聪明点:让网络在测试运行时候每层都只存储当前的激活数据,然后丢弃前面层的激活数据,这样就能减少巨大的激活数据量。这实际上是底层问题,在编写框架的过程中,设计者会进行这方面的考虑。
- 2 来自参数尺寸:即整个网络的参数的数量,在反向传播时它们的梯度值,以及使用momentum、Adagrad或RMSProp等方法进行最优化时的每一步计算缓存。因此,存储参数向量的内存通常需要在参数向量的容量基础上乘以3或者更多。
- 3 卷积神经网络实现还有各种零散的内存占用,比如成批的训练数据,扩充的数据等等。
一旦对于所有这些数值的数量有了一个大略估计(包含激活数据,梯度和各种杂项),数量应该转化为以GB为计量单位。把这个值乘以4,得到原始的字节数(因为每个浮点数占用4个字节,如果是双精度浮点数那就是占用8个字节),然后多次除以1024分别得到占用内存的KB,MB,最后是GB计量。如果你的网络工作得不好,一个常用的方法是降低批尺寸(batch size),因为绝大多数的内存都是被激活数据消耗掉了。