如何理解 MobileNet V2:线性激活 + 倒置残差模块 + 高效内存

MobileNetV2: Inverted Residuals and Linear Bottlenecks 2019 年

Abstract

  • 提出了新的 MobileNet V2,表现方面也是刷新了当时的 sota;

  • 提出了 SSDLite 用于 Object Detection,我当时比较关注 OD,所以实验部分就先只写 OD 了;

  • 提出了如何从 DeepLabv3 构建移动端语义分割模型 Mobile DeepLabV3;

  • MobileNet V2 基于新提出的 inverted residual structure,其中的连接在 bottleneck 层之间;

  • 中间的 expansion layer 使用轻量的深度卷积来提取特征,作为非线性源;

  • 还发现在 narrow layer 中最好去除非线性以较好地维持表征能力,这一点做了实验还有证明;

  • 允许将 输入输出域变换的表现力 分离开(还没很好理解)。

Introduction

移动端部署需求不再赘述。

本文主要的贡献是一个新的 layer module:the inverted residual with linear bottleneck。模块的输入是低维压缩表征,表征先被映射到高维中并使用轻量的深度卷积来提取特征,随后再用线性卷积(这里想强调的是对比非线性吧)将特征映射回低维空间

这个模块可以轻易地在各个框架中实现,且非常适合移动端,因为全过程几乎从来不直接使用大尺寸张量。

Preliminaries, discussion and intuition

Depthwise Seperable Convolutions

这部分深度分离卷积已经写过了,不再赘述。

Linear Bottlenecks

原文这部分讲得很学术了(感觉扣着读很爽2333)。

不严谨地来讲,神经网络模型越深,表达能力越强,随着层深的增加,特征也会有变化。以视觉任务为例,网络的前几层可能都在提取一些最基础细碎的特征,比如边缘、颜色等,这些特征有点像积木中的基础零部件,使用这些特征可能可以组成任何图像。而到后面的网络层中,可能特征就会更像大一点的模块,演变成高级的语义的特征,比如“鼻子”。

那么若以最初始的、常见的 3 个通道的 h w c 输入图像来讲,这张图像的全部信息都在这 h w c 的张量(论文叫 activation tensor)中了(好挤呀),那么这个 h w c 的张量空间中的特征密度一定是很大(当然也有一些冗余/不关注的信息),其他相对靠前的模型层也是类似的道理。

对于任何这样一张输入图像,模型中的每一个 layer activation 形成一个 manifold of interest(感兴趣流形(好别扭~其实就是信息啦,tensor))。然后我们都知道线性变换可以认为是不损失原本维度空间中的信息的,而非线性变换就不一定了。而 Relu 这个激活函数吧,它 0 点左边是 0,右边才是线性变换,什么意思呢?就是但凡要经过 Relu 的信息,只要你有落在 0 以左的就被损失掉啦。

说明:
这个只能是 Relu 的特性,并不能算它的缺点,Relu 在很多情况下都是一个很好的非线性变换器,只是在 MobileNet 系列中有时候会帮倒忙。为什么呢?还记得 MobileNet V1 清爽地将卷积核的 h w 面与通道数拆开了吗?所以呀,MobileNet 的卷积是只在单个通道上的,那要是以常见输入图像来说,一共就 3 个通道,每个通道上的信息/特征都很稠密啊,这个时候直接用 Relu,0 点左边就给你扔了呀。所以对于 MobileNet 系列来说,它就一定会假定输入进来的是低维的/稠密的特征,你想分开,就先映射到高维再去分。

那在模型的前几层就大幅度使用 Relu 是什么效果呢?就是豆子和豆荚皮儿还都混在一起呢,你非要让 Relu 帮你切一刀把豆豆分出来(太难了)。Relu 能怎么办,硬着头皮弄,损失很多豆子,还留下很多豆荚皮儿。

那在 MobileNet 的情况下,应该怎么更合理地使用 Relu 呢?就是让豆子都去 0 的右边,然后再让 Relu 切出来,保留住这部分有效特征。那如何让豆子都去 0 的右边?又回到了很基础的问题,在一个低维空间中不好分或者不可分的时候,可以将其映射到高维空间中,然后再去高维空间中找到超平面分隔开就好了。对,就是把豆子豆荚皮儿一起高高地抛起来,豆荚皮儿会落下得更慢,在高度上豆子和豆荚皮儿就更容易分开了,这个时候用 Relu 把豆子和豆荚皮儿分开就好了。抛得越高越容易分开,从稠密的、不可分的低维空间映射到越高维的空间,就越能给 Relu 创造应用空间,留下的有效特征(豆子)就越多。

总结一下,由于 Relu 的两点特性,即右边是线性变换 + 当感兴趣流形都在右边的时候就能保留全部信息(只是说保留全部的感兴趣特征向量哦),所以感兴趣流形(我觉得就是好的、有效的特征)应该在一个高维激活空间中的一个低维子空间中(对,记得站在 Relu 的右边线性激活区)。

所以,我们可以通过加入线性瓶颈层(linear bottleneck)来拿到低维的感兴趣流形。也有类似的 report,就是在输入中拿掉 Relu 可以提升性能(因为在靠前的层中使用 Relu 会损失信息)。

后文中就会使用这个瓶颈层,我们把中间层的深度和输入层的深度比例叫 expansion ratio。(就是升维那里到底升维到什么程度)

Inverted residuals

这里主要就是这个 Inverted 到底在 Inverted 什么?这里其实应该对比着 ResNet 来看。论文有专门附一张图来说明,图上每个步骤的厚度是有实际说明意义的。上一部分不是假定好了我们可以拿到低维的感兴趣流形了嘛,然后升维,提取特征,再降维回来。因为都假定好了低维输入就已经包括所有信息了,所以就直接连的两个 bottleneck,这样就算 “Inverted”。
在这里插入图片描述

Information flow interpretation

论文中提出的新结构,有一个很有趣的特性,就是自然地将 输入输出域(bottleneck layers)从输入变换到输出的过程 分开了。前者可以看作是网络在每层的 capacity,后者可以看做是 expressiveness。这和传统卷积是不一样的,传统卷积中这两者是在一起的,而且还受输出通道数的影响。

特别地,当中间层深度变为 0 也就是不存在的时候,正好就是那条 shortcut。当 expansion ratio 小于 1 的时候,这个模块就变回了原本的残差模块。

综上,这个设计允许我们能将学习网络的表达能力从输入的 capacity 中独立出来。

Model Architecture

基础模块就是上面说的 bottleneck depth-separable convolution with residuals。这个模块的结构和参数如下。
在这里插入图片描述

  • h w 就不说了;
  • k 输入通道, k’ 输出通道;
  • s 是卷积 stride,会影响 h w 平面的尺寸;
  • t 是扩张率,就是低维升维到高维那个扩张率,是深度方向上的;
  • dwise 就是 MobileNet V1 中的单通道卷积, Relu6
  • 1 × 1 conv2d 就是 MobileNet V1 中的通道方向上的 1 × 1 卷积,Relu6
  • linear 1 × 1 conv 2d 就是前文一直说的去掉 Relu 只用线性变换(前面两个激活都是非线性 Relu6 哦)。

那么整个 MobileNet V2 的结果是下面这样:
在这里插入图片描述

  • 最前面就是传统卷积;
  • 后面 bottleneck 就是刚刚说的模块;
  • c 是当前层的输出通道,n 是当前层堆叠几次,s 是层的第一个(即使堆叠了也是第一个)的 stride,会影响每层 input 的 h w 面的尺寸。
  • t 是扩张率,论文推荐用的 6。比如对于某层来讲,输入是 64 通道,输出要 128 通道,那么中间升维要到 64 × 6 = 384 通道。

Implementation Notes

本来这部分一开始没觉得很吸睛了,看了一下还是很承上也很精彩的(这个论文感觉说得都很清楚,有理论嘻嘻嘻),搜了搜也没看到多少将这小部分的,有也是翻译或者粘贴,所以更想试着去理解,感觉自己好像看懂了(只能懂一 dia dia)。

Memory efficient inference

其实这部分对于这篇论文来讲很重要的。为什么?因为 MobileNet 姓 Mobile 呀。这部分的论述,不仅给出了理论说明高效使用内存进行推断,更是它前面种种理论和网络结构设计的最好体现(虽然后面还有更具体的特定任务实现)。因为前面的深度分离卷积、倒置残差模块等等的存在,使得移动端运行的时候可以达成这么个样子的“高效”(跟传统 CNN 比)。

下面这段灰色还有想不清楚的地方。
Tensorflow 和 caffe 会创建一个有向无环图 G,图中有很多节点,用边表示 operations 节点表示 tensors(感觉跟自己之前看的计算图有点相反)。在计算中,会最小化需要存储在内存中的 tensors 的总数量。一般情况下,会搜寻图 G 中所有可能的计算顺序,然后选择一个能使下面式子最小的:

M ( G ) = min ⁡ π ∈ Σ ( G ) max ⁡ i ∈ 1... n [ Σ A ∈ R ( i , π , G ) ∣ A ∣ ] + s i z e ( π i ) M(G) = \min_{\pi \in \Sigma(G)}\max_{i \in 1...n}[\Sigma_{A\in R(i, \pi, G)}|A|] + size(\pi_i) M(G)=minπΣ(G)maxi1...n[ΣAR(i,π,G)A]+size(πi)

有向无环图的特性就如其名字而言,然后结合神经网络中的计算图,网络一旦建成,框架生成了计算图,那么就可以认为图的信息是固定的(先不要说动态图……),有多少节点多少边互相的关联都是已经定好的。节点通过边之间有一些依赖关系,A 节点连接到 B 节点的话(有方向哦),那 B 就注定了在 A 后面。但是比如 A 后面同时并列有 B 和 C,那么可选的计算可以先 AB 然后再 AC 或者相反,这就会产生同一张图的计算会有不同的顺序。

我反复看感觉这个式子的细节是有问题的(有些困惑)。

- M ( G ) M(G) M(G),等式左边没有直接给意义(抱歉我太菜了我觉得应该写了更好= =),根据上下文推断的应该是图 G 所需要的 mermory 记为 M ( G ) M(G) M(G),本来这段就是在最小化需要的内存嘛。

- min ⁡ π ∈ Σ ( G ) \min_{\pi \in \Sigma(G)} minπΣ(G) Σ ( G ) \Sigma(G) Σ(G) 是图 G 的所有可能的计算顺序 all plausible computation orders , π \pi π 在各种图/树算法中经常用来表示路径,在这儿就是前面的 computation order。这一项就是要在所有可能的可以计算出 G 的计算顺序中,挑选能让整个过程中占用内存最小的那个计算顺序。

- max ⁡ i ∈ 1... n \max_{i \in 1...n} maxi1...n,这里 1 到 n 对应的 node 也就是 tensor,我自己倾向于理解成路径/ computation order π \pi π 上有 n 个节点, π i \pi_i πi 是其中某个节点。

- R ( i , π , G ) R(i, \pi, G) R(i,π,G),连接到节点 π i \pi_i πi π n \pi_n πn 中任意一个节点的所有 tensor 的列表。 A 是列表中的某个 tensor。只有 i 之前的节点才有可能连接到 i 到 n 节点(有向)。

- s i z e ( π i ) size(\pi_i) size(πi) 然后我就很迷惑了o(╯□╰)o。(1)按照下一个公式的写作风格,论文在说明 max min 作用域时是加了中括号 [ ] 的,那我会认为中括号范围内的,才是 min 或者 max 的作用域。i 是在 max 部分时的下标,也没有在其他地方提起,突然就出现最后的 s i z e ( π i ) size(\pi_i) size(πi) 中了。(2)那我顺着前后文理解, π i \pi_i πi 还是节点呢吧,但是文字论述方面直接说是 s i z e ( i ) size(i) size(i) 而且说是 operation i 需要的内存。再无其他信息可以互相关联了呀,我一下子呆了啊o(╯□╰)o这一点一直没解决嘞。

上面说了半天还是普通有向无环图,那么对于朴素/简单一点的只有并行结构的有向无环图就会更简单一些。变成了:

M ( G ) = max ⁡ o p ∈ G [   Σ A ∈ o p i n p ∣ A ∣ + Σ B ∈ o p o u t ∣ B ∣ + ∣ o p ∣   ] M(G) = \max_{op \in G} [\ \Sigma_{A \in op_{inp}} |A| + \Sigma_{B \in op_{out}} |B| + |op|\ ] M(G)=maxopG[ ΣAopinpA+ΣBopoutB+op ]

emmm 这一下确实是简单了很多,好像直接看公式就能看出想表达的意思来(可是我看不出来和上面那个一般情况之间的关联了啊,“计量单位”直接是 op 了,前面还是 node 呢……)

就是直接把整个计算图拆解成一个个 op 来看待了,有的 op 需要的内存多,有的需要的少,总归所有活儿都要干的,那么运行整个图需要的最小内存就是满足图中消耗内存最多那个 op 的计算就可以了。

那么某个 op 有 输入 + 输出 + 计算过程的,计算过程嘛可以有 trick 的,比如当限定内存很小但是时间开销还在允许范围内的话,可以随算随用随丢嘛(然后别忘了 MobileNet 里面深度分离卷积都是单通道的,可以一个一个通道的随算随用随丢)。

如果我们把 V2 中的倒置残差模块当成一个 op 的话,里面的计算过程中的卷积操作(不是传统卷积哦)是可以任意处置的(就是想扔就扔,想用再算呗),需要的内存就仅取决于 bottleneck 的 tensor(就是输入输出啦),而不是内部的(这个 tensor 更大的,V2 就是先升维再降维嘛)。

Bottleneck Residual Block

好了,终于说到 V2 中的倒置残差计算了。为了方便看,再贴一遍图好了。
在这里插入图片描述
这个图中从左到右暂时叫 block 0123,将整个过程看作一个 op 叫 F ( x ) = [ A ∘ N ∘ B ] x F(x) = [A \circ N \circ B]x F(x)=[ANB]x,其中

  • A 和 B 都是线性变换,一个是升维图中 block 0 - 1, R s × s × k − > R s × s × n R^{s \times s\times k} ->R^{s \times s\times n} Rs×s×k>Rs×s×n,一个是降维 block 2 - 3, R s ′ × s ′ × k − > R s ′ × s ′ × n R^{s' \times s'\times k} ->R^{s' \times s'\times n} Rs×s×k>Rs×s×n
  • N 是作用在单个通道上的非线性变换,block 1 - 2, R s × s × n − > R s ′ × s ′ × n R^{s \times s\times n} ->R^{s' \times s'\times n} Rs×s×n>Rs×s×n

在 V2 中, N = R e l u 6 ∘ d w i s e ∘ R e l u 6 N = Relu6 \circ dwise \circ Relu6 N=Relu6dwiseRelu6 (单通道上哦)。所以, F ( x ) F(x) F(x) 需要的内存是 ∣ s 2 k ∣ + ∣ s ′ 2 k ′ ∣ + O ( max ⁡ ( s 2 , s ′ 2 ) ) |s^2k| + |s'^2k'| + O(\max(s^2, s'^2)) s2k+s2k+O(max(s2,s2))

还没完,算法还假定 op 内部的 tensor 是可分的,也就是切成一份份地来算,这样 op 该干的活儿都干了,内存中却一直保留一份就可以。

使用上面这个 trick 来减少计算图需要的内存基于两个限定条件:

  • 内部的变换是基于单通道的,所以可以一个通道一个通道地算。
  • 而对于非单通道计算 op(应该是 1 × 1 卷积升维降维那里吧),输入输出之间比例很显著。(其实没太懂显著就如何?)

而普通卷积是没有上面这两个限定条件,所以也不能应用前面那些减小内存的 trick,用了也没什么效果。

Experiments

ImageNet Classification

Object Detection

论文把 MobileNet V1 V2 当成目标检测工作中的特征提取器,对 SSD 进行了改进,将所有的传统卷积操作都替换成深度可分离卷积(但是仅有这个那只是 V1 吧)。

对于 MobileNet V1,设置如论文 《Speed/accuracy trade-offs for modern convolutional object detectors》。

对于 MobileNet V2,SSDLite 的第一层被附加到层15的扩展(输出步长为 16),SSDLite层的第二层和其余层连接在最后一层的顶部(输出步长为32)。此设置与MobileNetV1 一致,因为所有层都附加到相同输出步长的特征图上。

结果嘛,和 YoloV2 相比,参数少 10 倍,计算量少 20倍,表现还高了半个点~,如下。
在这里插入图片描述

Semantic Segmentation

Ablation study

Conclusions and future work

On the theoretical side: the proposed convolutional block has a unique property that allows to separate the network expressiveness (encoded by expansion layers) from its capacity (encoded by bottleneck inputs). Exploring this is an important direction for future research.

撒花~

  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值