全连接神经网络的局限性
过去的文章中,我们都是对 64×64 的小图片进行操作,实际上,因为每张图片都有 3 个颜色通道,它的数据量是 64×64×3=12288。
现代计算机中,64×64 真的是很小的图片,如果想处理大一点的图片,比如1000×1000,那么最终输入层的数据量是300万,假设我们有一个隐藏层,含有1000个神经元构成的全连接网络,那么数据量将达到 1000*300万,也就是30亿。在这样的情况下很难获得足够的数据防止过拟合,并且需要的内存大小很难得到满足。
本篇讲解的卷积神经网络(也称作 ConvNets 或 CNN)不仅可以达到减少参数的作用,而且在图像分类任务中还有其他优于全连接神经网络的特性。
卷积神经网络概览
一个图像的相互靠近的局部具有联系性,卷积神经网络的思想就是用不同的神经元去学习图片的局部特征,每个神经元用来感受局部的图像区域,如下图不同颜色的圆圈所示:
然后在更高层,将这些感受不同局部的神经元综合起来就可以得到全局的信息了。
卷积神经网络与普通神经网络的区别在于,卷积神经网络包含了由卷积层和子采样层构成的特征抽取器,并且在最后有几个全连接层用于对提取的特征进行分类。一个简单的卷积神经网络模型如下:
接下来我们讲解以下这几个层究竟在做的是什么。
卷积层
卷积层主要是做卷积运算。本文用 “*” 表示卷积操作。
卷积运算
假如我们拥有一个6x6的灰度图,矩阵如下图表示,在矩阵右边还有一个3x3的矩阵。我们称之为过滤器(filter)或核。
卷积的第一步就是将过滤器覆盖在灰度图的左上角的数字就是过滤器对应位置上的数字,如下图蓝色部分:
蓝色矩阵每个小个子的左上角的数字就是过滤器对应位置上的数字,将每个格子的两个数字相乘:
然后将得到的矩阵所有元素相加,即3+1+2+0+0+0+(-1)+(-8)+(-2)= -5,然后将这个值放到新的矩阵的左上角,最后的新的矩阵是一个4*4的矩阵。规律:n*n维的矩阵经过一个f*f过滤器后,得到一个(n-f+1)*(n-f+1)维度的新矩阵。
我们以1为步长(stride)向右移动一格,再进行类似的计算:
0 × 1 + 5 × 1 + 7 × 1 + 1 × 0 + 8 × 0 + 2 × 0 + 2 × (-1) + 9 × (-1) + 5 × (-1) = -4 写在刚刚得到的-5的右边。
重复向右移动,直到第一行都计算完成:
然后将过滤器向下移动一格,从左边开始继续运算,得到-10,写在新的矩阵第二行第一个位置上:
不断的向右、向下移动,直到计算出所有的数据,这样就完成了卷积运算,得出下图:
下面这张图也可以帮助我们理解卷积运算(黄色的矩阵式过滤器):
最终经过卷积得到的图像,我们称之为特征图(Feature Map)
对于上述过程,也许你有很多疑问,比如过滤器是什么,为什么是3×3的,作用是什么,滑动的步长stride为什么是1,为什么得到4×4的矩阵,卷积有什么作用等,我们一一解答。
过滤器(filter)
我们来看看下面的例子,本例子中我们用正数表示白色,0表示灰色,负数表示黑色。
左边 6×6 的矩阵是一个灰度图,左半部分是白色,右半部分是灰色。下面的3×3的过滤器是一种垂直边缘过滤器。
对二者进行卷积得到新的矩阵,这个矩阵的中间部分数值大于0,显示为白色,而两边为 0, 显示为灰色。
上图中三个红色箭头都是我们过滤得到的边缘。在这里,你可能会疑问为什么会有这么大的边缘,那是因为我们的原始图片太小,如果我们把原始灰度图放大一点:
再进行卷积,就可以得到:
这样就能大致看出过滤器确实过滤出了垂直边缘。如果想得到水平的边缘,将过滤器转置一下即可。而如果想得到其他角度的边缘,则需要在过滤器中设置不同的数值。下图是水平边缘过滤器:
当然很难手动设置过滤器的值去过滤某个特殊角度(比如31°)的边缘,我们通常是将过滤器中的9个数字当成9个参数。随机初始化这些参数后,通过神经网络的学习来获得一组值,用于过滤某个角度的边缘。
目前主流网络的过滤器的大小一般是1x1、3x3、5x5等,数值一般不会太大,因为我们希望这些过滤器可以获得一些更细微的边缘特征。另外,一般将长和宽都取奇数,具体原因我们讲到Padding的时候再解释。
Padding
Padding是填充。什么是填充呢?
在前面,我们的例子一个6*6的矩阵经过一个3*3的过滤器后,得到一个4*4的新矩阵。但是这样的话会有两个缺点,第一个缺点就是我们每次做卷积运算的时候,我们的图像都会缩小。可能我们在做完几次卷积之后,我们的图像就会变得很小。第二个缺点是角落边的像素点只会被使用一次,像下图的绿色。但是如果是中间的像素点(类似图中的红色框),就会被使用很多次。
所以,那些边缘或者角落的像素点在输出时使用较少。这意味者,我们将丢掉图片边缘区域。如果这里有重要信息,那我们就得不偿失了。
那我们该怎么解决这个问题呢?这时候就用到Padding了。我们可以在卷积操作之前填充这个图像。我们可以沿着图像的边缘再填充一层像素(通常为0,因为用0填充不影响原来的图片特征)。这样做的话,我们的原始图片像素就有6*6变成了8*8。接着我们对这幅8*8的图像进行卷积,得到的新矩阵就不是4*4的,而是6*6的。规律:n*n维的矩阵填充了P个像素点后再经过一个f*f过滤器后,得到一个(n+2p-f+1)*(n+2p-f+1)维度的新矩阵。
由上面这个规律:我们得出p=(f-1)/2,为了使p是对称填充,所以我们的f通常都是奇数。还有一个原因,是因为当我们有一个奇数的过滤器,我们会有一个中间像素点。这就是为什么前面我们说过滤器通常都是奇数维度。
那我们Padding填充的时候到底选择多少呢?1还是2还是更大的数字?
其实Padding有两个选择:一个Valid,一个是Same。
Valid:意味不填充。也就是之前的6*6矩阵经过一个3*3的矩阵后,得出一个4*4的矩阵。
Same:意味我们的图片输出大小和输入大小是一样的。也就是上面的6*6矩阵在Padding=1之后经过一个3*3的矩阵后,得出一个6*6的矩阵。图片输出大小和输入大小一样。
卷积步长
我们先用 3x3 的过滤器以2为步长卷积 7x7 的图像,看看会得到什么。
第一步,对左上角的区域进行计算:
第二步,向右移动,得:
再继续向右向下移动过滤器,就可以得到下面的结果:
规律:我们用一个f*f的过滤器卷积一个n*n的图像,Padding=p,步长为s,我们得到一个(n+2p-f)/s+1*(n+2p-f)/s+1
最后一个问题:如果我们的商不是整数呢?这时我们采用向下求整的策略。
三维卷积
上面的讲解你已经知道如何对灰度图进行卷积运算了,现在看看如何在有三个通道(RGB)的图像上进行卷积。
假设彩色图像尺寸 6×6×3,这里的 3 指的是三个颜色通道。我们约定图像的通道数必须和过滤器的通道数匹配,所以需要增加过滤器的个数到3个。将他们叠在一起,进行卷积操作。6x6x3的图像经过一个3x3x3过滤器得出一个新的4x4x1图像。
如下图:
具体过程就是,首先,把这个 3×3×3 的过滤器先放到最左上角的位置,依次取原图和过滤器对应位置上的各27个数相乘后求和,得到的数值放到新矩阵的左上角。
注意,由于我们是用三维的过滤器对三维的原图进行卷积操作,所以最终得到的矩阵只有一维。
然后不断向右向下进行类似操作,直到“遍历”完整个原图:
至于效果是怎么样的。结合之前的知识,我们假设了一组垂直边界过滤器:
它可以完成RGB三个通道上垂直边缘的检测工作。这也解释了为什么过滤器使用3个通道:也就是说如果你想获得RGB三个通道上的特征,那么你使用的过滤器也得是3个通道的。
如果你除了垂直边界,还想要检测更多的边界怎么办?可以使用更多 3维 甚至更多的过滤器,如下图框住的部分,我们增加了一组过滤器:
这样,两组过滤器就得到了两组边界,我们一般会将卷积后得到的图叠加在一起,得到上图左边的 4x4x2 的图像。如果你还想得到更多的边界特征,使用更多的3维的过滤器即可。
现在我们对维度进行总结:
首先我们假设我们的没有padding且步长为1,这时候使用 n × n × nc 原图和 nc’ 个 f × f × fc 的过滤器进行卷积,得到 (n-f+1) × (n-f+1) × nc’ 的图像
nc 是 原图的通道数(3),过滤器的通道数和原图的通道数必须一样,都是 nc。
nc’ 是最终得到的图像的通道数(在上面的例子为2),由使用的过滤器个数决定(而 nc’ 又是下一层的输入图像的通道数)
卷积层全貌
对于卷积层,我们同样需要进行以下操作,这也是前向传播的操作:
z^[1] = w^[1] * a^[0]+b^[1]
a^[1]=g(z^[1])
上面所用到的变量解释:
a^[0] 是我们原图的数据 X ,也就是上图 6x6x3 的RGB图。
w^[1] 是这一层的权重矩阵,由 2 个 3x3x3 的过滤器组成。
b^[1] 是第一层的偏置项,不同的过滤器对应不同的偏置值
g() 是激活函数,本例子中使用 ReLu
卷积层的工作说明:
式子中 w^[1] * a^[0] 是代表进行卷积运算得到两个特征图(也就是2个4x4的矩阵),如下图的绿色框所示,接着再对两个特征图加上不同的偏置就得到了z^[1],如下图红色框所示:
进一步应用激活函数就得到了两个处理过的图像,将他们叠加在一起,就得到了这一层的输出 a^[1](上图右下角)。
注意:上图中的加偏置(b1,b2)均使用了python的广播
简单来说,卷积层的工作就是:将输入的数据a^[l - 1]进行卷积操作,加上偏置,再经过激活函数非线性化处理得到a^[l]。到这里卷积层的任务就完成了。
为什么要使用卷积?
卷积层的主要优势在稀疏连接就和权值共享
稀疏连接
假设我们输入的是32×32×3的RGB图像。
如果是全连接网络,对于隐藏层的某个神经元,它不得不和前一层的所有输入(32×32×3)都保持连接。
但是,对卷积神经网络而言,神经元只和输入图像的局部相连接,如下图圆圈所示:
这个局部连接的区域,我们称之为“感受野”,大小等同于过滤器的大小(3*3*3)。
看下面的图:
右边的绿色或者红色的输出单元仅仅与36个输入特征中的九个相连接,其它像素值对输出不会产生任何影响。
相比之下,卷积神经网络的神经元与图像的连接要稀疏的多,我们称之为稀疏连接。
权值共享
权值共享其实就是过滤器共享。特征检测如垂直边缘检测过滤器如果适用于图片的某个区域,那么它也可能适用于图片的其他区域,如果我们用垂直过滤器扫描整张图片,就可以得到整张图片的所有垂直边缘,而换做水平过滤器就可以扫描出图片的水平边缘。
在CNN中,我们用不同的神经元(过滤器)去学习整张图片的不同特征,而不是利用不同的神经元 学习图片不同的局部特征。因为图像的不同部分也可能有相同的特征。每个特征检测过滤器都可以在输入图片的不同区域中使用同样的参数(即方格内的9个数字),以便提取垂直边缘或其它特征。右边两张图每个位置是被同样的过滤器扫描而得的,所以权重是一样的,也就是共享。假设有100个神经元,全连接神经网络就需要32×32×3×100=307200个参数。
因为共享了权值,提取一个特征只需要 f×f 个参数,上图右边两张图的每个像素只与使用 3x3 的过滤器相关,顶多再加上一个偏置项。在本例子中,得到一个feature map 只用到了 3*3+1=10 个参数。如果想得到100个特征,也不过是用去1000个参数。这就解释了为什么利用卷积神经网络可以减少参数个数了。
当你对提取到的 水平垂直或者其他角度 的特征,再进行卷积操作,就可以得到更复杂的特征,比如一个曲线。如果对得到的曲线再进行卷积操作,又能得到更高维度的特征,比如一个车轮,如此往复,最终可以提取到整个图像全局的特征。
到这里卷积层的内容就结束了。
池化层
对于一个32x32像素的图像,假设我们使用400个3x3的过滤器提取特征,每一个过滤器和图像卷积都会得到含有 (32-3 + 1) × (32 - 3 + 1) = 900 个特征的feature map,由于有 400 个过滤器,所以每个样例 (example) 都会得到 900 × 400 = 360000 个特征。而如果是96x96的图像,使用400个8x8的过滤器提取特征,最终得到一个样本的特征个数为 3168400。对于如此大的输入,训练难度大,也容易发生过拟合。池化的主要目的是降维,即在保持原有特征的基础上最大限度地将数组的维数变小。
为了减少下一个卷积层的输入,引入了采样层(也叫池化层),采样操作分为最大值采样(Max pooling),平均值采样(Mean pooling)。
最大值采样
举个例子,我们用3x3的过滤器以1为步长去扫描图像,每次将重叠的部分的最大值提取出来,作为这部分的特征,过程如下:
我们也可以使用2x2的过滤器。以2为步长进行特征值的提取。最终得到的图像长度和宽度都缩小一半:
平均值采样
与最大值采样不同的是 将覆盖部分特征的平均值作为特征,其他过程都一样。经过采样处理,可以提取更为抽象的特征,减少数据处理量的同时保留有效信息。做法是对里面的所有不为0的像素点取均值
下图是过滤器为2x2,步长为2进行特征值的提取:
注意:一定是不为零的像素点,这个很重要。如果把带0的像素点加上,则会增加分母。从而使整体数据变低。
注意:池化层没有需要学习的参数。且padding大多数时候为0
全连接层
在经过几次卷积和池化的操作之后,卷积神经网络的最后一步是全连接层,全连接层的个数可能不止一个:
最后一个采样层到全连接层的过渡是通过卷积来实现的,比如前层输出为 3x3x5 的feature map,那我们就用 3x3x5 的过滤器对前层的这些feature map进行卷积,于是得到了一个神经元的值:
全连接层有1024个神经元,那我们就用1024个3x3x5 的过滤器进行卷积即可。
第一个全连接层到第二个全连接层的过渡也是通过卷积实现的,若前层有1024个神经元,这层有512个,那可以看做前层有1024x1x1个feature map,然后用1x1的卷积核对这些feature map进行卷积,则得到100x1x1个feature map。
卷积神经网络要做的事情,大致分为两步:
- 卷积层、采样层负责提取特征
- 全连接层用于对提取到的特性进行分类
最后一步就和普通的神经网络没有什么区别,充当一个分类器的作用,输入是不同特征的特征值,输出是分类。在全连接层后,就可以根据实际情况用softmax对图片进行分类了。