卷积神经网络(Convolutional Neural Network,CNN)是常用的处理图像数据的神经网络。本节首先对卷积神经网络的总体结构进行介绍,包括卷积层、池化层、全连接层、softmax层等。在此基础上,介绍如何利用卷积神经网络做图像分类,例如判断一张图片上的动物是牛、羊、猪,还是狗。图像分类是推动机器学习发展非常重要的基础任务,但实际应用中,一张图片上往往不止一个动物/人/物体。本节随后介绍基于卷积神经网络的图像目标检测算法(包括算法是如何发展起来的),这有助于读者遵循相关脉络设计新的算法。最后,本节将介绍如何利用卷积神经网络自动生成新的图像。
在计算机视觉中,识别一张图片,需要考虑很多输入。比如识别图3.1中的一只狗,一张分辨率为32×32的RGB图像,其输入数据量为32×32×3字节。如果用第2章介绍过的传统的浅层神经网络来做图像识别,这个网络的结构如图3.1所示,需要包括一组输入、一个隐层和一个输出层。如果隐层有100个神经元,则输入和隐层之间的突触的权重有307 200个。
图3.1 利用浅层神经网络进行图像识别
训练有30万个突触权重的神经网络层,很容易出现过拟合。如2.5.1节所介绍的,过拟合会把图像中对于分类不重要的信息当成重要的信息,抓不住问题的本质,导致模型在训练数据上表现好,在更广泛的测试数据上表现差,即模型的泛化能力差。实际应用中神经网络的权重远多于30万个。深度学习的神经网络中,通常有很多个隐层,每一层的神经元的数量可能远不止100个。如果采用简单粗暴的全连接的方式,当输入是224×224大小的RGB图像、第一个隐层有1000个神经元时,仅输入和隐层之间的权重就有1.5亿个。有如此多参数的神经网络是很难训练的,即使训练出来也往往会过拟合。
为解决上述问题,卷积神经网络进行了以下两点设计:
(1)局部连接。视觉理解的关键在于建立联系。相邻的数个像素点很可能属于同一个物体,它们之间具有紧密的联系;距离较远的两个点很可能不属于同一物体,它们的联系也相对松散。因此,视觉联系具有很强的局部性,卷积神经网络也放弃使用全局全连接,而使用更高效的局部稠密连接。
(2)权重共享。卷积神经网络使用卷积核(也称为滤波器或卷积模板)做卷积处理,一张图片中不同的位置可以用同样的卷积系数(即突触权重)。例如一张图片的左上角和右下角,神经网络的突触权重可以是同一组值。其原理是,每一组权重抽取图像的一种特征。例如,抽取形状特征时,在图像的不同位置都可以用同一组权重。
基于局部连接和权重共享两种技术,卷积神经网络可以大幅减少处理图像时所需的权重的数量,从而避免过拟合。
本节以近年来常用的卷积神经网络VGG16 [40] 为例介绍卷积神经网络的总体结构。如图3.2所示,卷积神经网络中最重要的层是卷积层(convolutional layer),每个卷积层有一组卷积核来抽取特定的特征。卷积层的输入、输出的特征图(feature map)尺寸一般变化不大。为了缩小特征图尺寸,一个卷积层之后,一般会有一个池化层(pooling layer)。例如,图3.2中的一张224×224大小的RGB图片,其输入为224×224×3;通过第1组卷积层后变成64个224×224大小的特征图,这64个特征图代表64个不同的卷积核抽取出的图像中的64种不同的特征,同时特征图尺寸保持不变;卷积层后面是池化层,通过池化把特征图的尺寸从224×224变为112×112;然后交替地出现卷积层和池化层,特征图的尺寸随之不断变小,从112×112变为56×56,最后变成7×7。与此同时,抽取出来的特征数量在不断增多,从第1组卷积层抽取出64种特征,从第2组卷积层抽取出128种特征,从最后一组卷积层抽取出512种特征。然后用全连接层,将输入的神经元和输出的神经元全部一一连接起来。当然,在每一个卷积层和每一个全连接层内部,除了向量内积,还需要有激活函数。最后,还会用到softmax函数进行分类概率的凸显、抑制以及归一化。
以下将详细介绍卷积神经网络中的每一层具体是如何工作的。
卷积层是卷积神经网络中的主要组成部分。卷积层通过卷积可以抽取出图像中一些比较复杂的特征。由于卷积层的局部连接和权重共享的特点,对一张图片做卷积时,不相邻的区域不会放在一起计算,如图3.3所示。
浅层神经网络采用全连接方式,计算一个输出需要用到所有输入。而卷积神经网络计算卷积层的一个输出只需要用到 k w × k h 个输入,其中 k w × k h 是卷积核的大小, k w 和 k h 可以是1、3、5、7、9、11等。此外,浅层神经网络中所有神经元之间的连接都采用不同的权重,因此具有 N i 个输入、 N o 个输出的全连接的权重为 N i × N o 个。而卷积神经网络中卷积层的一对输入特征图和输出特征图共用同一组权重,权重仅为 k w × k h 个,大幅减少了权重的数量。
1.卷积运算
卷积神经网络的卷积运算是对输入子矩阵和卷积核做矩阵内积。假设图3.4是一张图片或输入的特征矩阵 X ,矩阵大小为6×6;卷积核 W 是3×3的矩阵;卷积步长为1,*表示卷积计算。为了计算输出矩阵 Y 的第一个值 y 0 , 0 ,将卷积核的中心放在矩阵 X 的(1,1)位置,将对应位置的矩阵 X 的元素 x i,j 和卷积核 W 的元素 w i,j 一一相乘后加和,得到随后,将卷积核的中心在矩阵 X 上右移一格,再将对应位置的矩阵 X 的系数和卷积核的系数一一相乘后加和,得到输出矩阵的第二个值 y 0 , 1 =40;将卷积核在矩阵 X 上每次移动一格,再做乘加计算可以得到输出矩阵 Y 的第一行的所有值。然后,将卷积核的中心移动到矩阵 X 的(2,1)位置,再将对应位置的矩阵 X 的系数和卷积核的系数一一相乘后加和,得到输出矩阵的第二行的第一个值 y 1 , 0 =5。移动卷积核在矩阵 X 上的位置,可以得到所有其他输出值。
图3.2 VGG16卷积神经网络的总体结构
图3.3 全连接与局部连接
图3.4 卷积计算。该示例将输出的位置限制在卷积核全部在输入图片内,也称为有效卷积
以上就是卷积运算的过程。该运算过程中,只用了一个卷积核,因此只能提取出一种特征。
2.多输入/输出特征图的卷积运算
图像中可能有很多种边缘特征,包括对角线特征、三角形特征、圈特征,甚至麻花形特征等。为了提取出图像中不同的特征,神经网络需要有多个不同的卷积核。提取出不同的特征之后,每一个神经网络层会输出多个特征图(或者说有多个输出通道),每一个特征图代表一种特征。如果一个网络层的输入是多个特征图(或者说有多个输入通道),这种情况下怎么做卷积呢?
以图3.5中的卷积为例。该神经网络卷积层的输入是3个6×6大小的特征图(即6×6×3的三维矩阵),分别表示对角线特征、圈特征、三角形特征;卷积核是3个不同的3×3卷积核(即3×3×3的三维矩阵),对应输入的3个不同的特征图;二者卷积输出一个4×4大小的特征图。在卷积运算中,输入特征图的通道数和卷积核的通道数必须一致。如果输入有3个特征图(也称为3个特征通道),就需要有3个卷积核来共同计算出最终的输出特征图。
为了计算输出特征图上(0,0)位置的值,每个输入特征图的左上角的3×3子矩阵和对应卷积核做二维卷积运算得到3个值,再加和。例如,第1个输入特征图中取出的子矩阵为[0,0,0;0,2,2;0,1,2],对应的卷积核为[-1,1,1;-1,1,-1;1,-1,1],二者做二维卷积运算得到1;第2个输入特征图中取出的子矩阵为[0,0,0;0,0,2;0,1,2],对应的卷积核为[1,-1,-1;-1,0,-1;-1,0,1],二者做二维卷积运算得到0;第3个输入特征图中取出的子矩阵为[0,0,0;0,1,1;0,0,2],对应的卷积核为[1,-1,-1;-1,-1,0;-1,1,1],二者做二维卷积运算得到1;3个结果加起来得到2,即输出特征图(0,0)位置的值。与二维卷积运算类似,为了得到其他位置的输出,可以在输入特征图上移动卷积核的位置,例如向右移一格或向下移一格后做卷积运算,可以分别得到输出特征图(0,1)或(1,0)位置的值。
图3.5 3个输入特征图、1个输出特征图的卷积计算示例
上面介绍了由3个输入特征图计算出1个输出特征图的过程。更进一步,如果输入还是3个特征图,而输出是2个特征图,则卷积核的数量要翻倍,如图3.6所示。输入特征图和第1组卷积核做卷积运算得到第1个输出特征图,输入特征图和第2组卷积核做卷积运算得到第2个输出特征图。通过这种方式,可以完成所有的卷积运算。在这个例子中,共有2×3个3×3的卷积核,即54个卷积系数。
图3.6 3个输入特征图、2个输出特征图的卷积计算示例
3.卷积运算可转换为矩阵相乘
原始的卷积运算是将卷积核以滑动窗口的方式在输入特征图上滑动,当前窗口内的元素与卷积核中元素对应相乘,然后求和得到当前位置的结果,因此滑动一个位置获得一个结果。元素对应相乘再求和的计算过程恰好与向量内积的计算过程相同,因此可以将每个窗口滑动时对应的元素拉成行向量,同时将卷积核拉成列向量,通过向量内积计算卷积当前位置的结果。这个将特征图拉成向量的过程称为im2col(image to column)。原始卷积运算过程中,窗口滑动后多个窗口对应的行向量排列在一起,就形成矩阵。每个卷积核分别拉成一个列向量,多个卷积核对应的列向量排列在一起,也形成矩阵。通过这个过程,卷积运算就可以转化成矩阵运算。矩阵运算结束后,输出矩阵的每个列向量对应一个输出特征图的结果,可以再将列向量恢复成特征图,这个过程称为col2im(column to image)。图3.6所示的多输入/输出特征图的卷积运算转换为矩阵运算的过程如图3.7所示。
将卷积运算转换为矩阵相乘,运算过程中的乘法和加法运算次数不变。但转换成矩阵运算形式后,一方面运算时需要的数据将被存放在连续的存储空间上,从而显著提升访存速度。本质上这是一种用空间换时间的方法,因为在im2col的过程中,两个窗口重叠的数据被冗余存储,占用了更多的存储空间。另一方面,大部分编程框架中都可以调用高效的矩阵乘法库,如BLAS、MKL等。将卷积运算转换成矩阵运算形式后,可以通过这些库进行加速。
4.卷积层如何检测特征
下面以图3.8a为例介绍卷积层如何检测垂直边缘特征。输入特征图或图片是6×6大小的矩阵,矩阵中的10代表白色,0代表黑色。该图片中间有一条线,把黑白区域分开,这是其边缘特征。为了抽取该边缘特征,应该设计什么样的卷积核?以3×3大小的卷积核为例,用[1,0,-1;1,0,-1;1,0,-1]做卷积核与输入进行卷积,可以得到图3.8a右侧的输出。在该输出中,0在两侧,30在中间,即输入图片中两侧没有明显变化的区域变成了0,相当于找到了输入图片中最中间的一条竖线把左右两边区分开来,从而把垂直边缘特征准确地提取出来。
图3.7 卷积运算转换为矩阵相乘的计算过程示例
如果要检测图3.8b左侧图片中的对角线边缘特征,可以用图3.8b中的卷积核[1,1,0;1,0,-1;0,-1,-1]。该卷积核对角线上的系数是0,右下的3个系数是-1,左上的3个系数是1。用该卷积核和输入做卷积,可以得到右侧的输出。在该输出中,对角线上的值为30,右下角和左上角的值为0,因为输入图片中左上角和右下角没有变化。
通过上述过程,可以用卷积核把垂直边缘或对角线边缘找出来。
5.边界扩充
做卷积运算时,如果不做边界扩充(padding),卷积之后的输出尺寸会被动地略微变小。假设输入图片或特征图的大小为 W i × H i ,卷积核的大小为 k w × k h ,则卷积输出的特征图的大小为( W i -k w +1)×( H i -k h +1)。这是因为计算每个点的输出时,卷积核需要完全在输入特征图内。例如,输入是32×32大小的图像,用一个4×4大小的卷积核进行卷积,如果不做边界扩充,输出特征图的大小为29×29;如果用同样大小的卷积核再做一层卷积,输出特征图的大小就变成26×26;如果用同样大小的卷积核再做一层卷积,输出特征图的大小就变成23×23;经过几层卷积之后,就没有输出了。对于一个上百层的神经网络,如果不做边界扩充,将计算不出最后的特征图,因此一定要做边界扩充。
边界扩充的主要目的是保证神经网络层的输入特征图和输出特征图的尺寸相同。具体手段是在图像四周补上一圈0。如图3.9b中,神经网络的输入特征图或图片的大小为4×4,卷积核大小为3×3。如果希望卷积输出的特征图的大小还是4×4,就需要在输入图片的四周加一圈0扩充为6×6大小的图片,对扩充后的图片进行卷积运算得到的输出特征图是4×4大小的。通过边界扩充,图片在经过多个卷积层后也不会被动地持续减小。此外,边界扩充可以强化图像的边缘信息,因为扩充的点都是0,在卷积中会发挥出比较强的特征提取的作用,而且图像的边缘通常会有比较重要的特征。
图3.8 特征检测
6.卷积步长
如果希望输出特征图的尺寸有显著变化,可以调整卷积步长(stride)。前面介绍的例子中卷积步长都是1,卷积核在输入特征图内每次向右或向下滑动一步再卷积,结合边界扩充得到的输出特征图的大小和输入特征图的大小是相同的。有些神经网络算法使用了大于1的卷积步长,在输入特征图上滑动卷积核时,可以一次跳2步或格,如图3.9c所示。一次跳2步,会加快每一层的运算速度,同时缩小输出特征图。采用大于1的卷积步长对特征图进行降采样,可以利用局部特征,获得平移不变性等。
选择何种卷积步长,可以根据实际应用需求来调整。如果要保持特征图大小不变,可以做边界扩充。如果要将输出特征图的长宽都减半,可以做边界扩充,同时将卷积步长设为2。
图3.9 原始图像大小为4×4、卷积核大小为3×3的卷积示例
7.小结
总结一下卷积运算过程。在一个卷积层中,有一组输入特征图,共包含 W i × H i × C i 个信息,其中 W i 和 H i 分别是输入特征图的宽度和高度, C i 是输入特征图的个数,也称为输入通道数。
卷积层有 C i × C o 个 k w × k h 大小的卷积核,其中 C o 是输出特征图的个数(也称为输出通道数), k w 和 k h 分别是卷积核的宽度和高度。卷积核一般是正方形的,即 k w = k h ,例如3×3、5×5,也有长方形的卷积核(可以作为神经网络训练时调优的备选手段)。长方形的卷积核可以减少参数的数量,例如将在3.1.2.3节介绍的Inception-v3,用一层1× n 的卷积和一层 n ×1的卷积代替 n × n 的卷积,参数量减少了 n × n -1× n-n ×1。卷积计算之后可能还要加一个偏置来得到输出。
输出特征图的大小是 W o × H o ,其中 W o 和 H o 分别是输出特征图的宽度和高度,其大小与输入特征图的大小、卷积步长 s 和边界扩充相关。如果卷积步长不是1,则输出特征图的大小会变成输入特征图的1/( s w × s h ),其中 s h 和 s w 分别是高度方向和宽度方向的卷积步长。不失一般性,假设边界扩充时在输入特征图的上下以及左右边界分别加 p t 、 p b 行0,以及 p l 、 p r 列0,则输出特征图的宽度和高度分别是
卷积层是面向图像应用的深度神经网络中非常重要的部分,其计算复杂度也相对较高。卷积运算可以转换为矩阵相乘来加速计算,首先根据卷积核尺寸将输入特征图中的对应位置展开成行向量以形成矩阵,其次将每个卷积核展开成列向量以形成矩阵,最后调用矩阵乘法库完成矩阵相乘从而实现加速。
池化层(pooling layer)可以主动减小图片的尺寸,从而减少参数的数量和计算量,抑制过拟合。例如,输入图片或特征图的大小为100×100,经过池化,可能变成50×50。池化层一般没有参数,训练时很简单。池化的方法有很多种,例如最大池化(max pooling,简记为max pool)、平均池化(average pooling,简记为avg pool)、 L 2 池化等。
最大池化是一种常用的池化方法,在池化窗口 k w × k h 内找最大值作为输出。以图3.10为例,假设池化窗口为2×2,步长为2,不做边界扩充,从输入特征图的左上角的2×2子矩阵找到最大值7作为第1个输出,池化窗口在输入特征图上右移2格后找到最大值5作为第2个输出,池化窗口继续右移2格找到最大值3作为第3个输出,继续向下滑动池化窗口可以得到所有的输出值。最大池化仅保留池化窗口内特征的最大值,可以提高特征的鲁棒性。
图3.10 最大池化计算示例
平均池化也是一种常见的池化方法。该方法在池化窗口内对所有的数取平均值,会把图像的一些特征平均化,也就是模糊化。
L 2 池化是在池化窗口内对所有的数计算平方并累加和后再开平方。
对于硬件设计而言,最大池化只需要找几个数中的最大值,很容易实现。而 L 2 池化需要计算开平方,硬件实现复杂度高。以前还有用几何平均做池化的,复杂度更高。如果几何池化窗口为2×2,则需要开4次方;如果几何池化窗口为3×3,则需要开9次方。几何池化计算时间很长,可能会带来一点准确率提升,但实际使用时会有很多麻烦。因此最大池化是最常用的。
卷积层和池化层构成特征提取器,而全连接层(fully-connected layer,简记为FC)是分类器。全连接层将特征提取得到的高维特征图映射成一维特征向量,该特征向量包含所有特征信息,可以转化为最终分类为各个类别的概率。例如,一个224×224大小的输入图片经过多层卷积和池化,可能变成4096个1×1大小的特征图,根据这4096个特征可以做一个全连接层,来判定最后是猪、狗、猫、牛、羊中的哪一个。在具体计算时,全连接层等价于先将输入的高维特征图展平成一个向量,然后通过矩阵相乘对向量做线性变换,映射成另一个向量。
有的卷积神经网络的最终输出是由全连接层决定的,但也有的卷积神经网络是用soft-max层做最终输出。
softmax对输出进行归一化,输出分类概率。其计算过程为:
其中, z j 是softmax层的第 j 个输入。从softmax的计算过程可以看出,输入和输出的数据规模是相同的;通过归一化计算,可以凸显较大的值并抑制较小的值,从而显著地抑制次要特征,决定分类概率。
不同数量和大小的卷积层、全连接层和池化层组合就形成了不同的卷积神经网络。图3.2中的VGG16网络,首先用卷积层和池化层做特征提取,特征图的大小从输入的224×224变成112×112,再变成56×56,每次宽高折半,最后变成7×7;然后做分类,512个7×7大小的特征图经过一个全连接层变成4096个特征,再经过一个全连接层输出4096个特征,然后经过一个全连接层变成1000个特征,最后这1000个特征做softmax得到神经网络的输出,例如属于1000种物体中的哪一种。
卷积神经网络中,常见的层组合方式如图3.11所示。卷积层和池化层通常是交替出现的,一个卷积层后面通常跟着一个池化层,也可能一个卷积层后面紧跟着两三个卷积层,再来一个池化层,也就是连续 N 个卷积层之后加一个池化层。这种卷积和池化组合重复出现 M 次之后,基本上能提取出所有特征,再用 K 个全连接层把这些特征映射到 O 个输出特征上,最后再经过一个全连接层或者softmax(有时候这两种都用)来决定输出是什么。这个例子中,最后识别出来是一只狗。
当图3.11中 N =3, M =1, K =2时,其网络结构为:输入→卷积层(ReLU)→卷积层(ReLU)→卷积层(ReLU)→池化层→全连接层(ReLU)→全连接层(ReLU)→全连接层→输出。
在GoogLeNet等网络中,还有更多种组合方式,例如池化层和卷积层可以包含分支。譬如一支卷积核输出一组76×76×37的特征,另一支卷积核输出一组38×38×99的特征,做完分支之后,最后将分支合起来做分类。关于分支将在3.1.2.3节详细介绍。
卷积神经网络为何选择深而不广的神经网络结构?2.2.2节介绍过,两层的神经网络中只有一个隐层,理论上只要有足够多的神经元,两层的神经网络足以拟合出任意的函数 [33] 。但在实际中,一个复杂特征往往是由多个简单特征组成的,采用深层的网络结构,可以很好地完成对图像从局部到整体的理解。例如人脸识别时,可能先看到一个局部的简单特征,可能是一团黑色的圆圈;再到更大的范围看,这个圆圈可能是眼睛;再到更大的范围看,眼睛上面还有眉毛;再到更大的范围看,可能左边有一个眼睛和眉毛,右边有一个眼睛和眉毛;再到更大的范围看,可能是一张脸,从而识别出一个人。这种层次化的结构非常适合从局部到整体地理解图像。
图3.11 常见的卷积神经网络结构
此外,深度神经网络可以减少权重数量。如果只用一个隐层,层数很少,但如果采用全连接,一个隐层里的权重数量会非常多。例如输入是1000个,如果隐层有1万个节点,则有1000万个参数。而在深度神经网络中,参数是相对较少的,而且参数的数量与图像的规模没有直接关系,因为在图像的任何一个位置使用的卷积核都是一样的。如果卷积核的大小是3×3,输入是3个特征图,输出是2个特征图,最终的所有参数个数仅为3×3×3×2=54。由于深度神经网络权重的数量较少,它过拟合的风险也会变小,在面对海量训练数据时,它的训练速度也能被业界所接受。
为了进一步直观地认识神经网络,图3.12展示了一个卷积神经网络可视化的效果 [54] 。该神经网络结构如表3.1所示,在ILSVRC-2012 ImageNet数据集上进行训练得到网络参数。经过几层卷积之后,可以把第一行中一个白圈里带着黑色的特征抽取出来,下面一些更复杂的特征,通过逐层的抽取都可以很好地提取出来。