作者简介

CW,广东深圳人,毕业于中山大学(SYSU)数据科学与计算机学院,毕业后就业于腾讯计算机系统有限公司技术工程与事业群(TEG)从事Devops工作,期间在AI LAB实习过,实操过道路交通元素与医疗病例图像分割、视频实时人脸检测与表情识别、OCR等项目。

目前也有在一些自媒体平台上参与外包项目的研发工作,项目专注于CV领域(传统图像处理与深度学习方向均有)。

前言

轻量化网络是深度学习领域的研究热点之一,其目标是在网络的参数量少、训练和预测速度快的情况下又能够保持一定的准确率,以下是一些常用的减少网络计算量的方法:

1. 网络模型设计:使用组卷积(Group Conv)、1x1 卷积(Ponit-Wise Conv)等在达到相同(或近似)效果的同时减少计算量,类似的案例如 Inception、Xception、ShuffleNet 以及本文的主角MobileNet等;

2. 剪枝:通过剪去网络中的冗余部分来减少网络计算量;

3. 知识蒸馏:利用大模型(Teacher-Model)来辅助小模型(Student-Model)学习,从而在小模型上达到目标效果。

MobileNet是Google的作品,能够将模型部署应用于移动端,同时又保证良好的效果,目前Google Pixel 系列的手机就是用的MobileNet。MobileNet目前更新到了v3版本,CW参考和收集了网上的资料及博文,在本文中会从v1到v3都进行解读,供大家参考学习。

如有不当之处,尽管反馈,大家一起学习与进步,欢迎交流,谢谢!

MobileNetv1

v1的主要工作是将标准的卷积过程拆为两个步骤 —— Depth-Wise Conv 和 Point-Wise Conv,并且引入2个超参,减少了计算量并且模型也更小,从而可以轻松地匹配移动和嵌入式视觉应用的设计要求。

1. 对标准卷积层的改进 —— Depth-Wise Conv 和 Point-Wise Conv

Depth-Wise Conv(简称dw),也称深度可分离卷积,它将卷积核的每个通道分别应用于输入特征图的每个通道(这样,输入和输出的通道数维持一致),而非如标准卷积般将输入特征图的所有通道加权组合在一起。

Point-Wise Conv(简称pw), 也称逐点卷积,它使用1x1大小的卷积核,用于组合深度可分离卷积的输出结果(将深度可分离卷积的输出通道数映射到目标通道数)。

标准卷积将提取空间特征和通道之间的相关性组合在一起,而MobileNet将其分解为Depth-Wise + Point-Wise,这样的效果是先基于各个通道提取空间特征(空间相关性),之后再组合通道之间的相关性,这种因式分解操作具有大幅度减少计算和模型大小的效果。

标准卷积=dw+pw卷积

为何说这样能够减少计算量?根据上图,举个例子动手计算下才有说服力(下文均使用 channel first 惯例,所谓的计算量指乘法次数):

(a) 表示标准卷积的过程:有N个卷积核,每个卷积核维度是M x

x

,输入特征图的通道数是M,输出特征图维度为N x

x

,计算量为

(b) 表示dw的过程:用M个维度为1 x

x

的卷积核去卷积对应输入特征图的M个通道,得到M个输出结果,注意这M个结果不会进行相加(相比标准卷积是卷积输入特征图的所有通道,并累加这M个结果),输出结果的维度是

,这时计算量为

(c) 表示pw的过程:用N个维度为M*1*1的卷积核卷积上述(b)的输出结果,最终得到维度为

的 feature map。这个过程和普通卷积无异,只是卷积核大小为1 x 1,此时计算量是

(b) + (c) 总共的计算量是

,我们拿它除以标准卷积 (a)的计算量,来看看缩减了多少:

也就是说,如果使用将我们最常使用的3x3标准卷积分解为dw+pw,那么计算量将缩减为原来的1/9有多!

根据paper原文的意思,有些点还需要提下:MobileNet在每一层均使用BN和ReLU两种非线性变换;

与标准卷积相比,dw非常有效。然而,它只会滤除输入通道,不会将它们组合起来以创建新功能。因此,需要通过pw来计算深度卷积输出的线性组合的附加层来生成这些新特征。

标准卷积层 vs MobileNet的dw+pw卷积层

2. 使用2个超参数构建更小的模型

(1) 宽度乘数(mulitiplier)

记宽度乘数为α,其作用是在每层均匀地减薄网络。对于给定的层和宽度乘数α,输入通道M的数量变为αM,输出通道数量N变为αN。

再拿上述例子来说,使用了宽度乘数后,dw+pw的计算量为:

,通常

设置为0.75。

(2) 分辨率乘数

这个超参通常隐式设置,应用于输入图像,以降低分辨率,记其为

同样地,拿上述例子来说,在使用了宽度乘数的基础上,进一步使用分辨率乘数后,dw+pw的计算量为:

最后来看下MobileNetv1的整体架构,总共28个卷积层(conv+bn+relu),第1个和最后1个都是标准卷积层,中间13个是dw+pw卷积层(dw+pw算作1个卷积层)

MobileNetv1架构和计算量

MobileNetv1各参数设置对比实验结果

附:MobileNetv1 paper (文末可领取原论文)

MobileNetv2

MobileNetv1的dw+pw卷积层

回顾下MobileNetv1的骚操作——首先利用3×3的深度可分离卷积提取空间特征,然后利用1×1的逐点卷积来组合通道特征,同时映射到目标通道数。这样既减少了参数量、计算量,同时又提高了网络运算速度,还能得到一个接近于标准卷积的不错的结果,看起来十分美好。

然而,现实是残酷的!

不少炼丹者在实际使用过程中, 发现深度卷积部分的卷积核被废掉了——训练完之后发现深度卷积核有不少是空的!

“废掉”的dw

于是,作者赶紧再发一篇paper(心想:本来想坑你们一把,没想到这么快就被识破了,不好玩 !),把锅甩给了ReLU(ReLU内心独白:我太难了!),同时,MobileNetv2也诞生了(此刻脑补众炼丹者们内心:也不知道是不是新挖的坑...)。

在MobileNetv2的paper中,作者举出一个充分必要条件将锅“合乎所以然”地甩给了ReLU——将低维流形映射到高维空间后使用ReLU变换再复原的例子:

低维流形在不同维度下的映射与复原对比

如上图,在2维空间有一组由m个点组成的螺旋线Xm数据(上图input),利用随机矩阵T映射到n维空间上并进行ReLU运算:

矩阵映射+ReLU的“拥抱”

这时,再利用随机矩阵T的逆矩阵

,将y映射回2维空间中:

逆映射

最后,根据映射到不同维度空间的情况,对比下逆映射后的结果与原始输入:

可以看到,当映射到2、3维空间(n = 2, 3)时,与原始输入input相比有很大一部分的信息已经丢失了;而映射到15、30维空间(n = 15, 30)时,还有许多信息能被保留下来。

这就是说,在低维度空间下与ReLU拥抱(做ReLU运算),很容易造成信息丢失;而在高维度的情况下,信息丢失则相对没那么严重。

这就解释了为什么dw的卷积核有不少是空,因为dw本身不会改变通道数,如果输入通道本来就少的话,经过ReLU后信息丢失就会比较严重。

咦...说了那么久,怎么好像都没有提到MobileNetv2呀?

哦,不不不,以上铺垫的这个问题很重要,因为针对这个问题的解决手段正是MobileNetv2最重要的骚操作,好了,接下来进入正题。

1. Expansion Layer

对于深度可分离卷积,其本身没有改变通道的能力,输入通道多少输出就是多少。有上述可知,这样的话,若输入通道很少,经过深度可分离卷积后再接ReLU,效果便不是很好,那怎么办呢?

我们可以在深度可分离卷积前先扩展通道数,也称作Expansion Layer(扩展层)。OK,那具体如何扩展呢?

既然在深度可分离卷积后我们使用了逐点卷积来将feature map的通道数映射到目标通道数,那么同样地,我们也可以在深度可分离卷积之前先使用逐点卷积进行升维(paper原文是升6倍)。

先使用逐点卷积升维,接着进行深度可分离卷积,最后再使用逐点卷积降维

2. Linear Bottleneck

相对于上述方式,这是一个比较“暴力”的手段,干脆把ReLU换掉,使用线性激活层替代。不过,我们可不能把所有的激活函数都换成线性的,这样整张网络就相当于一个线性操作了(别忘记使用激活函数的一个原因之一就是引入非线性变换),那么应该替换掉哪里的ReLU呢?

仔细看上图,第1个pw已将通道数扩展,中间的pw不会改变通道数,那么这两层后面使用ReLU通常较为“安全”,而最后1个pw通常是压缩通道,如果后面接ReLU,效果可能就相对没那么好了,因此可以将最后1个pw后的ReLU替换为线性激活函数,这样的结构也称作Linear Bottleneck。

3. Inverted Residual

还记得ResNet那货不?哦,这肯定是废话了,对于众炼丹者来说,它也算得上是个全明星了。

残差结构(通常也称作shortcut)能够其起到特征复用和避免梯度消失的作用,于是在MobileNetv2里也引入这一骚操作。

ResNet's residual vs MobileNetv2's Inverted-Residual

根据上图,对比下可以发现,两者都采用了 1×1 -> 3 ×3 -> 1 × 1 的卷积模式以及shortcut,不同点在于ResNet先使用pw降维,后使用pw升维,而MobileNetV2“反其道而行之”——先使用pw升维,后使用pw降维。

这样一来,MobileNetv2的block形状看起来就刚好与ResNet的block相反,因此作者给了它一个网红名字——Inverted-Residual。

综合以上,我们来看下MobileNetv2的block:

MobileNetv2 block

再来对比下v1和v2的block:

MobileNetv1 vs MobileNetv2

上图左边是v1的block,没有shortcut并且最后的pw后面接了ReLU6;

右边的是v2的block,加入了Expansion Layer(使用1×1 pw 升维),引入shortcut,同时去掉了最后pw后的ReLU,改为线性激活函数。注意,v2的block有2种,当dw的步长(stride)为1时,使用了shortcut,形成残差结构;而dw的步长(stride)为2时,由于input与output的尺寸不同,因此没有使用shortcut结构。

最后,附上MobileNetv2的网络结构:

MobileNetv2 结构

附:MobileNetv2 paper(文末可领取原论文)

MobileNetv3

本以为v2能成为网红,没想到一下子被v3干掉了...好吧,我们先来回顾下v1和v2都做了哪些骚操作。

v1将标准卷积分解为dw+pw,同时使用2个超参进一步构建小模型和降低计算量;

v2在v1基础上,在dw前加入 Expansion Layer 进行通道数的扩展,将dw后的pw接的ReLU6替换为线性激活层,并且引入了shortcut,最终形成 inverted-residual(“倒残差”) 这样的结构。

简单精炼,OK!那么v3在以上的基础上又有哪些骚操作呢?

1. 新的非线性激活函数 —— h-swish

在介绍h-swish先来认识一个家伙——swish,它一次是在谷歌大脑2017的论文 Searching for Activation functions 种亮相,其具备无上界有下界、平滑以及非单调的特性,并且在深层模型上的效果优于ReLU,并且作者通过实验进行了验证。

这好东西v3当然也尝试使用过,但是作者又认为这家伙虽然提高了精度,但在嵌入式环境中,还是有不少计算成本,原因是在移动设备上计算sigmoid 函数成本不小,因此把sigmoid这部分替换掉,改为ReLU6,并且将这整个激活函数命名为h-swish:

为啥叫h-siwsh?h代表什么呢?h其实是单词hard的第一个字母,h-swish的意思就是这个近似的函数可以逼近swish,让swish硬(hard)起来(别想歪呀...)。那为啥使用ReLU6呢?作者认为几乎所有的软硬件框架上都可以方便计算ReLU6,同时它能在特定模式下消除由于近似sigmoid的不同实现而带来的潜在数值精度损失。

sigmoid的“软”、“硬”模式以及swish和h-swish

下图展示了使用h-swish对于时间以及精度的影响,可以发现,使用h-swish@16可以提高大约0.2%的精度,但是持剑延长了大约20%。因此,总的来说,在更深的层中使用h-swish好处更大,于是建议通常在模型的后半部分使用。

h-swish对于时间以及精度的影响

2. 基于Squeeze & Excitation(SE)的轻量级注意力结构

在bottleneck中加入了SE结构,并且放在了dw之后。另外,由于SE部分的计算会消耗一定的时间,因此在SE结构的第1个FC层中,压缩了通道数(paper原文是变为原来的1/4),在最后的FC层再复原,通过实验证明,这样在减少时耗的同时也提高了精度,这是属于MobileNet的轻量级SE结构。

MobileNetv3中的轻量级SE结构

关于这里的SE,有几点提醒下:

(1). 这里的Pool指的是GAP(Global Average Pooling),即全局平均池化,经过它的“沐浴”后,feature map的大小变为1 x 1,通道数维持不变;

(2). 第1个FC后的激活函数是ReLU,最后1个FC后的激活函数是h-sigmoid(注意不是 h-swish,相比 h-swish 少乘了1个 x,即

3. 修改了部分v2的结构 —— 头部卷积核的通道数 和 尾部结构

(1). 修改头部卷积核的通道数

v2中的头部卷积核是 32 x 3 x 3,v3中改成了 16 x 3 x 3,作者发现这样在保证了精度的前提下还降低了3ms的时耗。

(2). 修改尾部结构

v2中,在最后的 Avg-Pooling 之前,使用了1个pw来升维,有利于结构的预测,但这同时引入了一定的计算量,于是作者在v3中作了修改:

先去掉这个 pw 前的 bottleneck 中的 dw 和后面的 pw,然后将原本位于 Avg-Pooling 前的 pw 放置于 Avg-Pooling 后,这样就变为先用 Avg-Pooling 将feature map大小由 7 x 7 降到了 1 x 1(Avg-Pooling 的 kernel size 是 7 x 7),然后再使用 pw 升维度,减少了计算量和时耗,同时发现精度并没有得到损失,最终变为如下所示的结构:

MobileNetv3尾部结构

相比于v2:

MobileNetv2尾部结构

4. 网络结构搜索 —— 资源受限的NAS(Platform-Aware NAS)与 NetAdapt

通过上述,可以发现v3相比于v2并没有做太多创新,其实重头之戏在于这部分。但是CW对这部分没有深入了解,参考了一些博文,在这里简单介绍一下。

总体过程就是先用资源受限的NAS,在计算和参数量受限的前提下搜索网络来优化各个块(block),称之为模块级搜索(Block-wise Search)。然后再使用 NetAdapt 算法对各个模块确定每一层的卷积核数量,称之为层级搜索(Layer-wise Search)。

MobileNet为何这么快?

看到这里,你有没有认真想过其实为何MobileNet会这么快呢?它是闪电侠吗,sorry跑题...

或许你会说因为它将标准卷积分解为dw+pw、使用了宽度乘数和分辨率乘数缩减计算量、使用了h-swish...blablabla等等,嗯,也没错,但是这里可以尝试从另一个视角去理解。

以v1为例,来看看计算资源分布情况:

MobileNetv1 计算资源统计

可以看到,大部分计算都分布在了pw也就是1 x 1卷积上,这么看来,快就快在1 × 1卷积了。

我们知道,卷积实质是矩阵之间的加乘运算,而在计算机进行操作时,需要将其先存入内存再操作,按照 “行先序”:

卷积运算与计算机“行先序”

再来一张图,你会发现这是有点“辛苦”的:

卷积在内存中的运算,实线标注出卷积计算中的访存过程(对应数据相乘)

于是,通常都会使用到一个叫im2col的骚操作,其实质是通过牺牲空间的手段(约扩增K×K倍),将特征图转换成更大的矩阵来进行卷积计算,具体为:

把在特征图中每一次循环所需的数据排列成列向量,然后逐一堆叠起来形成矩阵(按通道顺序在列方向上拼接),比如输入特征图维度为 Ci × Wi × Hi ,卷积核尺寸为 K × K ,输出维度为Co×Wo×Ho,那么卷积核转换为 Co x ( K x K) 的矩阵,同时输入特征图转换为(K x K) × (Ci x Wo x Ho)的矩阵,如下所示。

im2col“骚操作”

然后调用诸如 GEMM(矩阵乘矩阵)这样的库加速两矩阵的相乘计算。由于这过程按照计算需求排布了数据顺序,使得计算机每次计算过程中能够依次访问到特征图中所需的数据,因此极大地提高了运算速度。

im2col后GEMM

OK,现在回来看看MobileNet的宝贝1×1卷积,根据上述,1×1卷积的“行先序”储存结构以及使用im2col后的结构分别如下图所示:

1x1卷积在计算机内存中使用“行先序”的存储结构以及使用im2col骚操作后的存储结构

咦,这是...一样哒!也就是说,1x1卷积根本不需要im2col的过程,“即插即用”!大大节省了数据重排列的时间和空间,因此在底层计算中相对更快。

附:MobileNetv3 paper(文末可领取原论文)

若你认真看完了本文,那么CW真心感谢你!与此同时,如果你觉得本文给予了你帮助或者让你有了新的认知,那我就更开心了,CW期待与大家共同成长!

参考

https://cloud.tencent.com/developer/article/1467101

https://blog.csdn.net/u010712012/article/details/95922901

https://blog.csdn.net/qq_38807688/article/details/84590717

https://blog.csdn.net/Chunfengyanyulove/article/details/91358187

Hi,我们是-MobileNet-家族相关推荐

  1. 【读点论文】EfficientFormer: Vision Transformers at MobileNet Speed,运用纯transformer架构对比卷积模型在终端上部署的推理速度

    EfficientFormer: Vision Transformers at MobileNet Speed Abstract 视觉transformer(ViT)在计算机视觉任务中取得了快速的进展 ...

  2. 超越ShuffleNet、MobileNet、MobileViT等模型的新backbone FasterNet

    论文地址:https://arxiv.org/abs/2303.03667 摘要: 为了设计快速神经网络,许多工作都集中在减少浮点运算(FLOPs)的数量上.然而,作者观察到FLOPs的这种减少不一定 ...

  3. inception家族的发展史

    在讲之前说明一下:每一个网络都只会讲解它的主要亮点,可能某个网络的某一个小部分不会讲到,但是讲到的部分都会讲得很细.(讲的知识面不是很宽,但是讲到的部分都会讲得很细.) inceptionv1 (20 ...

  4. Clang:LLVM 的 C 语言家族前端

    Clang:LLVM 的 C 语言家族前端 Clang 项目为LLVM 项目的 C 语言家族(C.C++.Objective C/C++.OpenCL.CUDA 和 RenderScript)中,提供 ...

  5. Clang:LLVM的C语言家族前端

    Clang:LLVM的C语言家族前端 Clang项目为LLVM 项目的C语言家族(C,C ++,Objective C / C ++,OpenCL,CUDA和RenderScript)中的语言提供了语 ...

  6. 一篇文章让你读懂Pivotal的GemFire家族产品

    一篇文章让你读懂Pivotal的GemFire家族产品 学习了:https://www.sohu.com/a/217157517_747818 转载于:https://www.cnblogs.com/ ...

  7. 基于FPGA实现的MobileNet V1,FPGA深度学习加速器设计 CNN Accelerators based on FPGAs

    Automatic Generation of Multi-precision Multi-arithmetic CNN Accelerators for FPGAs 最近arXiv上挂出来一篇文章, ...

  8. 面向Mobile device的CNN模型手工设计与NAS分析总结,MobileNet V1,V2,V3,Efficient,MNasNet以及Efficient network design

    手工方法和NAS的高效网络模型设计总结与分析 这篇文章主要关注对于移动端,资源受限平台的高效神经网络设计(Manually)和搜索(NAS). ​​​​​​高效的CNN设计不只是用在服务器,云端,资源 ...

  9. CV算法复现(分类算法6/6):MobileNet(2017年V1,2018年V2,2019年V3,谷歌)

    致谢:霹雳吧啦Wz:霹雳吧啦Wz的个人空间_哔哩哔哩_Bilibili 目录 致谢:霹雳吧啦Wz:霹雳吧啦Wz的个人空间_哔哩哔哩_Bilibili 1 本次要点 1.1 pytorch框架语法 2 ...

  10. 为什么MobileNet的参数这么少

    我是用的MobileNet v2版本,这篇文章不是什么高明的文章,实际上其他很多地方都有介绍,本人只是验证一下,作为备忘 像VGGNet这样的神经网络,因为全部都是用的Conv2D,所以参数量较大,随 ...

最新文章

  1. python模块学习---cmd
  2. ajax传输是用utf8,使用ajax时UTF8编码不起作用
  3. php5.3中ZendGuardLoader与wincache冲突问题的解决方法
  4. 在VMware虚拟机中进行Ghost网刻不成功的几点提示
  5. 总结Android开发中必备的代码Review清单
  6. sscanf 与 sscanf_s的区别
  7. ASP.NET 2.0 Provider Toolkit
  8. 2010中国十大杰出IT博客大赛—唯有行动才能改造命运
  9. logistic回归__基于Python和Numpy函数库
  10. android 学习之SurfaceView
  11. 吴恩达机器学习课程笔记
  12. Python | 范德蒙矩阵
  13. 多媒体计算机相关知识,多媒体基础知识题库
  14. 你能为公司带来什么?
  15. 3G iPhone “白苹果”历险记
  16. 微信支付-支付结果通知接收
  17. 在SAP系统中,更改一个公司代码(Company Code)的会计科目表(Chart of Accounts)
  18. Sed 流文本编辑器
  19. TIA博途中OB86组织块的功能和使用方法
  20. 干货分享!杭州知名SEO公司清法网络告诉你如何玩转小红书

热门文章

  1. access订单明细表怎么做_图书销售订单明细表 (4)
  2. 软件无法连接oracle数据库,全面解析Oracle无法连接本地数据库问题
  3. 学生渐进片add如何给_【收藏】为青少年验配渐进多焦点时,如何选择合适ADD?...
  4. mysql nn_mysql workbench建表时PK,NN,UQ,BIN,UN,ZF,AI含义说明
  5. python是一种面相什么语言_Python面相对象编程的知识总结
  6. ajax json 封装,Ajax--json(Ajax调用返回json封装代码、格式及注意事项)
  7. linux的多任务 多进程,浅谈linux模拟多线程崩溃和多进程崩溃
  8. JavaScript的引入方式
  9. leetcode 75
  10. git 删除本地仓库