ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design

1. 摘要

最近,神经网络的架构设计都是基于计算复杂度的间接度量,比如 FLOPs。然而,直接的度量比如运行速度,其实也会依赖于内存访问和平台特性等其它因素。

因此本文建议直接在目标平台上用直接度量进行测试。基于一系列控制条件实验,作者提出了设计高效网络结构的一些实用指导思想,并据此提出了一个称之为 ShuffleNet V2 的新结构。

2. 介绍

为了衡量计算复杂度,一个广泛采用的度量方式是浮点运算的次数 FLOPs,但是,它是一个间接的度量,是对我们真正关心的直接度量比如速度或者时延的一种近似估计。在以前的工作中,这种不一致已经被学者们所发现,比如 MobileNet v2 要比 NASNET-A 快很多,但是它们两者具有差不多的 FLOPs。

上图中在 GPU 和 ARM 两个平台上,具有相同 FLOPs 的模型运行速度也会相差很多。因此只用 FLOPs 来衡量计算复杂度是不充分的,也会导致得不到最优的网络设计。

导致这种不一致的主要有两个原因:一是影响速度的几个重要因素只通过 FLOPs 是考虑不到的,比如 MAC(Memory Access Cost)和并行度;二是具有相同 FLOPs 的模型在不同的平台上可能运行速度不一样。

因此,作者提出了设计有效网络结构的两个原则。一是用直接度量来衡量模型的性能,二是直接在目标平台上进行测试。

3. 高效网络设计的实用指导思想

首先,作者分析了两个经典结构 ShuffleNet v1 和 MobileNet v2 的运行时间。

可以看到,虽然以 FLOPs 度量的卷积占据了大部分的时间,但其余操作也消耗了很多运行时间,比如数据输入输出、通道打乱和逐元素的一些操作(张量相加、激活函数)。因此,FLOPs 不是实际运行时间的一个准确估计。

G1:同样大小的通道数可以最小化 MAC。

深度可分离卷积中的点卷积比如 1×1 占据了大部分的复杂度。假设输入的特征图大小为 \(h*w*c_1\),那么输出通道数为 \(c_2\) 的 1×1 卷积的 FLOPs 为 \(B=hwc_1c_2\)

简单起见,我们假设计算设备的缓存足够大能够存放下整个特征图和参数。那么内存访问代价就为 \(MAC=hw(c_1+c_2)+c_1c_2\),这三项分别代表输入特征图、输出特征图和参数的代价。所以我们有:

\[\begin{aligned} MAC&=hw(c_1+c_2)+c_1c_2 \\ &=\sqrt{(hw)^2(c_1+c_2)^2}+\frac{B}{hw}\\ &\geqslant\sqrt{(hw)^2 4c_1c_2}+\frac{B}{hw}\\ &=2\sqrt{hwB}+\frac{B}{hw} \end{aligned} \]

当且仅当 \(c_1=c_2\) 时,MAC 取得最小值。但是这个结论只是理论上成立的,实际中缓存容量可能不够大,缓存策略也因平台各异。所以作者进一步设计了一个对比试验来验证,基准的网络由 10 个块组成,每个块有两层卷积,第一个卷积层输入通道数为 \(c_1\) 输出通道数为 \(c_2\),第二层与第一层相反,然后固定总的 FLOPs 调整 \(c_1:c_2\) 的值测试实际的运行速度,结果如下所示:

可以看到,当比值接近 1:1 的时候,网络的测试速度最快。

G2:太多的分组卷积会增加 MAC。

分组卷积是现在网络结构设计的核心,它通过通道之间的稀疏连接(也就是只和同一个组内的特征连接)来降低计算复杂度。一方面,它允许我们使用更多的通道数来增加网络容量进而提升准确率,但另一方面随着通道数的增多也对带来更多的 MAC。

针对 1×1 的分组卷积,我们有:

\[B=h*w*1*1*\frac{c_1}{g}*\frac{c_2}{g}*g=\frac{hwc_1c_2}{g} \]

\[MAC=hw(c_1+c_2)+\frac{c_1c_2}{g}=hwc_1+\frac{Bg}{c_1}+\frac{B}{hw} \]

其中 \(B\) 是需要的浮点数运算次数,\(g\) 是分组卷积的组数,可以看到,如果给定输入特征图的大小和计算代价,那么 MAC 与组数成正比。作者通过叠加 10 个分组点卷积层设计了实验,在保证计算代价相同的情况下采用不同的分组组数测试模型的运行时间,结果如下所示:

可以看到,分为 8 个组要比 1 个组慢得多。因此,作者建议要根据目标平台和任务小心地选择分组的组数,不能简单地因为可以提升准确率就选择很大的组数,而忽视了因此带来的巨大计算负担。

G3:网络碎片化会减少并行度。

在 Inception 结构中,一般会有多个分支,其中的每一个卷积或者池化操作称之为一个碎片操作。这种碎片结构有利于提升准确率,但却对设备的并行计算不友好,而且也会带来同步等额外的开销。因此,作者设计了下面的一系列结构块,堆叠 10 次组成一个网络,然后在同等 FLOPs 的情况下测试它们各自的运行速度。

实验结果如下所示,可以看到在 GPU 上碎片结构会大大降低运算速度,而在 CPU 上则不是那么明显。

G4:逐元素的操作不可忽视。

这里逐元素(Element-wise)的操作包括张量相加,ReLU 等,它们的 FLOPs 很小但却会有相对较大的 MAC,这里作者认为深度卷积也是逐元素的操作,因为它们的 MAC/FLOPs 比值比较大。

作者采用了 ResNet 的瓶颈结构来实验,也就是一个 conv 1×1->conv 3×3->conv 1×1 的结构,分别去掉其中的 ReLU 和跳跃连接,然后测试它们各自的运行速度。可以看到无论是去掉其中哪一个操作,运行速度都会加快。

因此,高效的网络结构应该满足:1. 使用平衡的卷积,也就是通道数一样;2. 合理使用分组卷积;3. 减少碎片度;4. 减少逐元素操作。这些在实际中都应该被考虑到而不是仅仅只关注 FLOPs,比如 ShuffleNet V1 严重依赖分组卷积,这违反了 G2;MobileNet v2 利用了反转瓶颈结构,这违反了 G1,而且在通道数较多的扩展层使用 ReLU 和深度卷积,违反了 G4,它是自动生成的结构碎片化很严重,这违反了 G3。

4. ShuffleNet V2

ShuffleNet V1 引入了分组点卷积、瓶颈结构和通道打乱,这与上面的指导思想相违背,因此我们要做的就是维持一个较大并且等宽的通道但不利用密集卷积或者太多的分组卷积。

因此作者引入了一个通道分割(channel split)操作,如上图(c)所示。在每个单元的开始,我们将特征图的 \(c\) 个通道分为两部分: \(c-c'\) 个通道和 \(c'\) 个通道,根据 G3 碎片尽可能少,其中一部分保持不变,另一部分包含三个通道数一样的卷积来满足 G1。两个 1×1 的卷积不进行分组一部分是为了满足 G2 一部分是因为通道分割已经分成了两组。最后,对两部分的特征进行拼接,这样通道数依然保持不变,最后进行一个通道打乱来保证两部分的信息进行交互。

针对空间下采样,通道分割被移除,然后输出通道数变为两倍,详细信息如上图(d)所示。

上述的基本块叠加在一起来组成最后的 ShuffleNet V2,其中 \(c'=c/2\),在平均池化后还有一个额外的 1×1 的卷积来对特征进行混合,详细信息如下表所示。同样,我们也可以对每一个块的通道数进行缩放来进一步调整网络的大小。

ShuffleNet V2 不仅仅非常高效,而且准确率也很高。有两个主要的原因:一是高效的基本块可以允许我们的特征通道数比较多,网络容量比较大;二是一半的特征图直接进入到下一个模块,这可以看作是一种类似于 DenseNet 和 CondenseNet 的特征复用。

在 DenseNet 中,作者分析了不同层之间权重的相关性,如下图左边所示,可以看到,相邻层之间的关联性是远远大于其它层的,这也就是说所有层之间的密集连接可能是多余的。

在 ShuffleNet V2 中,可以证明,第 \(i\) 层和第 \(i+j\) 层之间直接相连的特征图通道数为 \(r^jc\),其中 \(r=(1-c')/c\)。换句话说,特征复用的数量随着两个块之间的距离是指数级衰减的,如上图右边所示。

5. 实验结果

此外,ShuffleNet V2 的结构也可以用来构建大的模型或者和残差结构、SE 等相结合具体细节可参考原论文。

获取更多精彩,请关注「seniusen」!

posted @ 2019-12-16 10:51  seniusen  阅读(715)  评论(0编辑  收藏  举报