2.1 为什么要进行实例探究?
过去几年,计算机视觉研究中的大量工作都集中在如何把卷积、池化、全连接这些基本构建组合起来,形成有效的卷积神经网络。在计算及视觉任务中表现良好的神经网路框架,往往也适用于其它任务。这里有些知名的卷积网络结构,如LeNet-5,、AlexNet、VGG、ResNet、Inception。
2.2 经典网络
LeNet-5是1998年提出的一个卷积神经网络,假设输入一张32x32x1的灰度图,LeNet-5可以识别图片中的手写数字。在那个年代,卷积网络还是使用平均池化,而现在使用最大池化更多一些。当时也不用padding,并且使用有效卷积,所以没经过一次卷积,特征图都会减小。当时在最后一层还没有使用softmax,而是使用最近都很少使用的一种分类器。LeNet-5大约只有60k个参数,而现在的网络可能有10M-100M个参数。随着网络的假设,特征图的长和宽都是在减少,而通道数在增加。过去人们设计卷积网络通常使用sigmoid和tanh函数,而不是relu。这里的内容主要是原文的第二段和第三段,后面几段介绍了另一种思路,文中提到的图形变形网络如今并没有得到广泛应用。
AlexNet原文中使用的图像大小是224x224x3,但是如果尝试去推导下,就会发现227x227这个尺寸会更好。AlexNet和LeNet有很多相似之处,不过AlexNet要大的多大约有60M个参数。AlexNet比LeNet出色在于使用了更多的隐藏单元以及relu。在当时GPU的计算速度还比较慢,所以AlexNet采用了非常复杂的方法在两个GPU上进行训练,这些层被分别拆分到两个不同的GPU上,同时还专门有个方法用于两个GPU进行交流。AlexNet还使用了另一种叫作“局部响应归一化层(LRN)”,后来研究者发现LRN起不到太大的作用。
VGG-16没有那么多的超参数,是一种专注于构建卷积层的简单网络。这里的16是指卷积层和全连接层之和,总共包含约138M个参数。VGG-16结构不复杂,并且很规整,都是几个卷积层后面跟着可以压缩图像大小的池化层。卷积层的过滤器数量变化也存在一定的规律,64,128,256,512。也就是图像的缩小和通道增加的比例是有规律的。它主要的缺点是需要训练的特征数量非常大。
2.3 残差网络
非常深的网络很难训练,因为存在梯度消失和梯度爆炸问题。跳跃连接(skip connection)可以从一个网络层获得激活,然后迅速反馈给另外一层,甚至是更深的层,利用跳跃连接构建能够训练非常深网络的ResNet,有时深度能超过100层。残差网络由一个个残差块组成。
如果用一个标准的优化算法训练一个普通网络,凭经验会发现随着网络深度的加深,训练误差会先减少,然后增多。理论上,随着网络深度的加深,应该训练得越来越好才对,但对于一个普通的网络来说,深度越深意味着,用优化算法越难训练。但是残差网络不一样,即使网络加深训练的表现却不错,比如说误差会减少,就算训练深达100层的网络也不例外。对x或中间层的激活能达到网络的更深层,这种方式确实有助于解决梯度消失和梯度爆炸问题,能在训练更深网路的同时,又能保证良好的性能。
2.4 残差网路为什么有用?
一个神经网路在训练集上训练的好,才能在测试集或者验证集上有好的表现,所以至少在训练集上训练好ResNet是第一步。一个网络越深,它在训练集上训练网络的效率会有所减弱,但是在训练ResNet时不会出现。假设有一个大型神经网络,其输入是x,输出是$a^{[l]}$。如果想增加网路的深度,可以在后面再加两层输出$a^{[l+2]}$,可以把这两层看作一个ResNet块。假设在网络中使用relu,所有激活值都大于等于0。如果使用L2正则化或权重衰减,它会压缩$W^{[l+2]}$的值。如果$W^{[l+2]}=0$,为方便起见$b$也等于0,这几项就没有了,因为它们的值为0,最后等于$g(a^{[l]})$,也就是$a^{[l]}=a^{[l]}$,因为假定使用relu激活函数。结果表明残差块学习这个恒等式函数并不难。这意味着即使给神经网络增加了这两层,它的性能不会逊色于更简单的神经网络,因为学习恒等式是简单的,尽管它多了两层,也只是把$a^{[l]}$的值赋给了$a^{[l+2]}$。不论是把残差块添加到神经网路的中间还是末端位置,都不会影响网路的表现。当然我们的目标不仅是保持网路的效率,还要提升它的效率,想象一下,如果这些隐藏单元学到一些有用信息,那么它可能比学习恒等函数表现地更好。而这些不含残差快或跳跃连接的普通深度网络,当网络不断加深时,就算是选择用来学习恒等函数的参数都很困难。所以很多层最后的表现不但没有更好,反而更糟。残差网络起作用的主要原因就是,这些残差块学习恒等函数非常容易,能确定网络性能不会受到影响,甚至会提升效率。残差网络另一个值得讨论的细节是,假设$z^{[l+2]}$与$a^{[l]}$具有相同维度,所以ResNet使用了许多same卷积。如果维度不同,例如,$z^{[l+2]}$维度为256,$a^{[l]}$维度为128,可以用一个256x128的矩阵$W_{s}$左乘$a^{[l]}$,不需要对$W_{s}$做任何操作,它的参数可以通过学习得到。还可以通过padding来填充$a^{[l]}$到与$z^{[l+2]}$相同的维度
2.5 网络中的网路以及1x1卷积
1x1卷积有时也被称为Network in Network。假设一个28x28x192的输入层,可以使用池化层压缩它的高度和宽度。但是如果通道数量很大,该如何把它压缩成28x28x32的层呢?可以使用32个大小为1x1的过滤器,严格来讲,每个过滤器的大小都是1x1x192,因为过滤器中通道的数量必须与输入层中通道的数量保持一致。
2.6 谷歌Inception网络简介
构建卷积网路的时候,要考虑过滤器的大小是3x3还是5x5,或者要不要加池化层,而Inception网络的作用就是代替我们决定,虽然网络结构因此变得更加复杂,但网络表现却很好。基本思想是Inception网络不需要人为决定使用哪个过滤器,或是否需要池化,而是由网络自行确定这些参数。可以给网络添加这些参数的所有可能值,然后把这些输出连接起来,让网络自己学习它需要什么样的参数,采用哪些过滤器组合。但是不难发现,所描述的Inception层有一个问题,就是计算成本。
上面5x5的卷积,即紫色所示,计算这个28x28x32输出的计算成本,为28x28x32 x 5x5x192约等于120M,这个成本是相当高的。
使用1x1卷积可以将上述计算成本减小为原来的十分之一。 使用1x1卷积把输入值从192个通道减少为16个通道,然后运行5x5卷积得到最终输出。整体的输入和输出和上述相同,但我们要做的就是把左边这个大的输入层压缩成压缩成较小的中间层,它只有16个通道而不是192个,有时候这称为瓶颈层(瓶颈通常是某个对象最小的部分),先缩小网络然后再扩大它。下图的计算成本是12.4M,相比上述的120M降到了十分之一。通过使用1x1卷积来构建瓶颈层,从而大大降低计算成本,只要合理构建,也不会降低网络性能。
2.7 Inception网络
本节内容是对上节内容的一个总结,对于一个Inception模块通过使用1x1卷积来减少计算成本,并行构建多种卷积操作但保持特征图大小不变,通道数可以任意变换,最后将所有的特征图的通道拼接起来。如此,又可以通过数据训练自动选择需要的卷积操作,又减少了计算成本。
Inception网络是由多个Inception模块组成,在中间层可以额外添加全连接层用于softmax预测,这么做的目的是即便是隐藏单元和中间层也能参与特征计算,它在Inception网络中能起到一起调整的效果,并能防止过拟合。
2.8 使用开源的实现方案
从github上搜索现有实现,在此基础上进行自己项目的实现。
2.9 迁移学习
在计算机视觉社区有许多开源数据集,并且有许多人在这些数据集上训练自己的算法,有时候这些训练过程需要花费好几周并且需要很多的GPU,其它人经历了非常复杂的择优过程。这就意味着我们可以下载这些开源权重参数,把它当做一个很好的初始化。用迁移学习把公共数据集的知识迁移到自己的问题上。如果自己的数据集是一个非常小的数据集,比较好的做法是,将现有的网络和权重freeze,只训练softmax部分的权重,这相当于把现有的网络当做一个特征提取器,然后将特征送入softmax进行分类。如果自己的数据集变多,可以尝试freeze一部分层,一部分参与训练,或者在freeze的层后重新构造一个网络。当自己的数据非常大时,可以将下载的权重作为自己训练的初始化权重来代替随机初始化权重。
2.10 数据扩充
数据增强有助于神经网络的训练。常用的数据增强方法有垂直镜像、随机裁剪、色彩变换(AlexNet论文中PCA颜色增强)。理论上也可以用Rotation、Shearing、Local warping,但是实践起来有点复杂,所有很少使用。
常用的训练方法是,使用一个线程进行硬盘数据读取和数据增强,然后别的线程进行训练,这样数据读取和训练可以并行实现。一个CPU线程不断地读取数据然后进行变形从而生成一个批量的数据,这些数据持续地传给其它CPU线程或GPU进行训练。
2.11 计算机视觉现状
数据多时我们倾向于使用更简单的算法和更少的手工工程,因此不需要为这个问题精心设计,你可以有一个巨大的神经网络,甚至一个更简单的架构,只有有大量的数据就可以学习它想学的东西。相反,当没有那么多数据时,人们从事更多的是手工工程。学习算法有两种知识来源,一是有标记的数据,应用在监督学习;二是手工工程,可以使精心设计的特性,精心设计的网络体系结构或者是系统的其它组件。所以当没有那么多的标签数据时,只需要更多地考虑手工工程。即使在过去几年数据量的暴增,减少了很多的手工工程量,但是依旧不够,这也是为什么在计算机视觉中看到非常复杂的超参数选择。
在计算机视觉社区有一些benchmarks,很多人在这上面使用自己算法,从而能横向对比。在这上面有很多技巧能提高算法的性能,比如集成学习(设计好几个网络,然后将结果平均,能提高1-2%的性能)、在测试上进行Multi-crop。但是在实际生成系统中不会使用这些技巧,因为会带来很多内存和计算成本的消耗。