项目实现:GitHubcss
参考博客:CNN模型之ShuffleNethtml
v1论文:ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devicesgit
v2论文:ShuffleNet V2: Practical Guidelines for Ecient CNN Architecture Designgithub
Group convolution是将输入层的不一样特征图进行分组,而后采用不一样的卷积核再对各个组进行卷积,这样会下降卷积的计算量。由于通常的卷积都是在全部的输入特征图上作卷积,能够说是全通道卷积,这是一种通道密集链接方式(channel dense connection),而group convolution相比则是一种通道稀疏链接方式(channel sparse connection)。网络
『高性能模型』深度可分离卷积和MobileNet_v1ide
tensorflow和分组卷积的讨论:Feature Request: Support for depthwise convolution by groups函数
使用group convolution的网络有不少,如Xception,MobileNet,ResNeXt等。其中Xception和MobileNet采用了depthwise convolution,这是一种比较特殊的group convolution,此时分组数刚好等于通道数,意味着每一个组只有一个特征图。是这些网络存在一个很大的弊端是采用了密集的1x1 pointwise convolution(以下图)。性能
这个问题能够解决:对1x1卷积采用channel sparse connection 即分组操做,那样计算量就能够降下来了,但这就涉及到另一个问题。ui
group convolution层另外一个问题是不一样组之间的特征图须要通讯,不然就好像分了几个互不相干的路,你们各走各的,会下降网络的特征提取能力,这也能够解释为何Xception,MobileNet等网络采用密集的1x1 pointwise convolution,由于要保证group convolution以后不一样组的特征图之间的信息交流。spa
为达到特征通讯目的,咱们不采用dense pointwise convolution,考虑其余的思路:channel shuffle。如图b,其含义就是对group convolution以后的特征图进行“重组”,这样能够保证接下了采用的group convolution其输入来自不一样的组,所以信息能够在不一样组之间流转。图c进一步的展现了这一过程并随机,实际上是“均匀地打乱”。
在程序上实现channel shuffle是很是容易的:假定将输入层分为 组,总通道数为
,首先你将通道那个维度拆分为
两个维度,而后将这两个维度转置变成
,最后从新reshape成一个维度
。
ShuffleNet的核心是采用了两种操做:pointwise group convolution和channel shuffle,这在保持精度的同时大大下降了模型的计算量。其基本单元则是在一个残差单元的基础上改进而成。
下图a展现了基本ResNet轻量级结构,这是一个包含3层的残差单元:首先是1x1卷积,而后是3x3的depthwise convolution(DWConv,主要是为了下降计算量),这里的3x3卷积是瓶颈层(bottleneck),紧接着是1x1卷积,最后是一个短路链接,将输入直接加到输出上。
下图b展现了改进思路:将密集的1x1卷积替换成1x1的group convolution,不过在第一个1x1卷积以后增长了一个channel shuffle操做。值得注意的是3x3卷积后面没有增长channel shuffle,按paper的意思,对于这样一个残差单元,一个channel shuffle操做是足够了。还有就是3x3的depthwise convolution以后没有使用ReLU激活函数。
下图c展现了其余改进,对原输入采用stride=2的3x3 avg pool,在depthwise convolution卷积处取stride=2保证两个通路shape相同,而后将获得特征图与输出进行链接(concat,借鉴了DenseNet?),而不是相加。极致的下降计算量与参数大小。
能够看到开始使用的普通的3x3的卷积和max pool层。而后是三个阶段,每一个阶段都是重复堆积了几个ShuffleNet的基本单元。对于每一个阶段,第一个基本单元采用的是stride=2,这样特征图width和height各下降一半,而通道数增长一倍。后面的基本单元都是stride=1,特征图和通道数都保持不变。对于基本单元来讲,其中瓶颈层,就是3x3卷积层的通道数为输出通道数的1/4,这和残差单元的设计理念是同样的。
下表给出了不一样g值(分组数)的ShuffleNet在ImageNet上的实验结果。能够看到基本上当g越大时,效果越好,这是由于采用更多的分组后,在相同的计算约束下可使用更多的通道数,或者说特征图数量增长,网络的特征提取能力加强,网络性能获得提高。注意Shuffle 1x是基准模型,而0.5x和0.25x表示的是在基准模型上将通道数缩小为原来的0.5和0.25。
除此以外,做者还对比了不采用channle shuffle和采用以后的网络性能对比,以下表的看到,采用channle shuffle以后,网络性能更好,从而证实channle shuffle的有效性。
而后是ShuffleNet与MobileNet的对比,以下表ShuffleNet不只计算复杂度更低,并且精度更好。
咱们首先来看v1版本和v2版本的基础单元,(a)和(b)是ShuffleNet v1的两种不一样block结构,二者的差异在于后者对特征图尺寸作了缩小,这和ResNet中某个stage的两种block功能相似,同理(c)和(d)是ShuffleNet v2的两种不一样block结构:
看点以下:
从(a)和(c)的对比能够看出首先(c)在开始处增长了一个channel split操做,这个操做将输入特征的通道分红c-c’和c’,c’在文章中采用c/2,这主要是和第1点发现对应
而后(c)中取消了1*1卷积层中的group操做,这和第2点发现对应,同时前面的channel split其实已经算是变相的group操做了
channel shuffle的操做移到了concat后面,和第3点发现对应,同时也是由于第一个1*1卷积层没有group操做,因此在其后面跟channel shuffle也没有太大必要
最后是将element-wise add操做替换成concat,这个和第4点发现对应。
多个(c)结构链接在一块儿的话,channel split、concat和channel shuffle是能够合并在一块儿的。(b)和(d)的对比也是同理,只不过由于(d)的开始处没有channel split操做,因此最后concat后特征图通道数翻倍,能够结合后面具体网络结构来看:
如今咱们查看一个新的概念:内存访问消耗时间(memory access cost),它正比于模型对内存的消耗(特征大小+卷积核大小),在这篇文章中做者使用该指标来衡量模型的速度而非传统的FLOPs(float-point operations),它更多的侧重卷积层的乘法操做,做者认为FLOPs并不能实质的反应模型速度。
如今咱们来看一下这四点发现,分别对应告终构中的四点创新:
卷积层的输入和输出特征通道数相等时MAC最小,此时模型速度最快
过多的group操做会增大MAC,从而使模型速度变慢
模型中的分支数量越少,模型速度越快
element-wise操做所带来的时间消耗远比在FLOPs上的体现的数值要多,所以要尽量减小element-wise操做
至于模型的具体提高,建议之间看原文的图表。