Lecture 5 卷积神经网络
上一讲的神经网络是线性分类器的堆叠,只不过在中间加入非线性函数,对中间层产生的模板加权后得到最终的得分。卷积神经网络(Convolutional Neural Networks)与神经网络的构想是一致的,只是需要训练卷积层,卷积层可以保留输入数据的空间结构。
本课重点:
-
卷积神经网络的历史
-
卷积神经网络详述
-
卷积神经网络层的排列与尺寸设置
-
卷积神经网络经典案例
1 卷积神经网络的历史
1957年,Frank Rosenblatt发明了第一代感知器,第一次实现感知器算法。感知器算法和神经网络很相似,都有w、x、b等参数,也有参数更新规则。但是感知器只能输出0/1两个数,参数更新规则也不是反向传播。
更新规则:
1960年,Widrow和Hoff的Adaline/Madaline,首次尝试把线性层叠加,整合成多层感知器网络,与现代神经网络层的结构比较类似,但是仍然没有反向传播或其他训练方法。
1986年Rumelhart才首次提出反向传播算法,然后我们熟悉的链式法则、更新规则等才逐渐出现。至此有了神经网络核心的训练方法,但是仍然无法训练大型的神经网络。
2006年,Hinton和Salakhutdinov的论文表明神经网络不仅可以训练并且可以高效的训练,但是需要谨慎的初始化,才能反向传播。他们先预先训练得到隐层的参数,再训练整个网络。
直到2012年,深度神经网络才得到大规模的应用。首先是Geoffrey Hinton等将CNN用于语音识别,然后其同实验室的 Alex Acero等发表了里程碑式的论文,将CNN用于Image net大赛极大提高识别率,成为图像分类的标杆方法。
卷积网络的产生过程
从1959年开始 ,Hubel & Wiesel做了一些列实验,试图弄明白神经元如何在视觉皮层上工作。他们把电极放进猫的脑袋中,然后给猫不同的视觉刺激,比如不同的边缘方向、不同的形状等,然后测量神经元的应激响应。得出一些重要的结论:一是大脑皮层上的细胞与视觉中的区域相关联,有映射关系。二是神经元间存在分层关系。初级层次的细胞对光的方向产生反应,复杂一点的会对光的移动有反应,超复杂的可以反应端点,识别形状。
1980年,Fukushima 的感知神经器首次将这种简单细胞与复杂细胞的概念形成实例,一种简单细胞与复杂细胞交替层结构。简单细胞会有一些可调参数,复杂细胞对简单细胞执行池化操作。
1998年,LeCun, Bottou, Bengio, Haffner等人首次展示一个实例,应用反向传播和基于梯度的学习方法来训练卷积神经网络,用于邮政编码识别,效果显著。但是有局限性,不能用到更复杂的数据中。 图1 LeNet-52012年,Alex等人提出一种现代化的卷积神经网络,称为AlexNet。与LeCun的很相似,只是更大更深,可以充分利用大量图片数据比如Image net和GPU并行计算能力。 图2 AlexNet
今天,ConvNets已经被广泛应用到图像分类、目标检测、图像分割等。这些技术被广泛用于自动驾驶领域,使用GPU驱动,将高性能的GPU置于嵌入式系统。应用到其他领域,比如人脸识别、视频分类、姿势识别、医学影像分析、星系分类、路标识别,也应用到游戏中,比如AlfaGo。除了分类识别等任务,还可用于图像描述、艺术创作(Deep Dream,将图片生成梵高风格)。
2 卷积神经网络详述
2.1 卷积神经网络和常规神经网络对比
卷积神经网络(CNNs / ConvNets)和常规神经网络非常相似:
- 都是由神经元组成,神经元中有具有学习能力的权重和偏差。每个神经元都得到一些输入数据,进行内积运算后再进行激活函数运算;
- 整个网络依旧是一个可导的评分函数,该函数的输入是原始的图像像素,输出是不同类别的评分;
- 在最后一层(往往是全连接层),网络依旧有一个损失函数(比如SVM或Softmax),并且在神经网络中我们实现的各种技巧和要点依旧适用于卷积神经网络。
卷积神经网络的结构基于输入数据是图像,向结构中添加了一些特有的性质,使得前向传播函数实现起来更高效,并且大幅度降低了网络中参数的数量。
常规神经网络
常规神经网络的输入是一个向量,比如把一张32x32x3的图片延展成3072x1的列向量x,然后在一系列的隐层中对它做变换。每个隐层都是由若干的神经元组成,每个神经元都与前一层中的所有神经元连接(这就是全连接的概念)。但是在一个隐层中,神经元相互独立不进行任何连接。最后的全连接层被称为“输出层”,在分类问题中,它输出的值被看做是不同类别的评分值。比如线性分类Wx,W是10x3072的权重矩阵,即W有10个行向量,最终输出是一个10x1的得分向量,其中的每一个值是W的某一个行向量和x的点积结果,也就是一个神经元的输出。最终会有10个神经元输出10个值(),x和每一个神经元相连,因此是全连接的。
图3 全连接层神经元输出,x要和W的每一行进行点积,对应输出10个神经元的10个值劣势
但是常规神经网络在处理大的图片数据时参数会急速增加。效果也不尽如人意。比如在CIFAR-10中,图像的尺寸是32x32x3,对应网络的第一个隐层中,每一个单独的全连接神经元的参数个数即W的一个行向量就有32x32x3=3072个。若是一个尺寸为200x200x3的图像,会让神经元包含200x200x3=120,000个权重值。而网络中肯定不止一个神经元,那么参数的量就会快速增加!显而易见,这种全连接方式效率低下,大量的参数也很快会导致网络过拟合。
卷积神经网络
与常规神经网络不同,卷积神经网络的各层中的神经元都是3维的:宽度、高度和深度(这里的深度指的是激活数据体的第三个维度,而不是整个网络的深度,整个网络的深度指的是网络的层数)。以第一层卷积层为例,输入数据仍然是32x32x3(宽度x高度x深度)的,并不会将其延展成一个列向量,这样可以保持图像的空间结构(spatial structure)。与之相连的神经元权重不再是W的一个行向量(3072个参数),而是与输入数据有同样深度的滤波器(filter,也称作卷积核),比如是5x5x3的滤波器w。这样神经元不再与输入图像x是全连接的,而是局部连接(local connectivity),只和x中一个5x5x3的小区域进行全连接(常规神经网络中每个神经元都和整个x全连接),滤波器和这个区域计算一个点积wx(计算的时候会展成两个向量进行点积),然后加一个偏差b,就得到一个输出数据(wx+b)。这样的一个神经元共有5x5x3+1=76个参数。这个滤波器会在x上按一定的步长(stride)依次滑动,比如步长为1时,最终会得到一个28x28x1的输出数据,称作激活映射(activation map)或特征映射(feature map),对应28x28个神经元的输出。
图4 第一个卷积层示意,滤波器依次在输入图上滑动计算点积,得到输出激活映射对于用来分类CIFAR-10中图像的卷积网络,其最后的输出层的维度是1x1x10,因为在卷积神经网络结构的最后部分将会把全尺寸的图像压缩为包含分类评分的一个向量,向量是在深度方向排列的。 图5 常规神经网络与卷积神经网络结构对比 上图左边是常规神经网络,每个神经元和上层的神经元都是全连接的;右图是卷积神经网络,每个神经元都有三个维度,网络每一层都将3D的输入数据变化为神经元3D的激活数据并输出。在这个例子中,红色的输入层装的是图像,所以它的宽度和高度就是图像的宽度和高度,它的深度是3(代表了红、绿、蓝3种颜色通道)。蓝色的部分是第一层卷积层的输出,这里的深度显然不为1,表明有多种滤波器。如果我们有6个5x5的滤波器,每个卷积核代表想要的一种模式,它们依次滑过整张图片,那么第一个卷积层的输出结果是28x28x6的。如下图所示: 图6 6个滤波器对应的激活映射
2.2 卷积神经网络的结构
一个简单的卷积神经网络是由各种层按照顺序排列组成,网络中的每个层使用一个可以微分的函数将输入的3D数据变换为3D的输出数据从一个层传递到另一个层。卷积神经网络主要由三种类型的层构成:卷积层,池化(Pooling)层和全连接层(全连接层和常规神经网络中的一样)。通过将这些层叠加起来,就可以构建一个完整的卷积神经网络。
一个用于CIFAR-10图像数据分类的卷积神经网络的结构可以是[输入层-卷积层-ReLU层-池化层-全连接层],这四个层也是目前卷积神经网络比较流行的层。
- 输入层是[32x32x3]存有图像的原始像素,本例中图像宽高均为32,有3个颜色通道。
- 卷积层中,神经元与输入层中的一个局部区域相连,每个神经元都计算输入层上与自己相连的区域与自己权重的内积。卷积层会计算所有神经元的输出(具体有多少神经元下文会介绍)。如果使用12个滤波器(也叫作卷积核),得到的输出数据体的维度就是[32x32x12](之所以是32x32是因为用到零填充,下文会介绍;12是因为每个卷积核都会有一个输出数据图)。
- ReLU层将会逐个元素地进行激活函数操作,比如使用以0为阈值的折叶函数作为激活函数。该层对数据尺寸没有改变,还是[32x32x12]。
- 池化层在空间维度(宽度和高度)上进行降采样(downsampling)操作,假设数据尺寸变为[16x16x12]。
- 全连接层将会计算分类评分,数据尺寸变为[1x1x10],其中10个数字对应的就是CIFAR-10中10个类别的分类评分值。全连接层与常规神经网络一样,其中每个神经元都与前一层中所有神经元相连接。
卷积神经网络一层一层地将图像从原始像素值变换成最终的分类评分值。卷积层和全连接层(CONV/FC)对输入执行变换操作的时候,不仅会用到激活函数,还会用到很多参数(神经元的权值和偏差);而ReLU层和汇聚层则是进行一个固定不变的函数操作。卷积层、全连接层和汇聚层有超参数,ReLU层没有。卷积层和全连接层中的参数利用梯度下降训练。
实际应用的时候,卷积网络是由多个卷积层组成的序列,它们依次堆叠,然后使用激活函数(比如折叶函数)对其进行逐一处理。然后这些卷积层、ReLU层包括池化层会依次堆叠,上一层的输出作为下一层的输入。每一层都会使用多个卷积核,每个卷积核对用一个激活映射。 图7 卷积网络多个卷积层结构。之所以采用逐渐加深的结构,是因为实际中发现这样效果较好。卷积核可视化
卷积网络这些卷积层的所有卷积核完成训练后,会发现前面几个卷积层的卷积核代表的是一些比较简单的特征,比如边缘;中间几层的卷积核代表的特征变得复杂一些,比如一些边角和斑点;最后几层的特征就会变得特别丰富和复杂。所以这些卷积核是从简单到复杂的特征序列。这实际上和Hubel & Wiesel的实验结果比较相似,即使在我们并没有明确的让网络去学习这些从简单到复杂的特征,但是给它这种层次结构并经过反向传播训练后,这些类型的卷积核最终也能学到。 图8 卷积核可视化激活映射与卷积核可视化联系
我们有32个已经在卷积网络中训练好的5x5卷积核,每一个卷积核滑过原始图像得到一张激活映射,将它们可视化,我们可以看出卷积核在原图像寻找什么。比如下图上方红框中的第一个卷积核对应得到红框的激活映射,卷积核看起来像是一个定向边缘的模板,所以当期滑过图像,在那些有定向边缘的地方会得到较高的值。此外,之所以称作卷积,只是计算形式上就是卷积,滤波器和信号(图像)的元素相乘后求和。 图9 卷积核与激活映射间的联系整个卷积网络的可视化
图10 图像输入整个卷积网络输出得分的过程 左边的输入层存有原始图像,右边的输出层存有各类别评分。在处理流程中的每个激活映射铺成一列来展示,因为对3D数据作图比较困难,就把每个数据体深度方向切成层,然后铺成一列显示。图像经过一系列卷积层、RELU层、池化层,最后经过全连接层得到针对不同类别的分类得分,这里只显示了得分最高的5个评分值和对应的类别。整个网络包括输入层、输出层共有17层,架构是[conv-relu-conv-relu-pool] x3-fc-softmax,共有7000个参数,使用3x3卷积和2x2池化区域。(这些数字下面会逐一说明。) 斯坦福大学课程主页上展示的就是这个。提过这么多次的卷积层、池化层等,它们具体是什么以及什么工作原理,下面一一介绍。
2.3 卷积网络各层详细介绍
2.3.1 卷积层(Convolutional Layer,Conv layer)
卷积层是构建卷积神经网络的核心层,网络中大部分的计算量都由它产生。
概述
(下文中所有的空间都是指宽和高,不是3D空间,不包括深度。)
卷积层的参数是由一些可学习的滤波器(filter)集合构成的。每个滤波器在宽度和高度上都比较小,但是深度和输入数据一致。比如卷积神经网络第一层的一个典型的滤波器的尺寸可以是5x5x3(宽高都是5像素,深度是3是因为图像应为颜色通道,所以有3的深度)。在前向传播的时候,让每个滤波器都在输入数据的宽度和高度上滑动(更精确地说是做卷积),然后计算这个滤波器和输入数据对应每一个区域的内积,最终会生成一个2维的激活映射(也叫激活图)(activation map),激活图给出了在每个空间位置处滤波器的反应。直观地来说,网络会让滤波器学习,结果是当它看到某些类型的视觉特征时就激活,具体的视觉特征可能是某些方位上的边界,或者在第一层上某些颜色的斑点,甚至可以是网络更高层上的蜂巢状或者车轮状图案。在每个卷积层上,一般有多个滤波器组成集合(比如12个),每个都会生成一个不同的二维激活映射。将这些激活映射在深度方向上层叠起来就生成了这个卷积层的输出3D数据。
这个3D数据的每一个激活图,都是由一些参数相同的神经元在原图像的不同位置做内积得到的输出数据组成的。每张激活图对应的所有神经元参数都相同(因为实际上就是同一个滤波器在图像上不同位置滑动的结果,每到一个位置就是一个神经元),称为参数共享。
局部连接
卷积层每个神经元和原图像只在一个小区域进行全连接,称为局部连接。因为在处理图像这样的高维度输入时,让每个神经元都与前一层中的所有神经元进行全连接是不现实的。局部连接的空间大小叫做神经元的感受野(receptive field),它的尺寸(其实就是滤波器的空间尺寸)是一个超参数。在深度方向上,这个连接的大小总是和输入量的深度相等。即连接在空间(宽高)上是局部的,但是在深度上总是和输入数据的深度一致。
重复一下图4的例子,一个32x32x3的图片,滤波器可以是5x5x3。此时感受野尺寸是5x5,滤波器的深度需要和原图像深度一致,为3。那么神经元的权重个数为5x5x3=75个,再加一个偏差,共76个。神经元和原图像一个同样大小的区域是全连接的,共有75个连接,但是与整个图像时局部连接的(只在5x5的空间上连接),如果是全连接则需要有3072个连接。
图11 神经元与原图像连接方式
上图左边:红色的是输入数据体(比如CIFAR-10中的图像),蓝色的部分是第一个卷积层中的所有神经元。卷积层中的每个神经元都只是与输入数据体的一个局部空间相连,但是与输入数据体的所有深度维度全部相连(所有颜色通道)。在深度方向上有多个神经元(本例中5个),它们都接受输入数据的同一块区域(感受野相同)。深度方向上的神经元参数是不同的。
右边:神经元内部计算还和以前一样,还是计算权重和输入的内积,然后进行激活函数运算,只是它们的连接被限制在一个局部空间,即输入数据只是原图像的一部分。
神经元排列与输出数据尺寸
卷积层的所有神经元与原始图像卷积后,输出数据体的尺寸由三个超参数控制:深度(depth),步长(stride)和零填充(zero-padding)。
- 深度:卷积层中使用的滤波器往往有多个,深度就是滤波器的数量。每个滤波器在输入数据中寻找一些不同的东西。比如第一个卷积层的输入是原始图像,那么在深度维度上的不同神经元将可能被原图像上不同方向的边界,或者是颜色斑点激活。将这些沿着深度方向排列、感受野相同的神经元集合称为深度列(depth column),或者纤维(fibre)。
- 步长:步长就是滤波器每次移动跨越的像素数量。当步长为1,滤波器每次移动1个像素。当步长为2(实际中很少使用比2大的步长),滤波器滑动时每次移动2个像素。这个操作会让输出数据体在空间上变小。
- 零填充:滑动时会使输出数据体在空间上变小,比如32x32的图像经过一个卷积层输出数据在空间上可能是28x28的,经过多层后会迅速收敛。我们不希望这样,于是引入了零填充,即在图像的边界外填充零像素点。零填充有一个良好性质,可以控制输出数据体的空间尺寸(最常用的是用来保持输入数据体在空间上的尺寸,这样输入和输出的宽高都相等)。下面会有原理介绍。
假设图片的尺寸是NxN,滤波器尺寸是FxF,步长为S,则输出数据的尺寸为:(N-F)/S +1。所以当N=7,F=3:S=1时,输出为5;S=2时,输出为3;S=3时,输出为2.333,显然不合理。所以步长的设置不合理会导致网络的异常,引入零填充可一定程度解决这个问题。
下面考虑加入零填充的情形。在7x7的图像外面加入一圈零像素,滤波器尺寸仍为3x3,步长为1,此时的输出尺寸应该是多少?答案是7x7,因为此时的原图像相当于变成9x9。此时的输出数据空间尺寸和输入的相同。
图13 图像边界外加入零填充综上,可得输出数据尺寸的计算公式:
假如输入数据体尺寸(WxW),卷积层中神经元的感受野尺寸(FxF),步长(S)和零填充的数量(P),则输出数据体的空间尺寸为(W+2P-F)/S+1。比如上面输入是7x7,滤波器是3x3,步长为1,填充为1,那么就能得到一个7x7的输出。一般来说,当步长S=1时,为保证输入尺寸和输出尺寸相同,零填充的数量为:P=(F-1)/2。考虑最初的问题,一张32x32x3的图像,经过有10个5x5x3滤波器的卷积层,步长为1,零填充数量为2,则输出的尺寸为?显然是32x32x10。这是因为滤波器尺寸5步长1填充2可以保持空间尺寸,滤波器的数量又决定了输出的深度。那么这个卷积层有多少个参数呢?10个滤波器每个有5x5x3+1=76个参数,所以共有760个参数。那么这个卷积层一共有多少个神经元呢?答案是32x32x10,因为输出数据的每个数据点,都由一个神经元产生。也就是说输出数据体的尺寸,就代表着神经元的排列方式。但是,既然有32x32x10个神经元,每个神经元的参数为76,那为什么只有760个参数呢?下面参数共享部分可以找到答案。
继续之前,先看一个真实的案例。Alex Krizhevsky等论文中的架构,赢得了2012年的ImageNet挑战,其输入图像的尺寸是[227x227x3]。在第一个卷积层,神经元使用的感受野尺寸F=11,步长S=4,不使用零填充P=0。因为(227-11)/4+1=55,卷积层的深度K=96,则卷积层的输出数据体尺寸为[55x55x96]。55x55x96个神经元中,每个都和输入数据体中一个尺寸为[11x11x3]的区域全连接。在深度列上的96个神经元都是与输入数据体中同一个[11x11x3]区域连接,但是权重不同。在原论文中,作者说的输入图像尺寸是224x224,这是肯定错误的,因为(224-11)/4+1的结果不是整数。这件事在卷积神经网络的历史上让很多人迷惑,而这个错误到底是怎么发生的没人知道。我的猜测是Alex忘记在论文中指出自己使用了尺寸为3的额外的零填充。
参数共享
在卷积层中使用参数共享是用来控制参数的数量。就用上面的真实案例,在第一个卷积层就有55x55x96=290,400个神经元(因为一个滤波器每滑到一个位置,就对应一个神经元,得到一个神经元输出。滑过所有位置后的输出数据空间尺寸为55x55,对应着有55x55个神经元。再加上一共有96个滤波器,所以为55x55x96。),每个有11x11x3+1=364个参数。将这些合起来就是290400x364=105,705,600个参数。单单第一层就有这么多参数,显然这个数目是非常大的。
作一个合理的假设:如果一个特征在计算某个空间位置(x,y)的时候有用,那么它在计算另一个不同位置(x2,y2)的时候也有用。(参数共享的假设是有道理的:如果在图像某些地方探测到一个水平的边界是很重要的,那么在其他一些地方也会同样是有用的,这是因为图像结构具有平移不变性。)基于这个假设,可以显著地减少参数数量。也是基于这个假设,滤波器可以在原图片上滑动。如果我们将深度维度上一个单独的2维切片看做深度切片(depth slice),比如这个尺寸为[55x55x96]的输出数据体就有96个深度切片,每个尺寸为[55x55]。在每个深度切片上的神经元都使用同样的权重和偏差。在这样的参数共享下,例子中的第一个卷积层就只有96个不同的参数集了,一个参数集对应一个深度切片,共有96x(11x11x3+1)=34,944个不同的参数(包括偏差)。在每个深度切片中的55x55个权重使用的都是同样的参数。所以上面的 问题为什么只有760个参数,原因一目了然。在反向传播的时候,需要计算每个神经元对它的权重的梯度,所以需要把同一个深度切片上的所有神经元对权重的梯度进行累加,这样就得到了对这个共享权重的梯度。这样,每个切片只更新一个权重集。
注意:正是因为参数共享,卷积层的前向传播在每个深度切片中可以看做是在计算神经元权重和输入数据体的卷积(这就是“卷积层”名字由来)。这也是为什么总是将这些权重集合称为滤波器(filter)(或卷积核(kernel)),因为它们和输入进行了卷积。
有时候参数共享假设可能没有意义,特别是当卷积神经网络的输入图像是一些明确的中心结构时候。这时候我们就应该期望在图片的不同位置学习到完全不同的特征。一个具体的例子就是输入图像是人脸,人脸一般都处于图片中心。你可能期望不同的特征,比如眼睛特征或者头发特征可能(也应该)会在图片的不同位置被学习。在这个例子中,通常就放松参数共享的限制,将层称为局部连接层(Locally-Connected Layer)。
Numpy例子:
假设输入数据体是numpy数组X
。那么:
- 一个位于空间位置
(x,y)
处的深度列(或纤维)用X[x,y,:]
表示。 - 在深度为
d
处的深度切片,或激活图应该是X[:,:,d]
。
假设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(因为步长为2)。
要构建输出数据体中的第二张激活图,代码应该是:
-
V[0,0,1] = np.sum(X[:5,:5,:] * W1) + b1
(第三个维度是1,表明是第二个激活图) 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
(在两个方向上同时滑动)
这些卷积操作通常后面接的是ReLU层,对激活图中的每个元素做激活函数运算,这里没有显示。(V = np.max(V, 0)
)
卷积层的性质:
- 输入数据体尺寸:
- 需要四个超参数:
滤波器的数量,通常是2的指数,比如32、64、128等;
滤波器的空间尺寸,通常设为3、5;
步长,通常为1、2;
零填充数量,通常为0、1、2。 - 输出数据体的尺寸为:,其中
宽度和高度的计算方式相同,并且使用相同的步长。 - 由于参数共享,每个滤波器包含个权重加一个偏差,整个卷积层有个权重加个偏差;整个卷积层有个神经元,其中每个深度切片上个神经元共享参数。
- 在输出数据体中,第k个深度切片(空间尺寸是)是第k个滤波器和输入数据进行有效卷积运算的结果(使用步长S),最后在加上第k个偏差。
卷积层演示
下面是一个卷积层的运行演示。因为3D数据难以可视化,所以所有的数据(输入数据体是蓝色,权重数据体是红色,输出数据体是绿色)都进行深度切片然后排成一列来展现。输入数据体的尺寸是。卷积层的参数是。也就是说,有2个滤波器,滤波器的尺寸是,步长是2。因此,输出数据体的空间尺寸是(5-3+2)/2+1=3。注意输入数据体使用了零填充P=1,所以输入数据体外边缘一圈都是0。下面的例子在绿色的输出激活图上循环演示,展示了其中每个元素都是蓝色的输入数据和红色的滤波器逐元素相乘,然后求其总和,最后加上偏差得来。高清版展示,建议访问课程官网。
用矩阵乘法实现卷积
卷积运算本质上就是在滤波器和输入数据的局部区域做点积。卷积层的常用实现方式就是利用这一点,将卷积层的前向传播变成一个巨大的矩阵乘法:
- 输入图像的局部区域被
im2col
操作拉伸为列。比如,如果输入是[227x227x3],要与尺寸为11x11x3的滤波器以步长为4进行卷积,就取输入中的[11x11x3]数据块,然后将其拉伸为长度为11x11x3=363的列向量。重复进行这一过程,因为步长为4,所以输出的宽高为(227-11)/4+1=55,即需要55x55=3025个这样的列向量与滤波器做点积。所以输入数据X经过im2col
操作后的输出矩阵X_col
的尺寸是[363x3025],其中每列是X上拉伸的感受野,共有55x55=3,025个。注意因为感受野之间有重叠,所以输入数据体中的数字在不同的列中可能有重复。 - 卷积层的权重也同样被拉伸成行。举例,如果有96个尺寸为[11x11x3]的滤波器,就生成一个矩阵
W_row
,尺寸为[96x363]。 - 现在卷积的结果和进行一个大矩阵乘法
np.dot(W_row, X_col)
是等价的了,能得到每个滤波器和每个感受野间的点积。在这个例子中,这个操作的输出是[96x3025],给出了每个滤波器在每个位置的点积输出。 - 结果最后必须被重新变为合理的输出尺寸[55x55x96]。
这个方法的缺点就是占用内存太多,因为在输入数据体中的某些值在X_col中被复制了多次。但是,其优点是有非常多高效的矩阵乘法实现方式供我们可以使用,比如常用的BLAS API。同样,im2col
思路可以用在汇聚操作中。
反向传播:卷积操作的反向传播(同时对于数据和权重)还是一个卷积(但是是在空间上翻转的滤波器)。使用一个1维的例子比较容易演示(这里不再展开)。
其它卷积方式
1x1卷积:一些论文中使用了1x1的卷积,这个方法最早是在论文Network in Network中出现。人们刚开始看见这个1x1卷积的时候比较困惑,尤其是那些具有信号处理专业背景的人。因为信号是2维的,所以1x1卷积就没有意义。但是,在卷积神经网络中不是这样,因为这里是对3个维度进行操作,滤波器和输入数据体的深度是一样的。比如,如果输入是[32x32x3],那么1x1卷积就是在高效地进行3维点积(因为输入深度是3个通道)。
扩张卷积:最近一个研究(Fisher Yu和Vladlen Koltun的论文)给卷积层引入了一个新的叫扩张(dilation)的超参数。到目前为止,我们只讨论了卷积层滤波器是连续的情况。但是,让滤波器中元素之间有间隙也是可以的,这就叫做扩张。举例,在某个维度上滤波器w的尺寸是3,那么计算输入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的间隙。在某些设置中,扩张卷积与正常卷积结合起来非常有用,因为在很少的层数内更快地汇集输入图片的大尺度特征。比如,如果上下重叠2个3x3的卷积层,那么第二个卷积层的神经元的感受野是输入数据体中5x5的区域(可以说成这些神经元的有效感受野是5x5)。如果我们对卷积进行扩张,那么这个有效感受野就会迅速增长。
2.3.2 池化层(Pooling Layer,POOL Layer)
概述
通常,在连续的卷积层之间会周期性地插入一个池化层。它的作用是逐渐降低数据体的空间(宽、高)尺寸,这样的话就能减少网络中参数的数量,使得计算资源耗费变少,也能有效控制过拟合。池化层最常用的是MAX操作,对输入数据体的每一个深度切片独立进行操作,改变它的空间尺寸。最常见的形式是使用尺寸2x2的滤波器,以步长为2来对每个深度切片进行降采样,将其中75%的激活信息都丢掉。每个MAX操作是从4个数字中取最大值(也就是在深度切片中某个2x2的区域)。深度方向保持不变,不进行降采样。池化层也不用零填充,并且池化滤波器间一般没有重叠,步长等于滤波器尺寸。
池化层的性质
- 输入数据体尺寸:
- 有两个超参数:
池化尺寸,一般为2、3;
步长,一般为2
实际上,max池化层一般只有两种超参数设置方式:,叫做重叠汇聚(overlapping pooling);另一种更常用的是。 - 输出数据体尺寸:,其中
- 因为对输入进行的是固定函数计算,所以没有引入参数。此外,在汇聚层中很少使用零填充。
池化方式
除了最大池化,池化单元还可以使用其他的函数,比如平均池化(average pooling)或L-2范式池化(L2-norm pooling)。平均池化历史上比较常用,但是现在已经很少使用了。因为实践证明,最大池化的效果比平均池化要好。因为深度切片上要池化的这个小区域是由一些权重相同的相邻的卷积层滤波器对X的一个区域进行卷积的结果,最大值代表的是对卷积层滤波器表示的特征更加符合,神经元被激发的程度更大,这对于分类来说比较有利。 图14 池化示意图池化层在输入数据体的每个深度切片上,独立地对其进行空间上(高度、宽度)的降采样。左边:本例中,输入数据体尺寸[224x224x64]被降采样到了[112x112x64],采取的滤波器尺寸是2,步长为2,而深度不变。右边:最常用的降采样操作是取最大值,也就是最大池化,这里步长为2,每个取最大值操作是从4个数字中选取(即2x2的方块区域中)。
反向传播:函数的反向传播可以简单理解为将梯度只沿最大的数回传。因此,在前向传播经过池化层的时候,通常会把池中最大元素的索引记录下来(有时这个也叫作道岔(switches)),这样在反向传播的时候梯度路由就很高效。
不使用池化层:很多人认为可以不使用池化层。比如在Striving for Simplicity: The All Convolutional Net一文中,提出使用一种只有重复的卷积层组成的结构,不再使用池化层,通过在卷积层中使用更大的步长来降低数据体的尺寸。有发现认为,在训练一个良好的生成模型时,弃用池化层也是很重要的。比如变化自编码器(VAEs:variational autoencoders)和生成性对抗网络(GANs:generative adversarial networks)。现在看来,未来的卷积网络结构中,可能会很少使用甚至不使用池化层。
2.3.3 归一化层(Normalization Layer)
在卷积神经网络的结构中,提出了很多不同类型的归一化层,有时候是为了实现在生物大脑中观测到的抑制机制。但是这些层渐渐都不再流行,因为实践证明它们的效果即使存在,也是极其有限的。对于不同类型的归一化层,可以看看Alex Krizhevsky的关于cuda-convnet library API的讨论。
2.3.4 全连接层(Fully-connected Layer,FC Layer)
在全连接层中,神经元对于前一层中的所有激活数据是全连接的,这个和常规神经网络中一样,此时不再需要图像空间结构,把这个汇聚所有结果的3D数组拉成一个向量,与W的每个行向量进行点积,得到每一类的分数。最后一个池化层输出的结果是数据经过整个网络累计得到的,前几个卷积层可能检测一些比较简单的特征比如边缘,得到边缘图后输入到下一个卷积层,然后进行更复杂的检测,这样层层下来,最后一层的结果可以看成是一组符合模板的激活情况,比较大的值表明之前的所有检测结果都比较大,激活程度高,这样就汇聚了大量的信息。虽然输出的数据比较简单,但却是非常复杂的滤波器(或特征)激活后的情况,特征在卷积核中体现。之所以第一层卷积网络输出的结果比较复杂,是因为第一层的卷积核比较简单,很容易就激活了;而最后一层的卷积核非常复杂,所以输出的激活图看起来就会很简单,因为激活比较困难。但是这个激活图却能说明复杂特征的激活程度,用来评分是非常合理的。全连接层转化为卷积层
全连接层和卷积层之间唯一的不同就是卷积层中的神经元只与输入数据中的一个局部区域连接,并且在同一个深度切片上的神经元共享参数。然而在两类层中,神经元都是计算点积,所以它们的函数形式是一样的。因此,将此两者相互转化是可能的:
- 对于任一个卷积层,都存在一个能实现和它一样的前向传播函数的全连接层。权重矩阵是一个巨大的矩阵,除了某些特定块(这是因为有局部连接),其余部分都是零。而在其中大部分块中,元素都是相等的(因为参数共享)。
- 反过来,任何全连接层都可以被转化为卷积层。比如,一个K = 4096(即有4096个类别,W有4096个列向量)的全连接层,输入数据体的尺寸是7x7x512 ,那么W的每个列向量长度为7x7x512,全连接之后的输出为1x4096。这个全连接层可以被等效地看做一个F=7, P=0, S=1, K=4096 的卷积层。换句话说,就是将滤波器的尺寸设置为和输入数据体的尺寸一致也是7x7x512,这样两者卷积的结果就是一个实数。又因为有4096个滤波器,所以输出将变成1x1x4096,这个结果就和使用初始的那个全连接层一样了。
在两种变换中,将全连接层转化为卷积层在实际运用中更加有用。假设一个卷积神经网络的输入是224x224x3的图像,一系列的卷积层和池化层将图像数据变为尺寸为7x7x512的激活数据体(在AlexNet中就是这样,通过使用5个池化层来对输入数据进行空间上的降采样,每次尺寸下降一半,所以最终空间尺寸为224/2/2/2/2/2=7)。全连接层中,AlexNet先使用了两个尺寸为4096的全连接层,然后又使用了一个有1000个神经元的全连接层用于计算分类评分。我们可以将这3个全连接层中的任意一个转化为卷积层:
- 针对第一个连接区域是[7x7x512]的全连接层,令其滤波器尺寸为7x7x512,K=4096,这样输出数据体就为[1x1x4096]了;
- 针对第二个全连接层,令其滤波器尺寸为1x1x4096,K=4096,这样输出数据体仍为[1x1x4096];
- 对最后一个全连接层也做类似的,令其滤波器尺寸为1x1x4096,K=1000,最终输出为[1x1x1000]。
实际操作中,每次这样的变换都需要把全连接层的权重W重塑成卷积层中和输入数据尺寸相同的滤波器。那么这样的转化有什么作用呢?它在下面的情况下可以更高效:让卷积网络在一张更大的输入图片上滑动(即把一张更大的图片的不同区域都分别带入到卷积网络,得到每个区域的得分),得到多个输出,这样的转化可以让我们在单个前向传播的过程中完成上述的操作。
举个例子,我们将224x224x3的图片经过卷积网络(不包括最后三个全连接层)后得到7x7x512的激活数据体(降采样5次,除32)。然后经过第一个全连接层,该全连接层的神经元需要7x7x512个参数。现在一张384x384的大图片经过同样的网络(不包括最后三个全连接层)等效输出尺寸为12x12x512(384/32 = 12),如果直接用来通过全连接层,由于尺寸不同,会无法通过。这时就需要把384x384的图片切成6x6个224x224的小图像依次通过卷积网络,这样全连接层之前的输出为36个7x7x512的激活数据体,远远大于12x12x512,所以由于全连接层的存在,导致大量的重复运算。但是如果将3个全连接层转化来的3个卷积层,就不会存在尺寸的问题,384x384的图片可以直接通过转化后的卷积网络,最终得到6x6x1000的输出(因为(12 - 7)/1 + 1 = 6或 (384-224)/32+1 = 6)。这样我们可以在384x384图像上一次得到6x6个分类得分数组,而不是独立的得到36个大小为[1x1x1000]的得分数组,大大节省计算量。
当有一张384x384的图像,让含有全连接层的原始卷积神经网络以32像素的步长独立对图像中的224x224块进行多次评价,其效果和使用把全连接层变换为卷积层后的卷积神经网络进行一次前向传播是一样的。
自然,相较于使用被转化前的原始卷积神经网络对所有36个位置进行迭代计算,使用转化后的卷积神经网络进行一次前向传播计算要高效得多,因为36次计算都在共享计算资源。这一技巧在实践中经常使用,一次来获得更好的结果。比如,通常将一张图像尺寸变得更大,然后使用变换后的卷积神经网络来对空间上很多不同位置进行评价得到分类评分,然后在求这些分值的平均值。也可以用在目标检测上。
最后,如果我们想在大图像上高效的使用原卷积网络,但是想让步长小于32怎么办?方法是使用多次前向传播。比如还是384x384的图像,但我们想让步长为16(这样需要得到(384-224)/16+1=11,即11x11个得分数组)。那么先使用原图在转化后的卷积网络(步长是32)执行前向传播得到6x6个得分数组(这36个是11x11个中的一部分);然后沿宽度方向把原始图片向左(或向右)平移16个像素,右侧缺少的部分可以补零,高度方向不变,把这个384x384的图像输入到网络中(步长仍然是32),由于右方有16个无效数据0,所以水平方向只有前5次滑动卷积的结果是有效的,高度方向还是6次滑动都有效,所以有效输出为5x6个得分数组;沿高度方向平移同理,也是5x6个;最后高度、深度同时平移16像素,得到有效输出为5x5个得分数组。将这三组数组联合起来,36+30+30+25刚好有11x11个得分数组。
Net Surgery上一个使用Caffe演示如何用代码进行变换的IPython Note教程。
3 卷积神经网络层的排列与尺寸设置
3.1 层的排列规律
卷积神经网络通常是由三种层构成:卷积层,池化层(除非特别说明,一般是最大值池化)和全连接层(简称FC)。ReLU激活函数也应该算是一层,它逐元素地进行激活函数操作。卷积神经网络最常见的形式就是将一些卷积层和ReLU层放在一起,其后紧跟池化层,然后重复如此直到图像在空间上被缩小到一个足够小的尺寸,在某个地方过渡成成全连接层也较为常见。最后的全连接层得到输出,比如分类评分等。换句话说,最常见的卷积神经网络结构如下:
INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC
其中*
指的是重复次数,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
,此时每个池化层前有两个卷积层,这个思路适用于更大更深的网络,因为在执行具有破坏性的池化操作前,多重的卷积层可以从输入数据中学习到更多的复杂特征。
几个小滤波器卷积层的组合比一个大滤波器卷积层好:假设你一层一层地重叠了3个3x3的卷积层(层与层之间有非线性激活函数)。在这个排列下,第一个卷积层中的每个神经元都对输入数据体有一个3x3的视野。第二个卷积层上的神经元对第一个卷积层有一个3x3的视野,也就是对输入数据体有5x5的视野(32-30-28)。同样,在第三个卷积层上的神经元对第二个卷积层有3x3的视野,也就是对输入数据体有7x7的视野。假设不采用这3个3x3的卷积层,而是使用一个单独的有7x7的感受野的卷积层,那么所有神经元的感受野也是7x7,但是就有一些缺点。首先,多个卷积层与非线性的激活层交替的结构,比单一卷积层的结构更能提取出深层的更好的特征。其次,假设所有的数据有C个通道,即输入输出数据深度均为C,那么单独的7x7卷积层将会包含个参数,而3个3x3的卷积层的组合仅有个参数。直观说来,最好选择带有小滤波器的卷积层组合,而不是用一个带有大的滤波器的卷积层。前者可以表达出输入数据中更多个强力特征,使用的参数也更少。唯一的不足是,在进行反向传播时,中间的卷积层可能会导致占用更多的内存。
最新进展:传统的将层按照线性进行排列的方法已经受到了挑战,挑战来自谷歌的Inception结构和微软亚洲研究院的残差网络(Residual Net)结构。这两个网络(下文案例学习小节中有细节)的特征更加复杂,连接结构也不同。
3.2 层的尺寸设置规律
-
输入层(包含图像的)应该能被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的,因为汇聚操作过于激烈,易造成数据信息丢失,这通常会导致算法性能变差。
减少尺寸设置的问题:上文中展示的设置方案是很好的,因为所有的卷积层都能保持其输入数据的空间尺寸,池化层只负责对数据体从空间维度进行降采样。如果使用的步长大于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发明的网络在2013年ILSVRC比赛中夺冠,它被称为 ZFNet(Zeiler & Fergus Net的简称)。它通过修改结构中的超参数来实现对AlexNet的改良,具体说来就是增加了中间卷积层的尺寸,让第一层的步长和滤波器尺寸更小。
- GoogLeNet:2014年ILSVRC的胜利者是谷歌的Szeged等实现的卷积神经网络。它主要的贡献就是实现了一个奠基模块,它能够显著地减少网络中参数的数量(AlexNet中有60M,该网络中只有4M)。还有,这个论文中没有在卷积神经网络的最后使用全连接层,而是使用了一个平均池化,把大量不是很重要的参数都去除掉了。GooLeNet还有几种改进的版本,最新的一个是Inception-v4。
- VGGNet:2014年ILSVRC的第二名是Karen Simonyan和 Andrew Zisserman实现的卷积神经网络,现在称其为VGGNet。它主要的贡献是展示出网络的深度是算法优良性能的关键部分。他们最好的网络包含了16个卷积/全连接层。网络的结构非常一致,从头到尾全部使用的是3x3的卷积和2x2的池化。他们的预训练模型可以在网络上获得并可以在Caffe中使用。VGGNet不好的一点是它耗费更多计算资源,因为使用了更多的参数,导致更多的内存占用(140M)。其中绝大多数的参数都是来自于第一个全连接层。后来发现这些全连接层即使被去除,对于性能也没有什么影响,这样就显著降低了参数数量。
- ResNet:残差网络(Residual Network)是2015年ILSVRC的胜利者,由何恺明等实现。它使用了特殊的跳跃链接,大量使用了批量归一化(batch normalization)。这个结构同样在最后没有使用全连接层。读者可以查看何恺明的的演讲(视频,PPT),以及一些使用Torch重现网络的实验。ResNet是当前最好的卷积神经网络模型(2016年5月)并且是实际应用卷积神经网络默认的选择。何恺明等最近的工作是对原始结构做一些优化,可以看论文Identity Mappings in Deep Residual Networks,2016年3月发表。
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的参数。
计算上的考量
在构建卷积神经网络结构时,最大的瓶颈是内存瓶颈。大部分现代GPU的内存是3/4/6GB,最好的GPU大约有12GB的内存。要注意三种内存占用来源:
- 来自中间数据体尺寸:卷积神经网络中的每一层中都有激活数据体的原始数值,以及损失函数对它们的梯度(和激活数据体尺寸一致)。通常,大部分激活数据都是在网络中靠前的层中(比如第一个卷积层)。在训练时,这些数据需要放在内存中,因为反向传播的时候还会用到。但是在测试时可以聪明点:让网络在测试运行时候每层都只存储当前的激活数据,然后丢弃前面层的激活数据,这样就能减少巨大的激活数据量。
- 来自参数尺寸:即整个网络的参数的数量、反向传播时它们的梯度值,以及使用momentum、Adagrad或RMSProp等方法进行最优化时的每一步计算缓存。因此,存储参数向量的内存通常需要在参数向量的容量基础上乘以3或者更多。
- 卷积神经网络实现还有各种零散的内存占用,比如成批的训练数据,扩充的数据等。
一旦对于所有这些数值的数量有了一个大略估计(包含激活数据,梯度和各种杂项),把这个值乘以4,得到原始的字节数(因为每个浮点数占用4个字节,如果是双精度浮点数那就是占用8个字节),然后多次除以1024分别得到占用内存的KB,MB,最后是GB计量。如果你的网络工作得不好,一个常用的方法是降低批尺寸(batch size),因为绝大多数的内存都是被激活数据消耗掉了。
拓展参考
ConvNetJS CIFAR-10 demo可以在服务器上实时地调试卷积神经网络的结构,观察计算结果;
残差网络在Torch7中的实现。
总结
- 卷积神经网络的历史
- 卷积神经网络与常规神经网络的对比;卷积层、池化层、ReLU层、全连接层;局部连接、参数共享、最大池化、步长、零填充 、数据体尺寸等概念
- 卷积神经网络层的规律与尺寸设置
- 卷积神经网络经典案例