自2012年AlexNet以来,卷积神经网络(简称CNN)在图像分类、图像分割、目标检测等领域获得广泛应用。随着性能的要求越来越高,AlexNet 已经无法满足大家的需求,于是乎各路大牛纷纷提出性能更优越的 CNN 网络,如VGG、GoogLeNet、ResNet、DenseNet等。由于神经网络的性质,为了获取更好的性能,网络的层数不断增加,从7层 AlexNet 到16层 VGG,再从16层 VGG 到 GoogLeNet 的22层,再从22层 GoogLeNet到152层 ResNet 更有上千层的 ResNet 和 DenseNet。虽然网络的性能得到了提高,但随之而来的就是效率问题。
效率问题主要是 模型的存储问题 和 模型进行预测的速度问题(以下简称为速度问题)。第一,存储问题。数百层网络意味着有着大量的权值参数,保存大量权值参数对设备的内存要求很高;第二,速度问题。在实际应用中,往往是毫秒级别,为了达到实际应用标准,要么提高处理器性能(如优化英特尔处理器或其他处理器),要么就是减少计算量。
只有解决CNN的效率问题,才能使CNN走出实验室,更广泛的应用于移动端。对于效率问题,通常的方法是进行模型压缩(Model Compression),即在已经训练好的模型上进行压缩,使得网络携带更少的网络参数,从而解决内存问题,同时可以解决速度问题。
相比于在已经训练好的模型上进行处理,轻量化模型设计则是另辟蹊径。轻量化模型模型设计,主要思想在于设计更高效的“网络计算方式”(主要针对卷积方式),从而使网络参数减少的同时,不损失网络性能。
本文就近年提出的四个轻量化模型进行学习和对比,四个模型分别是:SqueezeNet、MobileNet、ShuffleNet、Xception。这四种网络模型都不是在训练好的模型上进行压缩,而是在网络结构中的卷积计算上进行优化和改造。
以下是四个模型的作者及发表时间:
网络 | 最早公开日期 |
发表情况 | 作者团队 | arXiv链接 |
---|---|---|---|---|
SqueezeNet | 2016.02 | ICLR-2017 | 伯克利&斯坦福 | https://arxiv.org/abs/1602.07360 |
MobileNet | 2016.04 | CVPR-2017 | https://arxiv.org/abs/1704.04861 | |
ShuffleNet | 2016.06 | N/A | Face++ | https://arxiv.org/abs/1707.01083 |
Xception | 2016.10 | CVPR-2017 | https://arxiv.org/abs/1610.02357 |
从命名 SqueezeNet 就知道本文的新意是 Squeeze,squeeze 在 SqueezeNet 中表示一个 squeeze 层,该层采用 1*1 卷积核对上一层 feature map 进行卷积,主要目的是减少 feature map 的维数(维数即通道数,就是将一个立方体的 feature map 切成一片一片的)。
创新点:采用不同于传统的卷积方式,提出 fire module。 fire module 包含两个部分: squeeze层 和 expand 层。首先是 squeeze 层,就是 1*1 卷积,其卷积核数要少于上一层的 feature map 数; expand 层分别用 1*1 和 3*3 卷积,然后concat(通道数相加)。SqueezeNet 的创新点在 inception 中都有,进行了一些列的改变。有需要的话可以参考 inception 的论文。
SqueezeNet 的核心在于 fire module,fire module 由两层构成,分别是 squeeze层 + expand层。如图示:squeeze层是一个 1*1 卷积核的卷积层,expand 层是 1*1 和 3*3 卷积核的卷积层,expand层中把1*1 和 3*3 得到的 feature map 进行 concat。
具体的操作示意图:
Fire module输入的feature map为H*W*M的,输出的feature map为H*M*(e1+e3),可以看到feature map的分辨率是不变的,变的仅是维数,也就是通道数,这一点和VGG的思想一致。
首先,H*W*M的feature map经过Squeeze层,得到S1个feature map,这里的S1均是小于M的,以达到“压缩”的目的,详细思想可参考Google的Inception系列。
其次,H*W*S1的特征图输入到Expand层,分别经过1*1卷积层和3*3卷积层进行卷积,再将结果进行concat,得到Fire module的输出,为 H*M*(e1+e3)的feature map。
fire模块有三个可调参数:S1,e1,e3,分别代表卷积核的个数,同时也表示对应输出feature map的维数,在本文提出的SqueezeNet结构中,e1=e3=4s1 。
讲完SqueezeNet的核心——Fire module,看看SqueezeNet的网络结构,如下图所示:
网络结构设计思想,同样与VGG的类似,堆叠的使用卷积操作,只不过这里堆叠的使用本文提出的Fire module(图中用红框部分)。
看看Squezeenet的参数数量以及性能:
在这里可以看到,论文题目中提到的小于 0.5M,是采用了 Deep Compression 进行模型压缩之后的结果。标题党,SqueezeNet < 0.5 MB,这个是用了模型压缩技术获得的,很容易让人误以为 SqueezeNet可以压缩模型。
SqueezeNet 小结:
1 Fire module 与GoogLeNet思想类似,采用1*1卷积对feature map的维数进行“压缩”,从而达到减少权值参数的目的;
2 采用与VGG类似的思想——堆叠的使用卷积,这里堆叠的使用Fire module 。
SqueezeNet与GoogLeNet和VGG的关系很大!
Standard convolution、depthwise convolution 和 pointwise convolution示意图如下:
Standard convolution是采用N个大小为DK*DK的卷积核进行操作(注意卷积核大小是DK*DK, DK*DK*M是具体运算时候的大小!)
而depthwise convolution + pointwise convolution需要的卷积核:
Depthwise convolution :一个卷积核负责一个通道,一个通道只被一个卷积核卷积;则这里有M个DK*DK的卷积核;
Pointwise convolution:为了达到输出N个feature map的操作,所以采用N个1*1的卷积核进行卷积,这里的卷积方式和传统的卷积方式是一样的,只不过采用了1*1的卷积核;其目的就是让新的每一个feature map包含有上一层各个feature map的信息!在此理解为将depthwise convolution的输出进行“串”起来。
举例讲解 Standard convolution、Depthwise convolution和Pointwise convolution:
假设输入的feature map 是两个5*5的,即5*5*2;输出feature map数量为3,大小是3*3(因为这里采用3*3卷积核)即3*3*3。
标准卷积,是将一个卷积核(3*3)扩展成M份(M=2), 是让二维的卷积核(面包片)拓展到与输入feature map一样的面包块形状。例如,我们设置3个 3*3的卷积核,如下图Kernel所示,但是在实际计算当中,卷积核并不是3*3*3这么多,而是3*3*2*3 ( w*h*c_in*c_out) 。也就是上面所说的把二维的卷积核拓展到与feature map一样的面包块形状,如下图的K1 扩展成 K11,K12 。
(注:不是复制M份,因为每个二维卷积核的参数是不一样的,因此不是复制!)
(实际上,卷积核实际的尺寸应该是 w*h*c_in*c_out。往往,我们忽略掉c_in这个数,在设置卷积核数量时,也不会涉及到这个参数,但是在计算过程中是不能忽略的。 其中,w*h就是通常我们所说的卷积核大小,例如3*3,5*5,7*7等;c_out是平时我们讲的卷积核个数,例如该卷积层设置了64个卷积核,则c_out = 64;而 c_in 则是等于上一层的feature map的数量。)
Standard过程如下图,X表示卷积,+表示对应像素点相加,可以看到对于O1来说,其与输入的每一个feature map都“发生关系”,包含输入的各个feature map的信息。
Depthwise 过程如下图,可以看到depthwise convolution 得出的两个feature map——fd1 和 fd2 分别只与 i1 和 i2 “发生关系” ,这就导致违背了观点 “输出的每一个feature map要包含输入层所有feature map的信息”,因而要引入pointwise convolution:
计算量对比:
其中DK为标准卷积核大小,M是输入feature map通道数,DF为输入feature map大小,N是输出feature map大小。本例中,DK=3,M=2,DF=5,N=3 , 参数的减少量主要就与卷积核大小DK有关。在本文MobileNet的卷积核采用DK=3,则大约减少了8~9倍计算量。
shuffleNet 是 Face++ 团队提出的,论文标题《ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices 》。一看名字shuffleNet,就知道shuffle是本文的重点。那么 shuffle 是什么? 为什么要 shuffle 呢?
shuffle 具体说是 channel shuffle,是将各部分的 feature map 的channel进行有序的打乱,构成新的 feature map,以解决 group convolution 带来的“信息流通不畅”问题。(mobileNet 是用 pointwise convolution 解决的这个问题)。由此可知, shuffle 不是任何网络都需要的,当采用了 group convolution 才有可能需要 shuffle 操作。为什么说是有可能,是因为还可以采用 point-wise convolution 或者其他新的方法来解决这个问题。
创新点:利用 group convolution 和 channel shuffle 着两个操作来设计卷积神经网络模型,以减少模型使用的参数数量。group convolution 并非原创,而 channel shuffle 是原创,channel shuffle 因 group convolution 而起。
对比一下 mobileNet,采用shuffle 替换掉 1*1 卷积(注意!是 1*1 Conv,也就是 pointwise convolution;特别注意的是 pointwise convolution 和 1*1 GConv 是不同的,1*1 GConv 更准确的理解是 pointwise group convolution;前者实现不同通道间的信息流通不畅,而后者仅仅是单通道卷积并造成信息不通畅问题),这样可以减少权值参数,而且是减少大量的权值参数,因为在 mobileNet中, 1*1 卷积有较多的卷积核,并且计算量巨大,mobileNet 每层的参数量和运算量如下图示:
shuffleNet 的创新点在于利用了group convolution 和 channel shuffle。
group convolution 自 AlexNet 就有,当时因为硬件限制而采用分组卷积;之后在2016年的 ResNeXt中,表明采用 group convolution 可获得高效的网络;Xception 和 mobileNet 均采用 depth-wise convolution,这些都是最近新出来的一系列轻量化网络模型。(group convolution 和 depth-wise 都会造成信息流通不畅问题,违背了观点 “输出的每一个feature map要包含输入层所有feature map的信息”)
如下图a所示,为了提升模型效率,采用 group convolution 但会有一个副作用,即 “ outputs from a certain channel are only derived from a small fraction of input channels. ” 于是采用channel shuffle 来改善各组间的“信息流通不畅”问题,如下图b所示。具体方法是:把各组的 channel 平均分成 g 份(下图 g = 3),然后依次序重新构成feature map。
对于一个卷积层分为 g 组:
-- 1. 有 g * n 个输出通道
-- 2. reshape为(g, n)
-- 3. 再转置为(n,g)
-- 4. 平坦化,再分为 g 组作为下一层的输入
示意图:
Shuffle Unit
在实际过程中我们构建一个 ShuffleNet Unit,便于构建实际模型。参考链接
图(a)是一个残差模块。对于主分支部分,我们可将其中标准卷积3×3拆分成深度分离卷积。我们将第一个1×1 卷积替换为逐点组卷积,再作通道混洗(即(b))
图(b)即ShuffleNet unit,主分支最后的1×1Conv改为1×1GConv,为了适配和恒等映射做通道融合。配合BN层和ReLU激活函数构成基本单元
图(c)即是做降采样的ShuffleNet unit,这主要做了两点修改:1. 在辅分支加入步长为2的3×3平均池化;2. 原本做元素相加的操作转为了通道级联,这扩大了通道维度,增加的计算成本却很少
NetWork Architecture
在上面的基本单元基础上,提出了 ShuffleNet 的整体架构:
主要分为三个阶段:
----每个阶段的第一个block的步长为2,下一阶段的通道翻倍
----每个阶段内的除步长其他超参数保持不变
----每个ShuffleNet unit的bottleneck通道数为输出的1/4(和ResNet设置一致)
这里主要是给出一个baseline。在ShuffleNet Unit中,参数g控制逐点卷积的连接稀疏性(即分组数),对于给定的限制下,越大的g会有越多的输出通道,这帮助我们编码信息。
ShuffleNet小结:
1.与MobileNet一样采用了depthwise convolution,但是针对 depthwise convolution带来的副作用——“信息流通不畅”,ShuffleNet采用了一个channel shuffle 操作来解决。
2.在网络拓扑方面,ShuffleNet采用的是ResNet的思想,而MobileNet采用的是VGG的思想,SqueezeNet也是采用VGG的堆叠思想。
参考链接1 参考链接2
Inception 是神经网络结构的一大神作,其提出的多尺寸卷积和多个小卷积核替代大卷积核等概念是现如今许多优秀网络架构的基石。也正是如此,基于此的 Xception 应运而生,作者称其为 Extreme Inception,增加的 Depthwise Separable Convolution也是让人眼前一亮。
本文不详细讲解论文内容,只探讨提出的几个基础概念和结构,并按时间顺序介绍。首先探讨的是 Inception 的多尺寸卷积核和 卷积核替换,然后是 Bottleneck,最后到 Xception 的 Depthwise Separable Convolution。
多尺寸卷积核
Inception 最初提出的版本,其核心思想就是使用多尺寸卷积核去观察输入数据。举个例子,我们看某一个景象由于远近不同,同一个物体的大小也会有所不同,那么不同尺度的卷积核观察的特征就会有不同的效果。于是就有了如下的网路结构图:
于是网络结构就变胖了,增加了网络的宽度,同时也提高了对不同尺度的适应程度。
Pointwise Convolution
但是我们的网络变胖的同时,计算量也变大了,所以我们就想办法减少参数量来减少计算量,于是Inception v1 中的最终版本加上了 1*1 卷积核。
使用 1*1 卷积核对输入的特征图进行降维处理,这样就会极大地减少参数量,从而减少计算。
举个例子,输入数据的维度是 256 维,经过 1*1 卷积之后,我们输出的维度是 64 维,参数量是原来的 1/4。这就是 Pointwise Convolutionm,俗称 1*1 卷积,简写为 PW,主要用于数据降维从而减少参数量。也有使用 PW 做升维的,在 MobileNet v2 中就使用 PW 将 3 个特征图变成 6 个特征图,丰富输入数据的特征。具体可以参阅相关论文。
卷积核替换
就算有了 PW,由于 5*5 和 7*7 卷积核直接计算导致计算量还是非常大的,训练时间还是比较长,我们还需要再优化。人类的智慧是无穷的,于是想出了 使用多个小卷积核替代大卷积核的方法,这就是 Inception v3,如图所示:
使用两个 3*3 卷积核来代替 5*5 卷积,效果上差不多,但参数量减少很多,达到优化的目的。不仅参数量减少了。层数也变多了,深度也随之加深。
除了规整的正方形,还有分解版本的 3*3 = 3*1 + 1*3 ,这个效果在深度较深的情况下比规整的卷积核更好:
我们假设输入256维,输出512维,计算一下参数量:
5*5 卷积核: 256*5*5*512 = 3276800
两个 3*3 卷积核: 256*3*3*256+256*3*3*512=589824+1179648=1769472
结果对比发现 1769472/3276800= 0.54,两个 3*3 卷积核的参数量是 5*5 的一半,可以大大加快训练速度。
Bottleneck
我们发现就算用了上面的结构和方法,参数量还是很大,于是乎我们结合上面的方法创造出 Bottleneck 的结构降低参数量。
Bottleneck 三步走是先 PW 对数据进行降维,在进行常规卷积核的卷积,最后 PW 对数据进行升维。我们举个例子,方便我们理解:
根据上图,我们做一个对比计算,假设输入 feature map 的维度为 256 维,要求输出的维度也是 256 维。有以上面的两种操作:
--直接使用 3*3 的卷积核。参数量 256*3*3*256 = 589824
--先使用 1*1 的卷积核,再经过 3*3 卷积核,最后经过一个 1*1 卷积核。参数量 256*1*1*64+ 64*3*3*64+ 64*1*1*256 = 69632
经过两种方式的对比,我们可以很明显的看到后者的参数量远小于前者。 Bottleneck 的核心思想还是利用多个小卷积核替代一个大卷积核,利用 1*1 卷积核替代大卷积核的的一部分工作。
Depthwise Separable Convolution
我们发现参数还是很多,于是人们想了又想,得出了 Depthwise Separable Convolution。这个想法最早来自论文《Design of Efficient Convolutional Layers using Single Intra-channel Convolution, Topological Subdivisioning and Spatial "Bottleneck" Structure》,后来被 Google 用在 MobileNet 和 Xception 中发扬光大。
这个卷积的大致意思是对每一个深度图进行卷积再融合,步骤是先 Depthwise Conv 再 Pointwise Conv,大大减少了参数量,下图是 Xception 模块的结构:
大致的步骤是这样的:
-- 分别按照不同通道进行一次卷积(生成 输入通道数 张 feature maps,不同通道之间的信息未共享)DW
--再将这些 feature maps 一起进行第二次卷积(解决不同通道信息的共享问题) PW
文字看起来有点抽象,举个例子来理解一下:输入的是 2 维的数据进行 3*3 卷积并输出 3 维的数据,与正常卷积对比:
正常卷积:
计算量:2*3*3*3 = 54
Depthwise Separable Convolution:
计算量:2*3*3 + 2*1*1*3 = 18+6 = 24
参数量对比 24/54 = 0.444 ,参数量是正常卷积的一半,在实际输入输出维度相差较大的情况下,效果更大明显。
总结:
从 Inception
到 Xception
的发展一路看来,每一次创新都让人啧啧称赞,精巧的结构设计和理念思想,让人佩服:1. 多个不同尺寸的卷积核,提高对不同尺度特征的适应能力;2. PW
卷积,降维或升维的同时,提高网络的表达能力;3. 多个小尺寸卷积核替代大卷积核,加深网络的同时减少参数量; 4. 精巧的 Bottleneck
结构,大大减少网络参数量; 5. 精巧的 Depthwise Separable Conv
设计,再度减少参数量。
了解了这些基础结构的思想,我们就可以站在巨人的肩膀上更好地向前看,走向更优秀的方向。