卷积神经网络结构演变(form Hubel and Wiesel to SENet)——学习总结,文末附参考论文

因为打算研究语义SLAM,所以就一直比较关注深度学习这块,这篇文章算是自己的一个总结。结合自己以前了解的,以及上课老师讲的和在深度学习大讲堂的免费课程对网络结构做一个梳理。前面参考刘昕博士所介绍的关于深度学习模型的演化结构,基本上也就是自己当时所记录的笔记,后面有一些参考资料来源于知乎CSDN等平台,文末附所有网络结构论文。

来源:深度学习大讲堂课程之深度学习基础课程

这幅图应该是比较完善的,基本涵盖了绝大部分的卷积神经网络。接下来,我就把自己当时的笔记整理一下,帮助自己更好的理解。我自己现在整理的网络包括:1989年LeCun first baby、LeNet、AlexNet、VGG、NIN 、GoogleNetV1~V4、Xception、, PReLU Net(MSRA-Net)、ResNet,、ResNet的增强版、ResNeXt、DenseNet、MobileNet V1~V2、ShuffleNet、SENet

本文涵盖的内容及网络

一、早期的尝试

1.猫的实验

1962年(神经网络的发源时期),Hubel和Wiesel对猫做了个实验,发现猫的视觉皮层对信息的处理是一种层级结构,也就是说它是一层一层提取信息的,最简单的信息在最前面提取,然后不断地对简单信息提取,逐渐就得到了高层次的抽象信息,他们当时获得的模型如图所示:

Hubel Weisel实验

即然有人提出来了,那么什么都喜欢用代码来实现的计算机科学家们,当然不会放过了,于是在1980年的时候福岛邦彦(Kunihiko Fukushima),把这个模型用代码实现了,但是他虽然用的是卷积的方式,但是他的误差是不是反向传播,用的是自组织的一些方法去实现的。

福岛的实现结构

看了下论文,论文中提到了输出应该和非负模拟值成正比(这玩意是不是和relu激活函数很像啊。。。。。。·),还是老一辈人厉害!!!

沿着神经科学的脚步:Jones and Palmer 在1987年发现猫的V1区域对视觉信号的响应和数学里面的gabor小波基非常相似,gabor小波基也曾经长时间用在图像处理当中,朱松纯当年也是在gabor小波基里面找到了一组根据最小化最大熵原则的小波基,获得了马尔奖(这个是后话,后面我打算也写一下关于我在计算机视觉课程里面学习到的一些有趣的东西,关于底层视觉建模统计学派和稀疏学派)。

2.福岛的尝试

好了,现在回到计算机科学家上面了,后面Maximilian Riesenhuber 和Tomaso Poggio根据hubel和福岛老爷爷的思路提出了基于层级的识别网路HMAX,从认知机制和计算实现方面给出了一些试验结果,这里面其实已经出现了max-pooling的操作了,并且在论文里面作者认为这是关键的一步。(很多思路都是在很早就已经成型了)。

福岛设计的模型

根据这个hubel的研究,其实我们就不难发现,分层去提取特征这个是已经被研究了很久的一个方向,这种思路的其中一个解释就是我们大脑识别其实也就分层的。

大脑视觉处理结构,图片源于深度学习大讲堂课程之深度学习基础课程

(我自己猜测可不可能有这样一种思路:我们分析问题其实就是分层的,一层一层去分析,每一层分析都特别的简单,就是一个化繁为简的过程,把无数简单叠加,就能解决一个复杂问题)。

3.1989年 LeCun First Baby

随着时间的流逝,LeCun大佬出场(祖师爷,天生自带光环)1989年,用了三个隐含层去学习一个邮政数字识别的网络。第一个隐层有12个通道,8*8的特征响应图(feature map),也就是有768个单元(8*8*12=768),每个特征响应图由5*5的卷积获得,这里lecun搞了个池化,但是没有明确提出来,原文中叫做two-to-one undersampled,也没有说到到底是求和还是平均还是取最大值,不过他这里提到了这样做的动机:对于高层感知可能更关注目标是否存在,而位置可能不那么重要(这不就是capsule试图去解决的吗??)。然后这里面他还用到了参数共享,第一层的参数是1068(768+25*12)。第二个隐层也是同样操作,获得的是12个通道4*4的结果,但是每个通道中的数据只来自上一层12个通道中的8个通道的数据(这8个怎么选择的,祖师爷没给出来,老爷子是不是也是实验出来的呢????),权值同样共享,这样第二个隐层的参数为2592(12*8*5*5+12*4*4),第三层就是全连接层是全连接层,一共30个单元,所以参数也好计算5790(12*4*4*30+30)。它池化层,局部感知,权值共享都有了,全连接层也有了,并且在论文中也详细解释了为什么要引入这些东西,不过感觉当时LeCun应该是有保留的,还有东西没说明白,比如这个池化,到底选的是什么?为什么只连接8个?怎么选取这8个?

网络结构

然后刘昕老师还在LeCun的论文里面发现了4个彩蛋,第一个正切函数收敛更快,可以通过对函数求导来对比值域便可以发现,正切函数的值域是(0,1)而sigmoid函数的值域是(0,1/4] ,2.4对应的是根号6,但是分母不对,但是两者出发点都一样,都是keeping the range.

来源于深度学习大讲堂课程之深度学习基础课程

4.LeNet

接下来就是1998年大名鼎鼎的LeNet,在这里LeCun发了一篇46页的论文,第一次喊出了卷积网(Convolutional network)的口号,并且把结果同各种方法做了一个比较,基本唯一能抗衡的就是V-SVM poly9,结果祭出大杀器Boost,Boosted LeNet-4,以0.1%的优势干掉了SVM。

LeCun在这篇论文里面非常详细的介绍了自己的工作,看看网络结构:

LeNet网络结构

C1,C3,C5都是用5*5的卷积核去提取信息,S2,S4采用的是相邻像素之和。这里面要讲一下的就是S2与C3之间的连接,这里有两方面的考虑:1.局部的连接可以限制感知区域2.打断网络中的对称性(老爷子认为这个是更重要的·)。看着这个是不是和ShuffleNet非常相似???想一想1989年那篇文章,12个里面只取8个来连接,两者是不是有点类似。所以个人猜测,这篇文章是在89年那篇文章的基础之上,老爷子通过了9年的沉淀,可能天天挑灯夜战,把89年的理论做了一个完整的梳理,进而提出了卷积网络的概念!(备注:LeCun把这个不叫卷积神经网络,只认为这个是卷积网络)

网络连接

这项工作一出来,的确在当时引起了不小的轰动,LeCun也准备大展拳脚,可惜啊,接下来出现了一些表现更好的手工设计的特征比如:SIFT,HOG,LBP这些算子再加上SVM,原理的清晰性,模型可解释,数据需求小,计算需求小,很快盖过了LeCun的工作,他也为此郁闷了15年。

二、历史转折

5.AlexNet

这一次工作直接导致了神经网络的复兴,在神经网络的历史上,可以说具有跨时代的意义,这里面也采用了许多技巧,比如dropout,relu激活函数,学习率衰减等,这些在优化方法里面会介绍到一些。

这里采用了多GPU分布计算,不过初衷好像是因为:他有两块GPU,没有其他原因!整个网络有5个卷积层和3个全连接层。网上找了一张AlexNet的结构图

来源于博客http://blog.csdn.net/lynnandwei/article/details/44411465

第一层:输入224*224*3,采用11*11的大卷积核,步长设定为4,输出96个通道(提取96个特征),经过激活函数,随后会经过一个LRN层(Local Response Normalization),这个层被称为响应规范化层,目的是为了进行一个横向抑制,使得不同的卷积核所获得的响应产生竞争。具体公式如下:

LRN这个现在很少使用了,因为效果不是很明显,不过作为一个回顾,还是要简单了解一下,说不定,你会根据这个获得一种启发。经过了LRN的输出,经过一个池化就可以进入第二个卷积层了,这里池化的核为3*3,步长为2。

简单介绍一下关于尺寸变化的计算公式:

根据上一式可以计算经过11*11的卷积之后,尺寸变为:55*55(这里采取进一操作),再经过一次pooling,获得第二个卷积层的输入尺寸:27*27

第二个卷积层:输入为27*27*96,用256个卷积核去提取特征,也就是提取了256个特征,卷积核的大小为5*5(这里面因为原文中是两块GPU,所以每块GPU的输入是48个通道,输出是128个通道,从图上看比较清晰),padding为2,随后经过激活函数,经过一个LRN层,然后池化,池化的核同样是3*3,步长为2,获得的输出为13*13*256。

第三第四第五的卷积层都没有接响应规范化层,第三层输入13*13*256,用384个大小为3*3的卷积核去提取特征,尺寸没有发生任何改变,获得13*13*384.第四层也一样,输出为13*13*384。第五层用256个核去卷积,获得13*13*256的特征,随后经过一个池化,池化的核为3*3,步长为2,获得6*6*256的输出,然后接到全连接层。

第一个全连接层4096,个输入,第二个也是4096个输入,第三个全连接层输出为1000,因为分类是要分1000个。

三、网络加深

6.VGG Net

VGGnet是牛津大学计算机视觉组的一个研究工作,这个工作基本流程和AlexNet差不多,卷积-池化-卷积-池化…全连接,但是它的开创性的通过反复堆叠小3*3的卷积层和2*2的池化层,加深了网络,并且获得了一个很好的效果,同时使用两个3*3的卷积核叠加能够获得一个5*5卷积的感知野,减小了参数并且还提升了非线性。

VGG网络参数

VGG通过将模块分组来进行卷积,每一个模块内的参数设置一样,经过了卷积之后再pooling。同时在VGG-C里面还引入了1*1的卷积,网上有的说是引入非线性,其实1*1的卷积相当于是一个将所有层的线性加权,因为卷积的时候,是把对所有层都进行卷积,然后再把对应位置上的值进行加和,得到激活值,所以本质就是一个线性加权的操作,这个在论文中也有提到。(这里采用分为几个模块卷积,我试图在论文中找到这种解释,不过好像没有发现,我个人猜测可能是为了模拟大脑分区的意味,不知道是不是??求大佬告知)

请注意这里Feature Map的数目变化:64-128-256-512-512.,每次池化之后,变为原图大小的1/4,也就是信息衰减为原来的1/4,但是由于增加了卷积通道,所以只相当于衰减了一半的信息,抽取太多信息不利于机器学习,所以这也算是一个小技巧吧!

7.PReLU Net(MSRA-Net)

这个网络其实是VGG的一个扩展,他比VGG的网络更加的深,但是我看了一下文章,感觉这篇文章其实更多的是探讨一个新的激活函数和以及它权重的初始化。改善了relu函数在把在复数设置为了一个小值,然后根据Xavier初始化的动机,尽量是每个网络层的输出方差相同,推导出了PRelu函数的初始化方法,在只考虑输入层为n通道的时候应当满足一个均值为0,方差为 \sqrt{\frac{2}{n}}

这是何凯明大神和孙剑大神共同发的文章。但是根据这篇文章的摘要中提到:这是第一次超越人类的识别率,因为2014年冠军GoogleNet的top-5错误率为6.66%,而人类在5.1%左右,PReLUNet可以达到4.94%.由于这里只看结构,所以从结构来看,它比VGG更加的深,采取的策略也是通过用小卷积核的形式。但是可以注意到,在第一层的时候,是用了一个7*7的卷积核,这一能够减小深度,在论文中是这样说的,他们注意到过深的网络会导致训练精度的下降(这其实就是ResNet提出的初衷),所以这个7*7是权衡出来的结果。观察下图,这里是不是也有同样的规律?通道数增加,特征的图长和宽降低,保证信息一次不会受到太多损失。

PReLU Net(MSRA-Net)网络参数

四、 增加卷积模块

8.network in network

这个工作的出发点是因为传统的卷积网络是在一个固定的patch上面线性加权,所以作者认为它对于特征的提取能力不够强,因此作者在卷积之后接了MLP,然后再输出。

两者的差别

传统的卷积,就是直接加权求和,然后在relu就得到了一个值。传统的CNN求输出只是把卷积核对所有输入通道对应位置上的数字相加了,也就是说它是一个简单的线性求和,这是不是和我们的CNN所想要的不一样呢?于是goodFlow在13年做了一个工作,它引入了一个Maxout层,我个人的理解是类似于用来跨通道进行池化的,就是把几个通道对应位置上最大输出作为这几个通道的一个输出,而不再是简单的线性求和。这项工作的数学依据在于:任意的凸函数都可以由分段线性函数以任意精度拟合,所以最大池化层是一个能够拟合任意凸函数。

NIN的作者就想,你只能拟合任意凸函数,那还是不行,我要搞一个能够拟合任意函数的层出来。径向基神经网络和MLP可以逼近任意函数,但是MLP与BP算法更相容,并且能够加深网络,更加符合设计特征重利用的思路,所以选择MLP,这不就不Maxout更加牛逼了吗?于是NIN的流程就很容易理解了:先卷积,卷积之后进行relu,之后再对这个结果连接一个MLP卷积层,以MLP卷积层的输出经过relu之后作为下一层的输入。这里的MLP卷积层的实现是通过两次1*1的卷积来实现的,224*224的图像经过一个卷积核为11,步长为4的卷积之后大小变为了55*55,一共96个通道。用1*1*96去卷积一下获得了55*55*96,relu激活一下,再用同样大小的去卷积,也是获得55*55*96的特征输出,在经过relu一下便是下一层的输入。1*1的卷积核设计是一个非常巧妙的设计思路,对后来的GoogleNet, MoblieNet这些网络结构都产生了影响。这篇文章里面还有一个创新点就是采用了平均池化作为最后一层的输出,提升了它的泛化性能,这里就不仔细去介绍了。最终NIN在减小了大量的参数前提下,比AlexNet的精度并没有下降太多。

NIN结构

9. GoogleNet (Iception V1)

提升网络性能,一个有效的途径就是增加网络层数,扩展网络宽度,但是沿着这条路去走就会发现,网络越复杂,参数越多,也就越容易出现过拟合,增加计算量。于是Google就提出了GoogleNet,其核心的思想就是:稀疏连接。这里有两个原因是他们认为稀疏性是有效的原因:1.生物神经连接是稀疏的2.稀疏的网络可以通过分析最后一层的激活性的相关统计来逐层构建一个最佳网络,这一点虽然数学上很难证明,但是Hebbian准则(连在一起的神经元一起激活)从侧面有力支持了这一观点。但是计算机对于非均匀稀疏的计算能力很差,想想LeCun最初的两个卷积网络是不是都是稀疏的?但是由于后面数据量太多了,为了更好的优化并行设计,又不得不改回来了。那么有没有什么办法,既可以利用稀疏性,还能提高计算效率呢?根据文献表明:将稀疏矩阵聚类成密集的子矩阵可以达到与稀疏矩阵同样的效果,于是就他们最开始就提出如下模型:

Inception V1初始结构

现在主要目的是:找出如何让已有的稠密组件接近与覆盖卷积视觉网络中的最佳局部稀疏结构。现在需要找出最优的局部构造,并且重复几次。根据相关文献表明层与层连接的结构在最后一层进行相关性统计的时候,将高相关性的聚集到一起。这些聚类构成下一层的单元,且与上一层单元连接。我们假设来自较早层的每个单元对应于输入图像的一些区域,并且这些单元被分组成滤波器组,接近输入层的低层中,相关单元集中在某些局部区域,最终得到在单个区域中的大量聚类,而1*1的卷积可以将这些聚类进行融合。三个不同大小的卷积核是用来去提取不同大小的聚类信息,随着网络的加深,越到后面,抽取的特征更为高级,信息关联的跨度会越大。所以3*3,5*5的在后面会更多一些。这里为了保证卷积后的尺寸一致,所以选了1*1,3*3,5*5的三个patch,同时增加一个pooling层提高效率。

但是这样的计算效率实在太低了,于是为了提高计算效率,他们在3*3和5*5之前都引入了一个1*1的模块,这个模块主要是用来降维的,删减一下通道,同时有不会失去过多信息,因为1*1的卷积核可以融合不同通道的特征。比如原先输入为100*100*64,经过5*5的卷积,输出通道为128,参数的总数就为:5*5*64*128=204800,如果这里先经过一个1*1*32的通道进行降维,那么参数总量为:1*1*32*64+5*5*32*128=104448,这样减小了几乎一半的参数,如果通道数更多,那么减小的参数会更多。所以最终得到了如下模型:

改进后模型结构图
整体网络结构图

上图是goognet论文中给出的图,前面两个softmax是为了避免梯度消失而加上去的,在训练把这两个loos加到一起来进行训练,测试的时候,这两块会被去掉。

随后google的大佬们觉得这个不太好,所以又进行了更为深入的研究,在V2当中提出了著名的BN算法,加速了网络的收敛。这篇在优化方法我再详细介绍吧。

10.Iception V3

在之前的基础上,google又提出了V3,V3是想要寻找一种既可以提高网络深度,又能发挥计算性能的方式,在V3当中,google提出了一些设计技巧,这些技巧没有太强的理论依据,大多来自于实验:

1避免表达的瓶颈,也就是说一开始不要让太多信息流失,逐渐缩小特征图尺寸的同时也要逐渐扩大输出的通道数

2. 高维度能够很更容易在网络的局部进行处理。在卷积网络结构中,增加非线性能够使得更多的特征解耦合。从而使的网络训练速度更快。

3.可以在低维空间进行空间信息汇总,而无需担心信息丢失,推测是因为如果采用空间聚合,则相邻的位置的信息具有强相关性,即使进行了降维,也不会带来太多的损失,并且维数的降低,也能够加速网络学习。

4.网络深度和宽度的平衡。Google认为提升网络深度可以提高网络输出质量,增加宽度(增加滤波器数量)可以在同等质量达到最佳水平。

更加GoogleNet V1的经验在一个网络中,我们可以推测在低层特征被激活之前,他们是高度相关的,所以这时候可以对特征进行删减,而不会对输出造成太多损失。这里他们进一步讨论如何降低参数,因为卷积的本质其实就是对应点和权重相乘,乘法是能够进行因式分解的,因此可以通过因式分解来降低参数量。(感觉有点牵强)根据感知野的变化规律,我们可以知道,用两个3*3的卷积核串联,相当于一个5*5的卷积核,而且参数更少。

来自于深度学习大讲堂课程之深度学习基础

这样就完了?并没有,Google大佬们还在进一步探讨,既然5*5可以用两个3*3来替换,那么3*3能不能再拆分成更小的?当然是可以的,于是用1*3和3*1的卷积核,这就可以获得3*3的感知野了。这里他们也讨论了为什么不拆成2*2的两个串联,因为2个2*2的需要8个参数,而1*3和3*1只需要6个参数,所以更加节省计算。

最终修改后的结构

在V3之后还有V4,不过是结合ResNet网络的,可以先看一下ResNet。

11.Inception V4

V4引入了ResNet,把resnet作为一个子模块嵌入到了原来的模型当中,在论文中他们放出了通过不同基础组件所获得的两个版本。整个方案框架和各个子模块如图所示:

整体结构
stem层
具体描述可见图片下方
同上

这篇文章的一个主要结论:Residual Net可以加速收敛,用Inception结构也可以达到相同的性能。纵观Google Inception系列其实是在做因式分解。

五、集成路线

12.Resnet

随着网络的加深,网络越来越难训练,这里面有两个比较关键的问题:一个是梯度消失和梯度爆炸的问题,另一个就是退化问题(随着网络加深,精度逐渐饱和,然后迅速退化)。梯度消失和梯度爆炸的问题已经可以通过BN这一类方法去优化,但是这个退化问题,它并不是由于过拟合造成的,而是由于难以优化所造成的。假设一个较浅的网络已经学到了一个参数,那么我一个比他更深的网络,就算我什么都不学,只是做一个恒等的映射,精度也应该和它持平才对,这也就说明了传统的网络难以去学习恒等映射这个关系。在训练的过程中,传统的神经网络希望去学习一种理想映射,即x→H(x),但是现在这个映射不好学(至少恒等映射它是很难学的),作者就提出,我现在不学习这个映射,转而学习输入与输出之间的插值这个映射,也就是x→F(x)=H(x)-x,只要我们通过不断学习参数,获得F(x),就能去逼近H(x)(这里其实就是把原来需要学习的东西拆成了两部分,我们只去学习其中的一部分,从理论上来看,应该是会比以前要好学一些)作者通过实验证明了残差函数一般会有较小的响应波动,表明恒等映射是一个合理的预处理。

这样做其实还带来了一个好处就是,梯度便于回传,还记得GoogleNet为了能够使梯度回传,加了三个损失函数吗?也就是要找近路帮助梯度回传,在ResNet之前Schmidhuber大佬,也就是LSTM之父,曾经提出过Higway Network,原理和ResNet比较接近,它是引入了一种类似于LSTM的门机制,保留一定的原始输入,这样前面的一层就能够获得一定比例的不经过非线性变换的信息,直接传输到下一层。ResNet比这个还要干脆一点,直接抄近道。

现在假定需要求的映射输出为 H(x) ,输入与输出之间的残差 F(x)=H(x)-x ,在学的残差 F(x) 之后,便能获得 H(x) ,这也就是残差网络名字的来源。

如果浅层的学习已经和我们期望输出完全一致的时候,可以直接令F为0,也就是恒等变换,如果有差异就通过F去修正它。假如优化目标函数是逼近一个恒等映射, 而不是0映射, 那么学习找到对恒等映射的扰动会比重新学习一个映射函数要容易。往往我们不可能是恒等映射,因为后面的层总会学到一些特征,但是它至少保证了,与浅层网络具有相同的拟合能力,而不会出现网络退化问题。

下图是整个ResNet的网络模型结构:

ResNet网络结构及其对比

如果输入维度和输出维度一致,我们可以直接相加,但是如果不一致怎么办呢?这里作者提供了两个思路:第一个把缺少的维度直接添加0,另一种就是通过1*1的卷积来令其输出维度一致。但是为了减小参数,这里作者提出了这里先通过1*1的卷积模块把输入进行一个线性加权,然后降维成64通道,经过3*3的卷积之后,再通过1*1的卷积模块还原为256通道。

改进和原来的对比

ResNet效果为什么这么好,其实还可以从集成学习的观点来看待。Serge Belongie等人在NIPS上面发了一篇文章讨论过这个问题,他们发现ResNet其实并不深,本质上是一群浅层网络的集合。

一个拥有三个Block的ResNet(左边)可以展开为右边的形式。信息传输的路径多了,所以准确度也就想用的要高一些,就算有些子网络学不好,还有其他网络嘛!

13.Identity Mappings in Deep Residual Networks(ResNet增强版)
这篇文章是ResNet的增强版,也是何凯明大佬的作品,它通过探究恒等映射来使网络变得更加容易训练,其实就是研究如何把梯度更好的回传回去。原来是将两个先相加,再通过激活函数,这样求导的时候,就会先经过一层激活函数,以relu激活函数为例,小于0的部分就没有了梯度,现在我们为了要让这部分更好的回传回去,所以我们可以调整一下,直接把通过短路连接不经过激活函数,与经过卷积的直接相加,这样就能够保证梯度可以全部回传到输入。(参考:blog.csdn.net/shwan_ma/

根据残差模块的设计,我们可以得到下式:

x_{l+1}=y_{l} 即可得到:

把这个递推公式展开即可得到:


从这个公式,至少能够获得两个比较有用的信息:

1) 任意的一层 X_L 都可以用浅层的 x_l 和它到层 X_L 的残差表示

2)最后一层的输出是原始输入与所有层的残差的总和(与boosting比较类似)。

现在为了保证来自原始的输入和经过卷积之后的输入在性质上是一致的,我们就需要对卷积的部分模块进行一下调整。作者通过实验尝试了好几种结构,具体如下,最后选择了一种:

14.ResNeXt

ResNeXt也是何凯明大佬的一篇文章,这篇文章的中心思想就是将卷积通道分组,其实传统的也就是只有一组。神经网络提高精度主要通过两个办法,一个是加深网络,一个就是加宽网络,但是这样带来了更多的超参数,因此作者提出了一种可以提高精度还能降低超参数的网络结构。这个结构其实和Inception有点类似(拆分-变换-合并)。

作者在文中提到了一个新名词——基数(cardinality),用来表示一组变换的大小(the size of the set of transformations),其实就是将通道进行分组,具体操作如下图所示:

上面三种是等价的,从图b来看,和Inception是很像的,不同之处在于每一个block的拓扑结构都一样,这样就省去了很多超参数。这就是整个的核心思想,其实想一想AlexNet也有分组啊,不过当时只是分了两组,现在分了32组。通过在空间维度对网络进行分解,减少了冗余,原来每一层的每个通道与上一层所有通道都发生了关联,但是现在只和组内的通道发生关联,同时,这个也能够集成多个单独训练的网络,提升准确率。

15.DenseNet

这篇文章是CVPR2017的BestPaper,整篇文章的思路就是把每一层都与其他所有层连接起来,思路很简单,效果很棒!这篇文章当时好像投了三次,都没中,结果投CVPR却中了,还拿了一个BestPaper。我想绝大部分人看到这篇文章的时候都是:我擦,这也可以吗?根据作者的解释说他们之前提出过一个随机深度网络来改善ResNet,也就是随机丢掉一些层,发现可以显著提升泛化性能。这个实验启发他们:神经网络其实并不一定要是一个递进层级结构,也就是说网络中的某一层可以不仅仅依赖于紧邻的上一层的特征,而可以依赖于更前面层学习的特征;还有就是ResNet网络存在冗余。于是他们针对这两点设计了DenseNet, 让网络中的每一层都直接与其前面层相连,实现特征的重复利用;同时把网络的每一层设计得特别「窄」,即只学习非常少的特征图(最极端情况就是每一层只学习一个特征图),达到降低冗余性的目的.

DenseNet的结构

这是整个DenseNet的网络结构,可以看到在每一个Block里面,各层之间是相互连接的,每一层的输入都包括了之前所有层的输出,这些输出通过concat连接在一起(ResNet是和),层内结构如下图所示:

在每个Block之间,有一个过渡层,每个过渡层由BN层、1x1卷积层和2x2平均池化层组成,这个1*1的卷积主要是用来降维。这篇文章当中,作者提出了一个叫做增长速率的词,其实也就是输出的通道数。这里把每经过一层的输出通道数都进行固定,举个例子:假定增长率为k,如果某一个block的输入为w,那么在l层的输入就应该为w+k*(l-1)。这里有一个解释:每一层都可以和它所在的block中之前的所有特征图进行连接,使得网络具有了“集体知识”(collective knowledge)。可以将特征图看作是网络的全局状态。每一层相当于是对当前状态增加k(k为增长速率)个特征图。增长速率控制着每一层有多少信息对全局状态有效。全局状态一旦被写定,就可以在网络中的任何地方被调用,而不用像传统的网络结构那样层与层之间的不断重复。

作者在DenseBlock中还引入了一个1*1的卷积,也是用来降维,作者发现这种对densenet极为有效,因此作者将具有BN-ReLU-Conv(1x1)-BN-ReLU-Conv(3x3)的结构称为DenseNet-B。如果同时具有过渡层的1*1卷积降维,在block内部也有1*1的卷积降维,那么这个就称为DenseNet-BC

16.Xception
这是CVPR2017年的一篇论文,它是针对Inception V3的一个改进,提出了深度可分离。但是这个实现过程却和ResNeXt有点类似,其实就是把整个分到了极致,每一个组只有一个通道。根据Inception背后的理念,认为卷积其实是一个既要学习空间相关性又要学习通道相关性的问题,通过将这两个相关性进行分离,能够使学习过程更有效。首先通过1*1的卷积核来提取数据的跨通道相关性,然后再在各个输出上面学习空间相关性。(部分参考:Xception算法详解 - CSDN博客)

我们来看看如何从Inception一步步获得Xception.

去掉平均池化,再把所有的卷积都变成3*3的卷积核:

现在有3个1*1的卷积核,1*1的卷积核能够把通道相关性提取出来,简化一下把这三个卷积核合并为一个卷积核:

由上图作者产生了这样一个想法,上面三组的通道之间是不能相互影响的,也就是上面的三组已经达到了通道分离的要求,那么如果把上面的组分得足够多,我每一个组都只用一个通道,这样所有通道之间都不再相关了。

根据以上思路,作者结合ResNet设计了Xception的网络结构,但是作者这里使用的深度可分离卷积操作,深度可分卷积和Inception的极端版本很像,不过深度可分卷积是先进行单通道卷积操作,再进行1*1的融合:

根据上述思路设计的网络结构如上,不过需要注意一下:

1.操作顺序:深度可分卷积一般(例如在TensorFlow中)先进行通道的空间卷积,然后进行1×1卷积,而Inception首先进行1×1卷积,这一个没有太大的影响。

2.上个操作后是否进行非线性操作:Inception中两个操作后都使用ReLU进行非线性激活,而深度可分卷积不使用。作者通过实验对比发现在depthwise separable convolution前后两部分操作之间不加入激活函数,效果反而比加了激活函数要好,作者推测可能是因为只有单独一个通道,对单通道加激活函数容易丢失特征。

这篇文章思路就在于把原来的空间和通道之间的相关性进行了分离,帮助网络更好的学习。

17.SENet
这是2017年ImageNet挑战赛的冠军,也是最后一届冠军。根据作者的说法,很多工作都是在空间维度进行的操作,如Inception结构中嵌入了多尺度信息,聚合多种不同感受野上的特征来获得性能增益;在Inside-Outside网络中考虑了空间中的上下文信息;还有将Attention机制引入到空间维度上等等。但是对通道的关注还比较少,所以他们从特征通道这个层面去着手来盖上性能。(我觉得ResNeXt、shuffle Net,Xception这些其实都是在从特征的通道层面着手去进行的,这可能会是一个趋势吧)具体怎么去做呢?希望显式地建模特征通道之间的相互依赖关系。其实就是希望让机器自己去学习各个通道的权重,这个权重可以用来表示各个通道之间的重要程度。

既然是权重那么肯定就是一个实数,也就是说作者希望最后输出的是一个n*1的向量,n是通道数,既然要用到权重,自然而然的就会想到LSTM里面的门机制,用一个sigmoid函数就可以获得每个通道的权重。那么在sigmoid函数之前,就必须能够用一个数来表示一个通道,这个数能够表示这个通道也就是具有全局的感受野,所以先对每一个通道进行全局池化,获得一个数,然后通过全连接网络的形式去对数据进行学习,这样也就获得了整个网络的架构,这个网络架构可以作为任何一个大的网络架构的一个子模块嵌入,具体如下:

在这里为什么要用两层全连接神经网络,一个先降到1/16,然后再升回来,作者解释到:两层具有更好的非线性,可以更好的拟合通道之间的复杂相关性,同时也能极大的减少参量和计算量。

六、轻量化网络

18.MoblieNet

MobileNet和Xception其实两者的思想都是一样的,都是用过将通道相关性和空间相关性进行分离,两者的不同在于Xception主要在于提高精度,而MobileNet则主要侧重压缩模型。首先,我们来看一下,为什么采用把通道和空间分离能够减小参数个数:

传统的卷积运算:假定卷积核大小为 D_k*D_k ,输入为M通道,大小为 D_{in}*D_{in},使用N个卷积核(N个输出)的运算量(有padding,stride=0)

卷积是在原图像进行滑动,每个点都会被计算,所以一个卷积核只滑动一个通道是 D_{k}*D_k*D_{in}*D_{in} 有M个通道N个卷积核,所以再相乘。

现在先用 D_{k}*D_k 的卷积核去提取每一个通道的空间相关性,计算量为

再用先用1*1的卷积核去进行通道相关性提取,计算量为

整体的计算量:

两者一除,则:

MobileNet的计算优势相当明显。

网络结构设计

传统的3D卷积常见的使用方式如下图左侧所示,deep-wise卷积的使用方式如下图右边所示(这里有个疑问?Xception作者说单通道卷积最好不引入激活函数,为什么这里还引入了???知乎上网友@xfanplus 给了我解答,我觉得还是有一定的道理:在Xception里面depthwise之后的1*1是用来升维的,低维到高维的变换本来就是一个欠信息映射过程,如果再用relu丢失掉一部分信息,就更难了;而mobilenet的depthwise之后的1*1是用来降维的,高维到低维本来会压缩信息,所以这里用ReLu影响不大)(参考:xfanplus:如何评价mobilenet v2 ?

MobileNet基础结构

作者又提出,我们可以根据需求对Mobile Net进行瘦身,给出了两个超参数:

Width Multiplier:就是按比例减少通道数,Resolution Multiplier:按比例缩小特征图尺寸

19.Mobile Net V2

这是一个针对MobileNet的改进版,其实就是引入了shortcut,但是直接引入残差会有点问题,所以就做了两个改进:1.引入一个逆残差模块(Inverted Residual block),先升维获取更多特征,再降维,这是因为后面提取的其实是空间信息,要是压缩太多了,对空间信息的损失也就比较大,所以这里就先升维。2.去掉了通道数少的层输出后的非线性激活函数,保留特征的多样性,这是因为减小通道数是为了降维使用的,本来降维就会损失一部分信息,再用ReLu函数的话就会损失更大的信息了。同时这里考虑到会降维,所以就用ReLU6作为非线性变换,因为它在与低精度计算一起使用时具有很强的鲁棒性(因为是针对移动端的,所以运算的时候是低精度的,mobileNetV1也是采用这个激活函数)。(参考:如何评价mobilenet v2 ?

对于第二点,为什么能够降维?作者指出,根据流形具有低秩的特性,也就是它存在于一个低维的子空间里面,所以我们可以对它进行降维。降维之后不用非线性激活是因为,如果使用非线性激活,会损失流形的一部分信息。

利用MxN的矩阵B将张量(2D,即N=2)变换到M维的空间中,通过ReLU后concat(y=ReLU(Bx)),再用此矩阵之逆恢复原来的张量。可以看到,当M较小时,恢复后的张量坍缩严重,M较大时则恢复较好。回到作者的思路上面来:根据作者这个思路,其实整个模块就可以出来了:

1*1ConV->ReLu6->DepthWise->ReLu6->1*1ConV->Add

作者这里提到了当stride为2的时候,尺寸不一样,所以没有shortcut,结构如下:

在这其中t是网络扩张的倍数,也就是第一个1*1卷积之后通道数为以前的t倍,c为输出的通道数,n表示该模块重复的次数,s是stride。表格里面有一些错误,第一个在s的第5行,这里如果为2的话,下一行就应该是14.另外,作者在论文中说有共计采用19个bottleneck,但是这里只有17个。不知道是不是笔误。

这里是19

20.ShuffleNet

这篇论文的思想是还是延续稀疏设计的理念,作者通过分析Xception和ResNeXt,发现这两种结构通过卷积核拆分虽然计算复杂度均较原始卷积运算有所下降,但是1*1的卷积却占了不少计算量。于是作者提出了一个通道混联的思路来达到通过1*1获取通道之间信息的效果,减小了计算量。首先作者还是延续了ResNeXt的思路,毕竟是何凯明的工作嘛,孙剑肯定还是比较看重的,采用了分组卷积的思路,同时为了打破组与组之间没有通道信息交互的缺点,在分组卷积经过1*1之后充分融合了组内的通道特性之后,再把通道混联,重新生成通道做3*3的depthwise,然后再把各组经过1*1变换为输出通道。

作者就是通过不断的堆叠Shuffle单元得到ShuffleNet,作者在文中给出了具体的演化过程:

图a是一个带有depthwise的bottleck单元,在图a的基础上,把1*1的卷积换成分组卷积,然后再引入一个shullfe操作,就得到了图b,(注意这里经过depthwise之后也没有激活函数),在shortcut引入一个3*3的平均池化,同时为了获得更多的通道,这里采取了concat的操作。这是按作者论文中来说的,但是我觉得其实直接就可以从ResNeXt来推导可能会更好。当然ResNeXt也是来自于ResNet,所以也没什么问题。

整个网路结构图如下:

我们来看看这个混联操作具体怎么实现(参考:http://blog.csdn.net/u011974639/article/details/79200559)这里假设有g组,每一组n个通道,首先将g*n进行reshape为g*n的矩阵,然后再将这个矩阵展开,每隔n个相连。

附上所有论文

链接:pan.baidu.com/s/1p-CY48 密码:41ij

编辑于 2018-03-16

文章被以下专栏收录