[关闭]
@huanghaian 2020-12-02T06:05:24.000000Z 字数 4960 阅读 988

移动端模型:shufflenetv2

分类


0 摘要

论文名称:ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design
论文地址: https://arxiv.org/abs/1807.11164

shufflenetv2论文最大贡献不是提出了一个新的模型,而是提出了4条设计高效CNN的规则,该4条规则考虑的映射比较多,不仅仅FLOPS参数,还可以到内存使用量、不同平台差异和速度指标等等,非常全面。在不违背这4条规则的前提下进行改进有望设计出速度和精度平衡的高效模型。

1 算法设计

1.1 算法核心

到目前为止,前人所提轻量级CNN框架取得了很好效果,例如Xception、MobileNet、MobileNetV2和ShuffleNetv1等,主要采用技术手段是分组卷积Group convolution和逐深度方向卷积depthwise convolution

在上述算法中,除了精度要求外,评价模型复杂度都是采用FLOPs指标(每秒浮点加乘运算次数),在mobilnetv1、v2和ShuffleNet都是采用该指标的。然而FLOPs 其实是一种间接指标,它只是真正关心的直接指标(如速度或延迟)的一种近似形式,通常无法与直接指标划等号。先前研究已对这种差异有所察觉,比如MobileNet V2要远快于 NASNET-A(自动搜索出的神经网络),但是两者FLOPs相当,它表明FLOPs近似的网络也会有不同的速度。所以,将FLOPs作为衡量计算复杂度的唯一标准是不够的,这样会导致次优设计。

研究者注意到FLOPs仅和卷积部分相关,尽管这一部分需要消耗大部分的时间,但其它过程例如数据 I/O、数据重排和元素级运算(张量加法、ReLU 等)也需要消耗一定程度的时间。

间接指标(FLOPs)和直接指标(速度)之间存在差异的原因可以归结为两点:

其次,FLOPs相同的运算可能有着不同的运行时间,这取决于平台。例如,早期研究广泛使用张量分解来加速矩阵相乘。但是,近期研究发现张量分解在GPU上甚至更慢,尽管它减少了75%的 FLOPs。本文研究人员对此进行了调查,发现原因在于最近的 CUDNN库专为3×3 卷积优化:当然不能简单地认为3×3卷积的速度是1×1卷积速度的1/9。

总结如下就是:

  1. 影响模型运行速度还有别的指标,例如MAC(memory access ),并行度(degree of parallelism)
  2. 不同平台有不同的加速算法,这导致flops相同的运算可能需要不同的运算时间。

据此,提出了高效网络架构设计应该考虑的两个基本原则:第一,应该用直接指标(例如速度)替换间接指标(例如 FLOPs);第二,这些指标应该在目标平台上进行评估。在这项研究中,作者遵循这两个原则,并提出了一种更加高效的网络架构。

1.2 4条高效网络设计原则

G1. 相同的通道宽度可最小化内存访问成本(MAC)
假设内存足够大一次性可存储所有特征图和参数;卷积核大小为 ;输入通道有c1个;输出通道有c2个;特征图分辨率为,则在1x1的卷积上,FLOPS:,容易推出:
image.png-7.2kB
MAC是内存访问量,hwc1是输入特征图内存大小,hwc2是输出特征图内存大小,c1xc2是卷积核内存大小。从公式中我们可以得出MAC的一个下界,即当c1==c2 时,MAC取得最小值。以上是理论证明,下面是实验证明:
image.png-42kB
可以看出,当c1==c2时,无论在GPU平台还是ARM平台,均获得了最快的runtime。

G2. 过度使用组卷积会增加MAC
和(1)假设一样,设g表示分组数,则有:
image.png-11.9kB
其中,当固定c1 w h和B,增加分组g,MAC也会增加,证明了上述说法。其中c1 w h固定,g增加的同时B复杂度也要固定,则需要把输出通道c2增加,因为g增加,复杂度是要下降的。以上是理论证明,下面是实验证明:
image.png-33.7kB
可以看出,g增加,runtime减少。

G3. 网络碎片化(例如 GoogLeNet 的多路径结构)会降低并行度
理论上,网络的碎片化虽然能给网络的accuracy带来帮助,但是在平行计算平台(如GPU)中,网络的碎片化会引起并行度的降低,最终增加runtime,同样的该文用实验验证:
image.png-50.4kB
可以看出,碎片化越多,runtime越大。

G4. 元素级运算不可忽视
image.png-177.7kB

element-wise operations也占了不少runtime,尤其是GPU平台下,高达15%。ReLU、AddTensor及AddBias等这一类的操作就属于element-wise operations. 这一类操作,虽然flops很低,但是MAC很大,因而也占据相当时间。同样地,通过实验分析element-wise operations越少的网络,其速度越快。
image.png-39.6kB

以上就是作者提出的4点设计要求,总结如下

结合上述4点可以对目前移动端网络进行分析:

1.3 算法模型

在ShuffleNetv1的模块中,大量使用了1x1组卷积,这违背了G2原则,另外v1采用了类似ResNet中的瓶颈层(bottleneck layer),输入和输出通道数不同,这违背了G1原则。同时使用过多的组,也违背了G3原则。短路连接中存在大量的元素级Add运算,这违背了G4原则。

基于以上缺点,作者重新设计了v2版本。V2主要结构是多个blocks堆叠构成,block又分为两种,一种是带通道分离的Channel Spilit,一种是带Stride=2。图示如下:
image.png-119.6kB
(a)为v1中的stride=1的block,(b)为v1中的stride=2的block,(c)是新设计的v2中的stride=1的block,(d)是新设计的v2中的stride=2的block。

在每个单元开始,c 特征通道的输入被分为两支,分别占据c−c'和c'个通道(一般设置c=c'*2)。左边分支做同等映射,右边的分支包含3个连续的卷积,并且输入和输出通道相同,这符合G1。而且两个1x1卷积不再是组卷积,这符合G2,另外两个分支相当于已经分成两组。两个分支的输出不再是Add元素,而是concat在一起,紧接着是对两个分支concat结果进行channle shuffle,以保证两个分支信息交流。其实concat和channel shuffle可以和下一个模块单元的channel split合成一个元素级运算,这符合原则G4。

对于stride=2的模块,不再有channel split,而是每个分支都是直接copy一份输入,每个分支都有stride=2的下采样,最后concat在一起后,特征图空间大小减半,但是通道数翻倍。

具体代码上非常简单,如下所示:

  1. class InvertedResidual(nn.Module):
  2. def __init__(self,
  3. in_channels,
  4. out_channels,
  5. stride=1,
  6. conv_cfg=None,
  7. norm_cfg=dict(type='BN'),
  8. act_cfg=dict(type='ReLU'),
  9. with_cp=False):
  10. super(InvertedResidual, self).__init__()
  11. self.stride = stride
  12. branch_features = out_channels // 2
  13. if self.stride > 1:
  14. # 当stride=2才需要
  15. self.branch1 = nn.Sequential(
  16. ConvModule(
  17. in_channels,
  18. in_channels,
  19. kernel_size=3,
  20. stride=self.stride,
  21. padding=1,
  22. groups=in_channels,
  23. conv_cfg=conv_cfg,
  24. norm_cfg=norm_cfg,
  25. act_cfg=None), # 没有激活函数
  26. ConvModule(
  27. in_channels,
  28. branch_features,
  29. kernel_size=1,
  30. stride=1,
  31. padding=0,
  32. conv_cfg=conv_cfg,
  33. norm_cfg=norm_cfg,
  34. act_cfg=act_cfg),
  35. )
  36. # 和mobilenetv2中的反转残差块差不多,但是激活函数relu位置不一样
  37. self.branch2 = nn.Sequential(
  38. ConvModule(
  39. in_channels if (self.stride > 1) else branch_features,
  40. branch_features,
  41. kernel_size=1,
  42. stride=1,
  43. padding=0,
  44. conv_cfg=conv_cfg,
  45. norm_cfg=norm_cfg,
  46. act_cfg=act_cfg),
  47. ConvModule(
  48. branch_features,
  49. branch_features,
  50. kernel_size=3,
  51. stride=self.stride,
  52. padding=1,
  53. groups=branch_features,
  54. conv_cfg=conv_cfg,
  55. norm_cfg=norm_cfg,
  56. act_cfg=None),
  57. ConvModule(
  58. branch_features,
  59. branch_features,
  60. kernel_size=1,
  61. stride=1,
  62. padding=0,
  63. conv_cfg=conv_cfg,
  64. norm_cfg=norm_cfg,
  65. act_cfg=act_cfg))
  66. def forward(self, x):
  67. def _inner_forward(x):
  68. if self.stride > 1:
  69. # stride=2的模块
  70. out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
  71. else:
  72. # stride=1的模块,直接spilt,x1不变,x2进行逐深度可分离卷积计算
  73. x1, x2 = x.chunk(2, dim=1)
  74. out = torch.cat((x1, self.branch2(x2)), dim=1)
  75. out = channel_shuffle(out, 2)
  76. return out
  77. return _inner_forward(x)

以上两个部分就可以构成各种block,在block基础上构造网络,如下图:
image.png-88.5kB

同样的有宽度因子来对通道进行缩减,主要0.5、1.0、1.5和2.0 4中配置。

2 实验结果

image.png-57kB
可以看出,和v1相比,在相同FLOP情况下,错误率更低,且和其他模型对比,复杂度明显下降。
image.png-63.5kB
上述结果是在coco目标检测上的结果,输入图像大小是 800×1200。FLOPs 行列出了输入图像大小为 224×224 时的复杂度水平。带*的shuffleNet v2是变种,区别是扩大了感受野,具体为在每个block的第一个pointwise卷积的前面插入一个额外的3x3深度卷积。可以看出,精度提高不少,且FLOPs仅仅增加一点。这就留下一个疑问:如何增加网络的感受野也是一个值得探讨的问题。

3 总结

shufflenetv2针对目前移动端模型都仅仅考虑精度和FLOPS参数,但是这两个参数无法直接反映真正高效网络推理要求,故作者提出了4条高效网络设计准则,全面考虑了各种能够影响速度和内存访问量的因素,并给出了设计规则。再此基础上设计了shufflenetv2,实验结果表明速度和精度更加优异。

4 参考文献

ShuffleNetV2:轻量级CNN网络中的桂冠

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注