MobileNetV1网络是谷歌团队在2017年提出的,专注于移动端和嵌入设备的轻量级CNN网络,相比于传统的神经网络,在准确率小幅度降低的前提下大大减少模型的参数与运算量。相比于VGG16准确率减少0.9%,但模型的参数只有VGG1/32。
其实简单来说,就是把VGG中的标准卷积层换成深度可分离卷积。不过这个深度可分离卷积刚开始接触比较抽象难理解,建议大家看看文末链接里大佬的讲解视频噢!也欢迎大家在评论区讨论 (ฅ´ω`ฅ)~
学习资料:
- 论文题目:《MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications》(《MobileNets:用于移动视觉应用的高效卷积神经网络》)
- **原文地址:**https://arxiv.org/abs/1704.04861
- **项目地址:**https://github.com/Zehaos/MobileNet
我们提出了一类有效的模型称为移动和嵌入式视觉应用的移动网络。MobileNets是基于流线型架构,使用深度可分卷积来建立轻量级深度神经网络。我们介绍了两个简单的全局超参数,它们可以有效地在延迟和准确性之间进行权衡。这些超参数允许模型构建者根据问题的约束为其应用程序选择适当大小的模型。我们在资源和精度权衡方面进行了大量的实验,并与其他流行的ImageNet分类模型相比,显示了较强的性能。然后,我们演示了MobileNets在广泛的应用和用例中的有效性,包括目标检测、细粒度分类、人脸属性和大规模地理定位。
本文主要工作
(1)提出了一个可在移动端应用的高效网络MobileNets,其使用深度可分类卷积使网络轻量化同时保证精度。
(2)设计了两个控制网络大小全局超参数,通过这两个超参数来进行速度 (时间延迟)和准确率的权衡,使用者可以根据设备的限制调整网络。
(3)在ImageNet分类集上与其他模型进行了广泛对比,验证了MobileNets的有效性。
自从AlexNet通过赢得ImageNet挑战赛:ILSVRC 2012普及了深度卷积神经网络以来,卷积神经网络在计算机视觉中变得无处不在。为了获得更高的精度,一般的趋势是构建更深更复杂的网络。然而,这些提高准确性的进步并不一定使网络在规模和速度方面更有效率。在机器人、自动驾驶汽车和增强现实等许多现实应用中,识别任务需要在一个计算能力有限的平台上及时执行。
本文描述了一种高效的网络架构和两个超参数,以便构建非常小的、低延迟的模型,可以很容易地满足移动和嵌入式视觉应用程序的设计要求。第2节回顾了以前在构建小模型方面的工作。第3节描述了MobileNet架构和两个超参数宽度乘法器和分辨率乘法器,以定义更小和更有效的MobileNet。第4节描述了在ImageNet上的实验以及各种不同的应用程序和用例。第5节以总结和结论结束。
文章这一部分主要是简要介绍了一下卷积神经网络的发展和应用,然后引出自己提出的MobileNets网络,指出其在移动端应用方面的有效性,最后介绍本篇论文的写作框架。
在最近的文献中,人们对构建小型而高效的神经网络的兴趣越来越大。许多不同的方法可以大致分为压缩预训练网络和直接训练小型网络。本文提出了一类网络体系结构,它允许模型开发人员为其应用程序选择与资源限制(延迟、大小)匹配的小型网络。MobileNets主要关注于优化延迟,但也产生小的网络。许多关于小型网络的论文只关注规模而不考虑速度。mobilenet最初是由[26]中引入的深度可分卷积构建的,随后在Inception模型[13]中使用,以减少前几层的计算。扁平网络[16]构建了一个完全因数分解卷积的网络,并展示了高度因数分解网络的潜力。与本文无关,分解网络[34]引入了一个类似的分解卷积以及拓扑连接的使用。随后,Xception网络[3]演示了如何向上扩展深度可分离的过滤器,以执行Inception V3网络。另一个小型网络是Squeezenet[12],它使用瓶颈方法来设计一个非常小的网络。其他简化计算网络包括结构变换网络[28]和油炸卷积网络[37]。
另一种获得小型网络的方法是缩小、分解或压缩预先训练好的网络。文献中提出了基于产品量化[36]、哈希[2]、剪枝、矢量量化和霍夫曼编码[5]的压缩方法。此外,还提出了各种因数分解方法来加速预先训练好的网络[14,20]。另一种训练小网络的方法是蒸馏[9],它使用一个更大的网络来教一个更小的网络。它是对我们的方法的补充,并在第4节中介绍了我们的一些用例。另一种新兴的方法是低比特网络[4,22,11]。
以前的方法
相关轻量化网络
建立小型高效的神经网络两种方法
(1)压缩预训练模型:
(2)蒸馏:
使用大型网络指导小型网络
标准的卷积层将作为输入DF×DF×M功能映射F和产生一个DF×DF×N特性图G, DF是输入特征图的高度和宽度,M是输入通道的数量(输入深度),DG是一个正方形的空间宽度和高度输出特性图和N是输出通道输出(深度)的数量。标准卷积层由大小为DK×DK×M×N的卷积核K参数化,其中DK为假设为平方的核的空间维数,M为输入通道数,N为前面定义的输出通道数。假设stride为1,padding为1,计算标准卷积的输出特征图为:
标准卷积的计算成本为:
其中,计算代价乘上输入通道M的数量,输出通道N的数量,核大小Dk×Dk, feature map大小DF×DF。MobileNet模型解决了这些术语及其相互作用。首先,它使用深度可分卷积来打破输出通道数量和内核大小之间的交互。标准卷积运算的作用是基于卷积核对特征进行滤波,并结合特征得到新的表示。滤波和组合步骤可分为两个步骤,通过使用分解卷积称为深度可分卷积,以大幅降低计算成本。深度可分卷积由两层构成:深度卷积和点卷积。我们使用深度卷积为每个输入通道应用一个过滤器(输入深度)。点态卷积,一个简单的1×1卷积,然后用来创建一个线性组合的输出的深度层。MobileNets对两个层都使用batchnorm和ReLU非线性。每个输入通道一个滤波器的深度卷积(输入深度)可表示为:
其中K为大小为DK×DK×M的深度卷积核,其中K中的第M个滤波器应用于F中的第M个信道,得到滤波后的输出特征映射G^的第M个信道。深度卷积的计算代价为:
深度卷积相对于标准卷积是非常有效的。但是,它只过滤输入通道,而不将它们组合起来创建新特性。因此,需要通过1×1的卷积来计算深度卷积输出的线性组合,以生成这些新特性。深度卷积和1×1(点态)卷积的组合称为深度可分离卷积,最早是在[26]中引入的。深度可分卷积的代价:
它是深度卷积和1×1点卷积的和。将卷积表示为滤波和组合的两步过程,计算量减少为:
MobileNet使用3×3深度可分卷积,它使用的计算量是标准卷积的8到9倍,仅在精度上略有降低,如第4节所示。空间维度上的额外因式分解,并没有节省很多额外的计算,因为深度卷积的计算量很少。
标准卷积我们已经很熟悉了,直接看下图吧:
方法
Dk×Dk×M×Df×Df
其中Dk为卷积核尺寸,Df为特征图尺寸,M为输入通道数,输出通道数为1。
逐点卷积负责转换通道,是1x1卷积进行跨通道信息融合,可以大大减少参数量。
方法
Pointwise Convolution的运算与标准卷积运算非常相似,它的卷积核的尺寸为1×1×M,( M为上一层的通道数)。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个卷积核就有几个输出Feature map。
参数量
1 x 1 x M x N
计算量
1x 1 x M x N x Df x Df
其中卷积核尺寸是1×1,Df为特征图尺寸,M为输入通道数,N为输出通道数。
因此计算一次深度可分离卷积的总体计算量为:
它们减少计算量的比例(参数量比例同计算量)为:
MobileNet结构是建立在前一节提到的深度可分卷积,除了第一层是一个完整的卷积。通过用如此简单的术语定义网络,我们可以很容易地探索网络拓扑以找到一个好的网络。MobileNet体系结构定义在表1中。除了最后的全连接层不存在非线性,并输入softmax层进行分类之外,所有层都遵循batchnorm和ReLU非线性。图3对比了一层与正则卷积、batchnorm和ReLU的卷积后的深度卷积、1×1点卷积的分解层和每一层卷积后的batchnorm和ReLU的非线性关系。下行采样在深度卷积和第一层卷积中都使用了strided convolution。最终的平均池将空间分辨率降低到全连接层之前的1。将深度卷积和点卷积计算为独立的层,MobileNet有28层。
仅仅用少量的多添加来定义网络是不够的。确保这些操作能够有效地实现也很重要。例如,非结构化的稀疏矩阵操作通常不会比密集矩阵操作快,直到非常高的稀疏程度。我们的模型结构将几乎所有的计算都放在密集的1×1个卷积中。这可以通过高度优化的通用矩阵乘法(GEMM)函数来实现。通常,卷积是由GEMM实现的,但是需要在内存中进行名为im2col的初始重新排序才能将其映射到GEMM。例如,在流行的Caffe包[15]中使用了这种方法。1×1卷积不需要在内存中重新排序,可以直接使用GEMM实现,GEMM是最优化的数值线性代数算法之一。MobileNet将95%的计算时间花费在1×1个卷积上,其中75%的参数如表2所示。几乎所有的附加参数都在全连接层中。
MobileNet模型在TensorFlow[1]中使用RMSprop[33]进行训练,其异步梯度下降类似于Inception V3[31]。然而,与训练大型模型相反,我们使用较少的正则化和数据扩充技术,因为小型模型的过拟合问题较少。当训练MobileNets时,我们不使用侧头或标签平滑,并通过限制在大型初始训练[31]中使用的小型作物的大小来减少图像失真的数量。此外,我们发现在深度方向的滤波器上放很少或没有权值衰减(l2正则化)是很重要的,因为它们的参数很少。对于下一节中的ImageNet基准测试,所有模型都使用相同的训练参数进行训练,而不考虑模型的大小。
(1)核心层(深度可分离卷积层)
深度可分离卷积结合BN和ReLU,与标准卷积模型的对比如下
(2)整体网络结构
整个MobileNetV1网络除了平均池化层和softmax输出层外,共28层。
第1层为标准卷积,接下来26层为核心层结构(深度可分离卷积层),最后是平均池化层,全连接层加softmax层输出。
除全连接层不使用激活函数,而使用softmax进行分类之外,其他所有层都使用BN和ReLU。
(3)训练设置
计算复杂度设计
深度可分离卷积结构几乎把全部的计算复杂度放到了1×1卷积中,在MobileNet中有95%的时间花费在1×1卷积上,这部分也占了75%的参数。
原因:
- ①1x1卷积核是一个比较密集的矩阵,所以在卷积计算时速度较快
- ②1x1卷积可以直接通过GEMM (general matrix multiply)进行加速,其他类型的卷积在使用GEMM之前需要先经过Im2co
训练配置参数
使用Tensorflow框架训练,使用RMSprop优化器,异步梯度下降(并行数据),需要“钞能力”才可以搞定整个结构。
注意事项: 由于小网络不易过拟合,MobileNet较少使用正则化和数据增强技术(如没有使用side heads或者label smoothing)。
尽管基本的MobileNet体系结构已经很小而且延迟很低,但是很多时候一个特定的用例或应用程序可能需要模型更小和更快。为了构建这些较小和较昂贵的模型计算我们介绍一个非常简单的参数α称为宽度乘数。宽度乘数α的作用是在每一层薄网络统一。对于一个给定的乘数α层和宽度的输入通道数M变成αM和输出通道的数目N变成αN。切除分离卷积的计算成本与宽度乘数α是:
α2 (0;1]典型设置为1、0.75、0.5、0.25。α= 1基线MobileNet和α< 1 MobileNets减少。宽度乘数降低计算成本的影响,参数的数量大约α2平方。宽度乘法器可以应用于任何模型结构,以定义一个新的更小的模型,具有合理的准确性、延迟和大小权衡。它用于定义一个新的简化结构,需要从零开始训练。
目的
为了构造这些更小、计算成本更低的模型,引入了一个非常简单的参数α,称为宽度乘数。
作用
统一让网络中的每一层都更瘦
方法
比如针对某一层网络和α ,输入通道从M变成αM ,输出通道从N变成αN,该层计算量变成了:
**举个栗子:**如果把α设置成0.8,那么网络结构中的每一个卷积层中的卷积核个数都变为0.8倍。
α=1时是基准的MobileNet,而α < 1时是减小的MobileNet。
计算量减少了
第二个hyper-parameter减少神经网络的计算成本是一项决议乘数ρ。我们将其应用于输入图像,每一层的内部表示随后被相同的乘法器缩减。在实践中我们隐式地设置ρ通过设置输入分辨率。我们现在可以表达我们的网络的核心层次的计算成本与宽度切除可分离旋转乘数乘数ρα和解决:
通常隐式设置,因此网络的输入分辨率为224、192、160或128。ρ= 1基线MobileNet和ρ< 1计算MobileNets减少。决议乘数的影响由ρ2降低计算成本。作为一个例子,我们可以看看一个典型的层在MobileNet和看到如何深度可分卷积,宽度乘子和分辨率乘子降低成本和参数。表3显示了一个层的计算和参数的数量,因为架构收缩方法依次应用于该层。第一行显示全卷积层的多加和参数,输入feature map大小为14×14×512,内核K大小为3×3×512×512。我们将在下一节详细讨论资源和准确性之间的权衡。
作用
分辨率超参数ρ用来减少每一层输出的特征图大小的,通过减小特征图的分辨率来降低模型所需要的计算量
方法
ρ负责控制输入图像的尺寸,间接控制中间层feature map的大小。输入的尺寸大,中间层的feature map就大,feature map大卷积的次数就会变多,次数变多运算量变大。
计算量减少了
文中给出了标准卷积、深度可分离卷积以及添加了两种超参数深度可分离卷积的参数量对比例子,输入的feature map 为14x14x512,卷积核大小为3x3x512x512,乘法和加法的计算量如下图:
结论
首先,我们展示了深度可分卷积的MobileNet与全卷积模型的比较结果。在表4中我们可以看到,使用深度可分卷积与全卷积相比,在ImageNet上只降低了1%的精度,这在多加和参数上节省了很多。接下来,我们将展示使用宽度倍增器的较薄模型与使用较少层的较浅模型的比较结果。为了使MobileNet更浅,删除了表1中的5层可分离滤波器,其特征尺寸为14×14×512。表5显示,在相似的计算和参数数量下,使MobileNets变薄比变浅好3%。
实验1:对比标准卷积和深度可分离卷积的效果
表4展示使用标准卷积和深度可分离卷积网络的效果对比
**结论:**虽然精读略有下降,但是使用深度可分离卷积的MobileNets计算量和参数小了很多。
实验2:在计算量恒定情况下,MobileNet是选择更瘦还是更浅的网络
表5展示一个浅层MobileNet (删除了5层卷积层)和一个使用α等于0.75的MobileNet网络结构的对比
**结论:**可以看到二者的参数量和计算量差不多,但是深度减少之后的浅网络精度相对降低,所以超参数比减少网络层数有用。
表6显示了精度,计算和尺寸缩小MobileNet架构的权衡宽度乘数α。精度下降平稳,直到架构是由25α=太小。
表7显示了通过训练具有降低输入分辨率的MobileNets,不同分辨率乘子的准确性、计算和大小权衡。精度随着分辨率的提高而平稳下降。
图4显示了之间的权衡ImageNet 16模型的准确性和计算由宽乘数α2 f1的叉积;0:75;0:5;0:25g和决议f224;192;160;128g。
图5显示之间的权衡ImageNet准确性和参数的数量为16模型由宽乘数α2 f1的叉积;0:75;0:5;0:25g和决议f224;192;160;128 g。
表8将完整的MobileNet与原始的GoogleNet[30]和VGG16[27]进行了比较。MobileNet几乎与VGG16一样精确,但它比VGG16小32倍,计算强度低27倍。它比GoogleNet更精确,但体积更小,计算量是后者的2.5倍。表9比较与宽度减少MobileNet乘数α= 0:5和降低分辨率160×160。简化的MobileNet比AlexNet[19]好4%,同时比AlexNet小45倍和少9:4倍的计算。在相同的尺寸和22倍的计算量下,它也比Squeezenet[12]好4%。
实验3:不同宽度的MobileNet效果对比
表6展示了不同width缩放因子α对准确率的影响
结论: 当α=0.25时才有显著下降。
实验4:不同分辨率的MobileNet效果对比
表7展示了不同分辨率ρ对准确率的影响
**结论:**准确度随着分辨率变低而下降。
实验5:精度和MAdds、参数量的关系
图4展示MAdds和精度的关系
**结论:**当模型越来越小时,精度可近似看成对数跳跃形式的。
图5展示了精度和参数量的关系
结论: 参数量高的精度对应也高。
实验6:和先进网络模型对比
表8比较了MobileNet与GoogleNet和VGG16的效果(大网络)
结论:
表9比较了用width multiplier α=0.5和resolution160*160减少后的MobileNet
结论:
我们训练MobileNet在斯坦福狗数据集[17]上进行细粒度识别。我们扩展了[18]的方法,并从web上收集了比[18]更大但更嘈杂的训练集。我们使用带噪声的web数据预训练细粒度的犬类识别模型,然后在Stanford Dogs training set上对模型进行微调。Stanford Dogs test set的结果如表10所示。MobileNet几乎可以实现[18]的最先进的结果,大大减少了计算和大小。
实验7:在Stanford Dogs数据集上训练MobileNet
表10用网络噪声数据来预训练一个细粒度狗的识别模型,在Stanford Dogs训练集上进行微调
结论: 和Inception v3对比,MobileNet在计算量和参数量降低一个数量级的同时几乎保持相同的精度。
行星[35]把确定照片拍摄地点的任务作为一个分类问题。该方法将地球划分成网格状的地理单元,这些单元作为目标类,并在数百万张带有地理标记的照片上训练一个卷积神经网络。PlaNet已经被证明能够成功地定位大量的照片,并且在处理相同任务方面表现得比Im2GPS更出色[6,7]。我们在相同的数据上使用MobileNet架构对PlaNet进行再培训。而基于Inception V3架构[31]的完整行星模型有5200万个参数和57.4亿个mult-add。MobileNet模型只有1300万个参数,通常是300万个参数用于车身,1000万个参数用于最后一层,58万个多附加参数。如表11所示,MobileNet版本虽然更紧凑,但与PlaNet相比,其性能仅略有下降。此外,它的表现仍然远远好于Im2GPS。
实验8:基于以图搜地,对比不同网络结构进行对比
表11在相同的数据上使用MobileNet架构重新训练PlaNet,然后进行对比
结论: MobileNet版本与PlaNet相比,虽然更紧凑,但性能仅略有下降。但它的性能仍然大大优于Im2GPS。
MobileNet的另一个用例是压缩大型系统,有未知或深奥的培训程序。在人脸属性分类任务中,我们展示了MobileNet与蒸馏之间的协同关系,这是一种深度网络的知识传递技术。我们寻求通过7500万的参数和1600万的mult补充来减少一个大的面部属性分类器。分类器在一个与YFCC100M[32]相似的多属性数据集上进行了训练。我们使用MobileNet架构进行人脸属性分类器。通过训练分类器来模拟更大的模型2的输出,而不是地面真理标签,因此可以通过培训分类器来模拟大模型的输出,从而使从大型(以及潜在的无限)未标记的数据集进行培训。结合蒸馏训练的可扩展性和MobileNet的无偏性参数化,最终系统不仅不需要正规化(例如重量衰变和早期停止),而且还演示了增强的性能。从表12中可以看出,mobilenetbased的分类器对攻击性模型的收缩很有弹性:它在跨属性(指AP)中达到了类似的平均精度(指AP),但仅消耗1%的多值。
实验9:人脸属性分类任务
表12演示了在MobileNet和蒸馏法之间的协同关系
结论: 基础MobileNet分类器具有积极的模型缩小的弹性:它在人脸属性上实现了一个相似的平均精度却只消耗了Mult-Adds的 1%。
MobileNet也可以作为一个有效的基础网络部署在现代目标检测系统。我们报告的结果,MobileNet训练的目标检测基于COCO数据基于最近的工作,赢得了2016年COCO挑战[10]。在表13中,MobileNet在fast - rcnn和SSD框架下与VGG和Inception V2进行了比较。在我们的实验中,SSD被评估为300输入分辨率(SSD 300), fast - rcnn被评估为300和600输入分辨率(FasterRCNN 300, fast - rcnn 600)。快速rcnn模型对每个图像评估300个RPN建议框。模型在COCO训练+val上进行训练,排除8k minival图像,在minival上进行评估。对于这两个框架,MobileNet实现了与其他网络的可比较的结果,只有一小部分的计算复杂性和模型大小。
实验10:COCO数据目标检测训练
表13演示了MobileNet与VGG和Faster-RCNN和SSD框架的比较
结论: MobileNet和另外两个网络相比计算复杂度和模型大小都相对更小
FaceNet模型是目前最先进的人脸识别模型。它建立基于三重损耗的面嵌入。为了建立一个移动的FaceNet模型,我们使用蒸馏来最小化FaceNet和MobileNet输出在训练数据上的平方差异。非常小的MobileNet模型的结果可以在表14中找到。
实验11:人脸识别检测
表14显示了基于MobileNet和蒸馏技术训练出结果
结论: 精读略有下降但计算量和参数大幅度减少
提出了一种基于深度可分卷积的移动网络模型结构。我们研究了一些导致有效模型的重要设计决策。然后,我们演示了如何使用宽度倍增器和分辨率倍增器构建更小、更快的mobilenet,通过权衡一定的精度来减少大小和延迟。然后,我们比较了不同的移动网络和流行的模型,显示出更大的尺寸,速度和准确性的特点。最后,我们展示了MobileNet在广泛应用于各种任务时的有效性。作为帮助采用和探索MobileNets的下一步,我们计划在张量流中发布模型。
(1)提出了一种基于深度可分离卷积构建了轻量级网络— MobileNet,并设置宽度乘子和分辨率乘子以调整网络大小达到在不同设备上适配的 目的。
(2)通过实验其在大幅度降低MAdds和参数量时,没有出现精度显著下降。
(3)在实验部分展示了MobileNet在分类、检测、人脸识别等各种图像任务上的效果,并且将MobileNets与其他先进的模型进行对比,凸显了 MobileNets良好的尺寸和性能。
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary
# 定义DSC结构:DW+PW操作
def BottleneckV1(in_channels, out_channels, stride):
# 深度可分卷积操作模块: DSC卷积 = DW卷积 + PW卷积
return nn.Sequential(
# dw卷积,也是RexNeXt中的组卷积,当分组个数等于输入通道数时,输出矩阵的通道输也变成了输入通道数时,组卷积就是dw卷积
nn.Conv2d(in_channels=in_channels, out_channels=in_channels, kernel_size=3, stride=stride, padding=1,
groups=in_channels),
nn.BatchNorm2d(in_channels),
nn.ReLU6(inplace=True),
# pw卷积,与普通的卷积一样,只是使用了1x1的卷积核
nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1),
nn.BatchNorm2d(out_channels),
nn.ReLU6(inplace=True)
)
# 定义MobileNetV1结构
class MobileNetV1(nn.Module):
def __init__(self, num_classes=5):
super(MobileNetV1, self).__init__()
# torch.Size([1, 3, 224, 224])
self.first_conv = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=2, padding=1),
nn.BatchNorm2d(32),
nn.ReLU6(inplace=True),
)
# torch.Size([1, 32, 112, 112])
# 叠加的基本结构是: DW+PW(DW用来减小尺寸stride=2实现,PW用来增加通道out_channels增加实现)
self.bottleneck = nn.Sequential(
BottleneckV1(32, 64, stride=1), # torch.Size([1, 64, 112, 112]), stride=1
BottleneckV1(64, 128, stride=2), # torch.Size([1, 128, 56, 56]), stride=2
BottleneckV1(128, 128, stride=1), # torch.Size([1, 128, 56, 56]), stride=1
BottleneckV1(128, 256, stride=2), # torch.Size([1, 256, 28, 28]), stride=2
BottleneckV1(256, 256, stride=1), # torch.Size([1, 256, 28, 28]), stride=1
BottleneckV1(256, 512, stride=2), # torch.Size([1, 512, 14, 14]), stride=2
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 1024, stride=2), # torch.Size([1, 1024, 7, 7]), stride=2
BottleneckV1(1024, 1024, stride=1), # torch.Size([1, 1024, 7, 7]), stride=1
)
# torch.Size([1, 1024, 7, 7])
self.avg_pool = nn.AvgPool2d(kernel_size=7, stride=1) # torch.Size([1, 1024, 1, 1])
self.linear = nn.Linear(in_features=1024, out_features=num_classes)
self.dropout = nn.Dropout(p=0.2)
self.softmax = nn.Softmax(dim=1)
self.init_params()
# 初始化操作
def init_params(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight)
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear) or isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def forward(self, x):
x = self.first_conv(x) # torch.Size([1, 32, 112, 112])
x = self.bottleneck(x) # torch.Size([1, 1024, 7, 7])
x = self.avg_pool(x) # torch.Size([1, 1024, 1, 1])
x = x.view(x.size(0), -1) # torch.Size([1, 1024])
x = self.dropout(x)
x = self.linear(x) # torch.Size([1, 5])
out = self.softmax(x) # 概率化
return x
# if __name__ == '__main__':
# model = MobileNetV1()
# # print(model)
#
# input = torch.randn(1, 3, 224, 224)
# out = model(input)
# print(out.shape)
if __name__ == '__main__':
net = MobileNetV1().cuda()
summary(net, (3, 224, 224))
网络结构打印如下:
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 32, 112, 112] 896
BatchNorm2d-2 [-1, 32, 112, 112] 64
ReLU6-3 [-1, 32, 112, 112] 0
Conv2d-4 [-1, 32, 112, 112] 320
BatchNorm2d-5 [-1, 32, 112, 112] 64
ReLU6-6 [-1, 32, 112, 112] 0
Conv2d-7 [-1, 64, 112, 112] 2,112
BatchNorm2d-8 [-1, 64, 112, 112] 128
ReLU6-9 [-1, 64, 112, 112] 0
Conv2d-10 [-1, 64, 56, 56] 640
BatchNorm2d-11 [-1, 64, 56, 56] 128
ReLU6-12 [-1, 64, 56, 56] 0
Conv2d-13 [-1, 128, 56, 56] 8,320
BatchNorm2d-14 [-1, 128, 56, 56] 256
ReLU6-15 [-1, 128, 56, 56] 0
Conv2d-16 [-1, 128, 56, 56] 1,280
BatchNorm2d-17 [-1, 128, 56, 56] 256
ReLU6-18 [-1, 128, 56, 56] 0
Conv2d-19 [-1, 128, 56, 56] 16,512
BatchNorm2d-20 [-1, 128, 56, 56] 256
ReLU6-21 [-1, 128, 56, 56] 0
Conv2d-22 [-1, 128, 28, 28] 1,280
BatchNorm2d-23 [-1, 128, 28, 28] 256
ReLU6-24 [-1, 128, 28, 28] 0
Conv2d-25 [-1, 256, 28, 28] 33,024
BatchNorm2d-26 [-1, 256, 28, 28] 512
ReLU6-27 [-1, 256, 28, 28] 0
Conv2d-28 [-1, 256, 28, 28] 2,560
BatchNorm2d-29 [-1, 256, 28, 28] 512
ReLU6-30 [-1, 256, 28, 28] 0
Conv2d-31 [-1, 256, 28, 28] 65,792
BatchNorm2d-32 [-1, 256, 28, 28] 512
ReLU6-33 [-1, 256, 28, 28] 0
Conv2d-34 [-1, 256, 14, 14] 2,560
BatchNorm2d-35 [-1, 256, 14, 14] 512
ReLU6-36 [-1, 256, 14, 14] 0
Conv2d-37 [-1, 512, 14, 14] 131,584
BatchNorm2d-38 [-1, 512, 14, 14] 1,024
ReLU6-39 [-1, 512, 14, 14] 0
Conv2d-40 [-1, 512, 14, 14] 5,120
BatchNorm2d-41 [-1, 512, 14, 14] 1,024
ReLU6-42 [-1, 512, 14, 14] 0
Conv2d-43 [-1, 512, 14, 14] 262,656
BatchNorm2d-44 [-1, 512, 14, 14] 1,024
ReLU6-45 [-1, 512, 14, 14] 0
Conv2d-46 [-1, 512, 14, 14] 5,120
BatchNorm2d-47 [-1, 512, 14, 14] 1,024
ReLU6-48 [-1, 512, 14, 14] 0
Conv2d-49 [-1, 512, 14, 14] 262,656
BatchNorm2d-50 [-1, 512, 14, 14] 1,024
ReLU6-51 [-1, 512, 14, 14] 0
Conv2d-52 [-1, 512, 14, 14] 5,120
BatchNorm2d-53 [-1, 512, 14, 14] 1,024
ReLU6-54 [-1, 512, 14, 14] 0
Conv2d-55 [-1, 512, 14, 14] 262,656
BatchNorm2d-56 [-1, 512, 14, 14] 1,024
ReLU6-57 [-1, 512, 14, 14] 0
Conv2d-58 [-1, 512, 14, 14] 5,120
BatchNorm2d-59 [-1, 512, 14, 14] 1,024
ReLU6-60 [-1, 512, 14, 14] 0
Conv2d-61 [-1, 512, 14, 14] 262,656
BatchNorm2d-62 [-1, 512, 14, 14] 1,024
ReLU6-63 [-1, 512, 14, 14] 0
Conv2d-64 [-1, 512, 14, 14] 5,120
BatchNorm2d-65 [-1, 512, 14, 14] 1,024
ReLU6-66 [-1, 512, 14, 14] 0
Conv2d-67 [-1, 512, 14, 14] 262,656
BatchNorm2d-68 [-1, 512, 14, 14] 1,024
ReLU6-69 [-1, 512, 14, 14] 0
Conv2d-70 [-1, 512, 7, 7] 5,120
BatchNorm2d-71 [-1, 512, 7, 7] 1,024
ReLU6-72 [-1, 512, 7, 7] 0
Conv2d-73 [-1, 1024, 7, 7] 525,312
BatchNorm2d-74 [-1, 1024, 7, 7] 2,048
ReLU6-75 [-1, 1024, 7, 7] 0
Conv2d-76 [-1, 1024, 7, 7] 10,240
BatchNorm2d-77 [-1, 1024, 7, 7] 2,048
ReLU6-78 [-1, 1024, 7, 7] 0
Conv2d-79 [-1, 1024, 7, 7] 1,049,600
BatchNorm2d-80 [-1, 1024, 7, 7] 2,048
ReLU6-81 [-1, 1024, 7, 7] 0
AvgPool2d-82 [-1, 1024, 1, 1] 0
Dropout-83 [-1, 1024] 0
Linear-84 [-1, 5] 5,125
Softmax-85 [-1, 5] 0
================================================================
Total params: 3,223,045
Trainable params: 3,223,045
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 115.43
Params size (MB): 12.29
Estimated Total Size (MB): 128.30
----------------------------------------------------------------
Process finished with exit code 0
上一篇介绍了MobileNetV1,主要是将普通Conv转换为dw和pw,但是在dw中训练出来可能会很多0,也就是depthwise部分得到卷积核会废掉,即卷积核参数大部分为0,因为权重数量可能过少,再加上Relu激活函数的原因。
后来作者大大又做了修改,MobileNetV2使用了深度可分离卷积+先升维+倒残差+低维不使用ReLU的策略构建了比V1效果更好更轻量的网络。
在本文中,我们描述了一种新的移动架构MobileNetV2,该架构提高了移动模型在多个任务和多个基准数据集上以及在不同模型尺寸范围内的最佳性能。我们还描述了在我们称之为SSDLite的新框架中将这些移动模型应用于目标检测的有效方法。此外,我们还演示了如何通过DeepLabv3的简化形式,我们称之为Mobile DeepLabv3来构建移动语义分割模型。
MobileNetV2架构基于倒置的残差结构,其中快捷连接位于窄的瓶颈层之间。中间展开层使用轻量级的深度卷积作为非线性源来过滤特征。此外,我们发现为了保持表示能力,去除窄层中的非线性是非常重要的。我们证实了这可以提高性能并提供了产生此设计的直觉。
最后,我们的方法允许将输入/输出域与变换的表现力解耦,这为进一步分析提供了便利的框架。我们在ImageNet[1]分类,COCO目标检测[2],VOC图像分割[3]上评估了我们的性能。我们评估了在精度、通过乘加(MAdd)度量的操作次数,以及实际的延迟和参数的数量之间的权衡。
本文主要工作
(1)本文提出了一种新的移动端架构MobileNet V2,在当前移动端模型中最优
(2)本文介绍了一种新框架:SSDLite,描述了如何通过SSDLite将这些移动模型应用于对象检测
(3)本文还演示了如何通过简化形式的DeepLabv3(称之为mobile DeepLabv3)构建移动端的语义分割模型
神经网络已经彻底改变了机器智能的许多领域,使具有挑战性的图像识别任务获得了超过常人的准确性。然而,提高准确性的驱动力往往需要付出代价:现代先进网络需要超出许多移动和嵌入式应用能力之外的高计算资源。
本文介绍了一种专为移动和资源受限环境量身定制的新型神经网络架构。我们的网络通过显著减少所需操作和内存的数量,同时保持相同的精度推进了移动定制计算机视觉模型的最新水平。
我们的主要贡献是一个新的层模块:具有线性瓶颈的倒置残差。该模块将输入的低维压缩表示首先扩展到高维并用轻量级深度卷积进行过滤。随后用线性卷积将特征投影回低维表示。官方实现可作为[4]中TensorFlow-Slim模型库的一部分。
这个模块可以使用任何现代框架中的标准操作来高效地实现,并允许我们的模型使用标准基线沿多个性能点击败最先进的技术。此外,这种卷积模块特别适用于移动设计,因为它可以通过从不完全实现大型中间张量来显著减少推断过程中所需的内存占用。这减少了许多嵌入式硬件设计中对主存储器访问的需求,这些设计提供了少量高速软件控制缓存。
本文主要贡献
该模块将低维压缩表示作为输入,首先将其扩展到高维,并使用轻型深度卷积进行过滤。特征随后通过线性卷积投影回低维表示。
该模块的优点
(1)该模块可以在任何现代框架中高性能实现。
(2)适合于移动设计。减少了许多嵌入式硬件设计中对主内存访问的需求,提供了少量非常快速的软件控制高速缓存。
调整深层神经架构以在精确性和性能之间达到最佳平衡已成为过去几年研究活跃的一个领域。由许多团队进行的手动架构搜索和训练算法的改进,已经比早期的设计(如AlexNet[5],VGGNet [6],GoogLeNet[7]和ResNet[8])有了显著的改进。最近在算法架构探索方面取得了很多进展,包括超参数优化[9,10,11]、各种网络修剪方法[12,13,14,15,16,17]和连接学习[18,19]。 也有大量的工作致力于改变内部卷积块的连接结构如ShuffleNet[20]或引入稀疏性[21]和其他[22]。
最近,[23,24,25,26]开辟了了一个新的方向,将遗传算法和强化学习等优化方法带入架构搜索。然而,一个缺点是最终所得到的网络非常复杂。在本文中,我们追求的目标是发展了解神经网络如何运行的更好直觉,并使用它来指导最简单可能的网络设计。我们的方法应该被视为[23]中描述的方法和相关工作的补充。在这种情况下,我们的方法与[20,22]所采用的方法类似,并且可以进一步提高性能,同时可以一睹其内部的运行。我们的网络设计基于MobileNetV1[27]。它保留了其简单性,并且不需要任何特殊的运算符,同时显著提高了它的准确性,为移动应用实现了在多种图像分类和检测任务上的最新技术。
以前的相关工作
本文的目标
开发关于神经网络如何操作的更好的直觉,并使用它来指导最简单的网络设计。
深度可分卷积是许多高效神经网络架构的关键组成部分[27,28,20],我们在目前的工作中也使用它们。其基本思想是用分解版本替换完整的卷积运算符,将卷积拆分为两个单独的层。第一层称为深度卷积,它通过对每个输入通道应用单个卷积滤波器来执行轻量级滤波。第二层是1×1卷积,称为逐点卷积,它负责通过计算输入通道的线性组合来构建新特征。
标准卷积使用K∈Rk×k×di×dj维的输入张量Li,并对其应用卷积核K∈Rk×k×di×dj来产生hi×wi×dj维的输出张量Lj。标准卷积层的计算代价为hi⋅wi⋅di⋅dj⋅k⋅k。
深度可分卷积是标准卷积层的直接替换。经验上,它们几乎与常规卷积一样工作,但其成本为:
hi⋅wi⋅di(k2+dj) (1)
它是深度方向和1×1逐点卷积的总和。深度可分卷积与传统卷积层相比有效地减少了几乎k^2倍的计算量。MobileNetV2使用k=3(3×3的深度可分卷积),因此计算成本比标准卷积小88到99倍,但精度只有很小的降低[27]。
这个在MobileNetV1(点这里复习一下)时我们就见过了:
虽然精读略有降低,但计算量比标准卷积低8到9倍。
考虑一个由n层Li组成的深度神经网络,每层都有一个hi×wi×di维的激活张量。在本节中,我们将讨论这些激活张量的基本属性,我们将把它们看作hi×wi个具有di维的“pixels”。非正式地,对于输入的一组真实图像,我们说层激活的集合(对于任何层Li)形成一个“感兴趣的流形”。长久以来,人们一直认为神经网络中的流形可以嵌入到低维子空间中。换句话说,当我们查看深层卷积层的所有单独的d通道像素时,在这些值中编码的信息实际上位于某个流形中,这反过来又可嵌入到低维子空间中。
乍一看,这样的实例可以通过简单地减少层的维度来捕获和利用,从而降低操作空间的维度。这已经被MobileNetV1[27]成功利用,通过宽度乘数参数在计算量和精度之间进行有效折衷,并且已经被合并到其他网络的高效模型设计中[20]。遵循这种直觉,宽度乘数方法允许降低激活空间的维度,直到感兴趣的流形横跨整个空间为止。然而,当我们回想到深度卷积神经网络实际上具有非线性的每个坐标变换(例如ReLU)时,这种直觉就会失败。 例如,在1维空间中的一行应用ReLU会产生一个ray
,在Rn空间中,它通常会产生一个具有n个连接的分段线性曲线。
很容易看出,如果层变换ReLU(Bx)的结果具有非零的体积S,映射到内部S的点通常通过输入的线性变换B获得,因此表明与全维度输出相对应的输入空间的一部分受限于线性变换。换句话说,深层网络只在输出域的非零体积部分具有线性分类器的能力。我们将在补充材料中进行更正式的说明。
另一方面,当ReLU破坏通道时,它不可避免地会丢失该通道的信息。但是,如果我们有很多通道,并且激活流形中有一个结构,信息可能仍然保留在其它通道中。在补充材料中,我们说明,如果输入流形可以嵌入到激活空间的显著较低维子空间中,则ReLU变换将保留该信息,同时将所需的复杂性引入到可表达的函数集中。
总而言之,我们已经强调了两个特性,这些特性表明需要的感兴趣流行应该位于较高维激活空间的低维子空间中:
1.如果感兴趣的流形在ReLU转换后保持非零体积,则其对应于线性转换。
2.只有当输入流形位于输入空间的低维子空间时,ReLU才能保留有关输入流形的完整信息。
这两个深刻见解为我们提供了优化现有神经架构的经验提示:假设感兴趣流形是低维的,我们可以通过将线性瓶颈层插入到卷积模块中来捕获这一点。实验证据表明,使用线性层是至关重要的,因为它可以防止非线性破坏太多的信息。在第6节中,我们通过经验证明,在瓶颈中使用非线性层确实会使性能降低几个百分点,进一步证实了我们的假设。我们注意到[29]报告了非线性得到帮助的类似报告,其中非线性已从传统残差块的输入中移除,并导致CIFAR数据集的性能得到了改善。
对于本文的其余部分,我们将利用瓶颈卷积。我们将把输入瓶颈的大小与内部大小之间的比例作为扩展比。
v1的做法以及不足
**做法:**引入α参数来做模型通道的缩减,相当于给模型“瘦身”,这样特征信息就能更集中在缩减后的通道中。
**不足:**但研究人员发现深度可分离卷积中有大量卷积核为0,即有很多卷积核没有参与实际计算。
v2的作者发现是ReLU激活函数的问题,认为 ReLU这个激活函数,在低维空间运算中会损失很多信息,而在高维空间中会保留较多有用信息 。
ReLU与维度的关系
ReLU会对维度较低的张量造成较大的信息损耗。
两个性质
(1)如果"manifold of interest"都为非零值,则经过ReLU相当于只做了一个线性变换,没有信息丢失
(2)维度足够多时,ReLU能够保留"manifold of interest"的完整信息
本文方法
论文针对这个问题在Bottleneck末尾使用Linear Bottleneck(即不使用ReLU激活,做了线性变换)来代替原本的非线性激活变换。具体到v2网络中就是将最后的Point-Wise卷积的ReLU6都换成线性函数。
实验证据表明,使用线性层至关重要,因为它可以防止非线性破坏太多的信息。
可分离卷积块的演化
瓶颈块与残差块类似,其中每个块包含一个输入,然后是几个瓶颈,然后是扩展[8]。然而,受直觉的启发,瓶颈实际上包含所有必要的信息,而扩展层只是伴随张量非线性变换的实现细节,我们直接在瓶颈之间使用快捷连接。图3提供了设计差异的示意图。插入快捷连接的动机与经典的残差连接类似:我们想要提高梯度在乘法层之间传播的能力。但是,倒置设计的内存效率要高得多(详见第5节),而且在我们的实验中效果稍好。
图3:残差块[8,30]和倒置残差之间的差异。对角阴影线层不使用非线性。我们用每个块的厚度来表明其相对数量的通道。注意经典残差是如何将通道数量较多的层连接起来的,而倒置残差则是连接瓶颈。最好通过颜色看。
瓶颈卷积的运行时间和参数计数基本实现结构如表1所示。对于大小为h×w的块,扩展因子为t,内核大小为k,具有d′维输入通道和d″维输出通道,所需的乘法加法总数为h⋅w⋅d′⋅t(d′+k2+d′′)。与(1)相比,这个表达式有一个额外项,因为实际上我们有一个额外的1×1卷积,但是我们的网络性质使我们能够利用更小的输入和输出维度。在表3中,我们比较了MobileNetV1,MobileNetV2和ShuffleNet之间每种分辨率所需的尺寸。
表1:瓶颈残差块从k转换为k′个通道,步长为s,扩展系数为t。
表3:不同架构中需要在每个空间分辨率上实现的最大通道数/内存(以Kb为单位)。我们假设激活使用16位浮点数。对于ShuffleNet,我们使用与MobileNetV1和MobileNetV2的性能相匹配的2x,g=3。对于MobileNetV2和ShuffleNet的第一层,我们可以采用第5节中描述的技巧来降低内存需求。尽管ShuffleNet在其它地方使用了瓶颈,但由于存在非瓶颈张量之间的快捷连接,因此非瓶颈张量仍然需要实现。
引入问题
如果整个网络都是低维那么整体的运算速度就会很快,但会有信息丢失;如果提取足够多的特征的话,还是希望有高纬度通道来做这件事。
深度卷积本身没有改变通道的作用,比如本文前例中的深度可分离卷积的例子,在前一半的深度卷积操作中,输入通道数量就是输出通道数量。
本文的做法
MobileNetV2中首先在DW深度卷积之前使用PW卷积进行升维(升维倍数为t,t=6),再在一个更高维的空间中进行卷积操作来提取特征,最后再压缩数据,让网络变小。
举个例子: 假设输入到Inverted Residual的输入为56×56×24,则先经过1×1的Conv进行升维,扩展因子 t 为6,维度变为144。然后经过3×3的DW卷积,维度不变。最后再使用1×1的Conv进行降维,维度变为24.
ResNet和MobileNetV2对比
论文中的图示
我们架构的一个有趣特性是它在构建块(瓶颈层)的输入/输出域与层转换之间提供了自然分离——这是一种将输入转换为输出的非线性函数。前者可以看作是网络在每一层的容量,而后者则是表现力。与常规和可分离的传统卷积块相比,其中表现力和容量都缠结在一起并且是输出层深度的函数。
特别是在我们的实例中,当内层深度为0时,由于快捷连接,基础卷积是恒等函数。当扩展比率小于1时,这是一个经典的残差卷积块[8,30]。但是,就我们的目的而言,我们表明扩大比率大于1是最有用的。
这种解释使我们能够独立于其容量研究网络的表现力,并且我们认为需要进一步探索这种分离,以便更好地理解网络性质。
架构块的特点
它在构建块的输入/输出域(瓶颈层)和层转换之间提供了一种自然的分离,层转换是一种将输入转换为输出的非线性函数。
前者可以被视为网络在每一层的容量,而后者则被视为表现力。
传统卷积块的特点
在传统的卷积块中,表达能力和容量都纠结在一起,是输出层深度的函数。
现在我们详细描述我们的架构。正如前一节所讨论的那样,基本构件块是一个瓶颈深度可分离的残差卷积。该模块的详细结构如表1所示。MobileNetV2的架构包含具有32个滤波器的初始全卷积层,接着是表2中描述的19个残差瓶颈层。我们使用ReLU6作为非线性,因为用于低精度计算时它的鲁棒性[27]。我们总是使用现代网络中的标准核尺寸3×3,并在训练期间利用丢弃和批归一化。
除第一层外,我们在整个网络中使用恒定的扩展率。在我们的实验中,我们发现5到10之间的扩展速率导致几乎相同的性能曲线,较小的网络以较小的扩展速率更好,而较大的网络在较大扩展速率时具有稍微更好的性能。
对于我们所有的主要实验,我们使用扩展因子66来应用于输入张量的大小。例如,对于瓶颈层采用64通道的输入张量并产生具有128×128通道的张量,中间扩展层则具有64*6=384个通道。
和[27]一样,我们通过使用输入图像分辨率和宽度倍数作为可调超参数来调整我们的架构以适应不同的性能点,可以根据所需的精度/性能权衡来调整。我们的主要网络(宽度乘数1,224×224)的计算成本为3亿次乘法,并使用了340万个参数。我们研究了性能权衡,输入分辨率从96到224,宽度乘数从0.35到1.4。网络计算成本范围从7次乘法增加到585M MAdds,而模型大小在1.7M个参数和6.9M个参数之间变化。
一个较小的实现差异,[27]是对于小于1的乘数,我们将宽度乘数应用于除最后一个卷积层以外的所有层。这可以提高更小模型的性能。
网络架构
表2:MobileNet V2实现架构。
除了第一层,在整个网络中使用恒定的扩展因子t;
较小的网络用较小的扩展因子t表现更好;
较大的网络使用较大的扩展因子时表现更好。
Tradeoff hyper parameters—超参数的权衡
MobileNet v2同样使用MobileNet V1中的两个超参数,宽度系数α和分辨率系数ρ;
与MobileNet v1的不同是,对于小于1的乘数,本文将宽度系数α应用于除最后一个卷积层之外的所有层。这提高了较小模型的性能。
倒置的残差颈层允许特定地内存有效的实现,这对于移动应用非常重要。使用TensorFlow[31]或Caffe[32]等标准高效的推断实现,构建了一个有向无环计算超图G,由表示操作的边和代表中间计算张量的节点组成。预定计算是为了最小化需要存储在内存中的张量总数。在最一般的情况下,它会搜索所有合理的计算顺序Σ(G),并选择最小化
其中R(i, π, G)是连接到πi…πn任何节点的中间张量列表,|A|表示张量A的大小,size(i)是操作i期间内部存储所需的总内存量。
对于仅具有平凡并行结构(例如残差连接)的图,只有一个非平凡的可行计算顺序,因此可以简化计算图G推断所需的内存总量和界限:
或者重申,内存量只是在所有操作中组合输入和输出的最大总大小。在下文中我们将展示如果我们将瓶颈残差块视为单一操作(并将内部卷积视为一次性张量),则总内存量将由瓶颈张量的大小决定,而不是瓶颈的内部张量的大小(更大)。
使用例如TensorFlow或Caffe 的推理的标准有效实现构建了有向非循环计算超图G,其包括表示操作的边和表示中间计算的张量的节点。
如果本文将瓶颈残差块视为单个操作(并将内卷积视为可任意处理的张量),则内存总量将由瓶颈张量的大小决定,而不是由瓶颈内部的张量(并且大得多)的大小决定。
Bottleneck Residual Block—残差瓶颈块
瓶颈残差块 图3b中所示的F(x)可以表示为三个运算符的组合F(x)=[A∘N∘B]x,其中A是线性变换A:Rs×s×k→Rs×s×n,N是一个非线性的每个通道的转换:N:Rs×s×n→Rs′×s′×n,B是输出域的线性转换:B:Rs′×s′×n→Rs′×s′×k′。
对于我们的网络N=ReLU6◦ dwise◦ ReLU6,但结果适用于任何的按通道转换。假设输入域的大小是|x|并且输出域的大小是|y|,那么计算F(X)所需的内存可以低至|s2k|+|s′2k′|+O(max(s2,s′2))。
该算法基于以下事实:内部张量I可以表示为t张量的连接,每个大小为n/t,则我们的函数可以表示为
F(x)=∑i=1t(Ai∘N∘Bi)(x)
通过累加和,我们只需要将一个大小为n/t的中间块始终保留在内存中。使用n=t,我们最终只需要保留中间表示的单个通道。使我们能够使用这一技巧的两个约束是(a)内部变换(包括非线性和深度)是每个通道的事实,以及(b)连续的非按通道运算符具有显著的输入输出大小比。对于大多数传统的神经网络,这种技巧不会产生显著的改善。
我们注意到,使用t路分割计算F(X)所需的乘加运算符的数目是独立于t的,但在现有实现中,我们发现由于增加的缓存未命中,用几个较小的矩阵乘法替换一个矩阵乘法会很损坏运行时的性能 。我们发现这种方法最有用,t是22和55之间的一个小常数。它显著降低了内存需求,但仍然可以利用深度学习框架提供的高度优化的矩阵乘法和卷积算子来获得的大部分效率。如果特殊的框架级优化可能导致进一步的运行时改进,这个方法还有待观察。
残差块和逆残差的区别
对角线阴影层不使用非线性。本文用每个块的厚度来表示它的相对通道数。请注意经典残差如何连接具有大量通道的层,而反向残差如何连接瓶颈。
本文用到的原理
内部张量τ可以表示为t个张量的串联,每个张量的大小为n/t。函数可以表示为:
本文使用方法
本文通过累加总和,只需要在内存中一直保存一个大小为n/t的中间块。使用n = t,最终必须始终只保留中间表示的单个通道。
该方法的两个约束:
(a)内部变换(包括非线性和深度方向)是每通道的
(b)连续的非每通道操作符具有输入大小与输出的显著比率
文章从classification、detection、segmentation三个应用方面测试了该模型的效果。
训练设置我们使用TensorFlow[31]训练我们的模型。我们使用标准的RMSPropOptimizer,将衰减和动量都设置为0.9。我们在每层之后使用批标准化,并将标准权重衰减设置为0.00004。遵循MobileNetV1 [27]的设置,我们使用初始学习率为0.045,学习率的衰减比率为每个迭代周期衰减0.98。我们使用16个GPU异步,批大小为96。
结果我们将我们的网络与MobileNetV1,ShuffleNet和NASNet-A模型进行了比较。表4列出了一些选定模型的统计数据,完整的性能图如图5所示。
训练参数设置
将本文的网络与MobileNetV1、ShuffleNet和NASNet-A模型进行了比较
表4显示了一些选定模型的统计数据
图5显示了完整的性能图
**结论:**MobileNetV2比MobileNetV1性能提高很多。和ShuffleNet和NASNet-A相比虽然参数量和计算量有增加但精读有提升
我们评估和比较了MobileNetV2和MobileNetV1的性能,MobileNetV1使用COCO数据集[2]上Single Shot Detector(SSD)[34]的修改版本作为目标检测的特征提取器[33]。我们还将YOLOv2[35]和原始SSD(以VGG-16[6]为基础网络)作为基准进行比较。由于我们专注于移动/实时模型,因此我们不会比较Faster-RCNN[36]和RFCN[37]等其它架构的性能。
SSDLite 在本文中,我们将介绍常规SSD的移动友好型变种。我们在SSD预测层中用可分离卷积(深度方向后接1×11×1投影)替换所有常规卷积。这种设计符合MobileNets的整体设计,并且在计算上效率更高。我们称之为修改版本的SSDLite。与常规SSD相比,SSDLite显著降低了参数计数和计算成本。
对于MobileNetV1,我们按照[33]中的设置进行。对于MobileNetV2,SSDLite的第一层被附加到层15的扩展(输出步长为16)。SSDLite层的第二层和其余层连接在最后一层的顶部(输出步长为32)。此设置与MobileNetV1一致,因为所有层都附加到相同输出步长的特征图上。
MobileNet模型都经过了开源TensorFlow目标检测API的训练和评估[38]。 两个模型的输入分辨率为320×320。我们进行了基准测试并比较了mAP(COCO挑战度量标准),参数数量和Multiply-Adds数量。结果如表6所示。MobileNetV2 SSDLite不仅是最高效的模型,而且也是三者中最准确的模型。值得注意的是,MobileNetV2 SSDLite效率高20倍,模型要小10倍,但仍优于COCO数据集上的YOLOv2。
训练参数设置
**数据集:**COCO数据集
SSDLite设置
在SSD预测层中,将所有规则卷积替换为可分离卷积(深度为1×1投影)
表5显示了SSD和SSDLite的对比结果
**结论:**与常规SSD相比,SSDLite显著减少了参数计数和计算成本。
在本节中,我们使用MobileNetV1和MobileNetV2模型作为特征提取器与DeepLabv3[39]在移动语义分割任务上进行比较。DeepLabv3采用了空洞卷积[40,41,42],这是一种显式控制计算特征映射分辨率的强大工具,并构建了五个平行头部,包括(a)包含三个具有不同空洞率的3×3卷积的Atrous Spatial Pyramid Pooling模块(ASPP)[43],(b)1×1卷积头部,以及(c)图像级特征[44]。我们用输出步长来表示输入图像空间分辨率与最终输出分辨率的比值,该分辨率通过适当地应用空洞卷积来控制。对于语义分割,我们通常使用输出stride=16或88来获取更密集的特征映射。我们在PASCAL VOC 2012数据集[3]上进行了实验,使用[45]中的额外标注图像和评估指标mIOU。
为了构建移动模型,我们尝试了三种设计变体:(1)不同的特征提取器,(2)简化DeepLabv3头部以加快计算速度,以及(3)提高性能的不同推断策略。我们的结果总结在表7中。我们已经观察到:(a)包括多尺度输入和添加左右翻转图像的推断策略显著增加了MAdds,因此不适合于在设备上应用,(b)使用输出步长16比使用输出步长8更有效率,(c)MobileNetV1已经是一个强大的特征提取器,并且只需要比ResNet-101少约4.9-5.7倍的MAdd[8](例如,mIOU:78.56与82.70和MAdds:941.9B vs 4870.6B),(d)在MobileNetV2的倒数第二个特征映射的顶部构建DeepLabv3头部比在原始的最后一个特征映射上更高效,因为倒数第二个特征映射包含320个通道而不是1280个通道,这样我们就可以达到类似的性能,但是要比MobileNetV1的通道少2.5倍,(e)DeepLabv3头部的计算成本很高,移除ASPP模块会显著减少MAdd并且只会稍微降低性能。在表7末尾,我们鉴定了一个设备上的潜在候选应用(粗体),该应用可以达到75.32%mIOU并且只需要2.75B MAdds。
将MobileNetv1,v2作为DeepLabv3中的特征提取网络,随后经过ASPP融合多尺度语义信息以及解码网络用于语义分割
训练参数设置
**数据集:**PASCAL VOC 2012
三种变体
(1)不同的特征提取器
(2)简化DeepLabv3头部以加快计算速度
(3)不同的推理策略以提高性能
三种变体结果总结
结论
(1)多尺度输入和添加左右翻转图像,增加了计算力,并不适用
(2)使用输出步幅=16比输出步幅=8更有效
(3)MobileNetV2只需要大约4.9− MAdds,比ResNet-101少5.7倍
(4)MobileNetV2的第二个最后一层功能图上构建DeepLabv3比在原始的最后一层功能图上更有效
(5)DeepLabv3磁头的计算成本很高,移除ASPP模块可以显著降低MADD,而性能只会略有下降。
倒置残差连接。残差连接的重要性已被广泛研究[8,30,46]。本文报告的新结果是连接瓶颈的快捷连接性能优于连接扩展层的的快捷连接(请参见图6b以供比较)。
线性瓶颈的重要性。线性瓶颈模型的严格来说比非线性模型要弱一些,因为激活总是可以在线性状态下进行,并对偏差和缩放进行适当的修改。然而,我们在图6a中展示的实验表明,线性瓶颈改善了性能,为非线性破坏低维空间中的信息提供了支持。
Inverted residual connections—反向残差连接
图7(b)比较了快捷连接瓶颈层和扩展层以及不使用残差连接
**结论:**快捷方式连接瓶颈的性能优于连接扩展层的快捷方式
Importance of linear bottlenecks—线性瓶颈的重要性
线性瓶颈模型比非线性模型更弱,因为激活总是在线性范围内运行,并对偏差和比例做出适当的改变。然而,如图6a所示的实验表明,线性瓶颈提高了性能,提供了非线性破坏低维空间信息的支持。
我们描述了一个非常简单的网络架构,使我们能够构建一系列高效的移动模型。我们的基本构建单元具有多种特性,使其特别适用于移动应用。它允许非常有效的内存推断,并依赖利用所有神经框架中的标准操作。
对于ImageNet数据集,我们的架构改善了许多性能点的最新技术水平。对于目标检测任务,我们的网络在精度和模型复杂度方面都优于COCO数据集上的最新实时检测器。值得注意的是,我们的架构与SSDLite检测模块相比,计算量少20倍,参数比YOLOv2少10倍。
理论上:所提出的卷积块具有独特的属性,允许将网络表现力(由扩展层编码)与其容量(由瓶颈输入编码)分开。探索这个是未来研究的重要方向。
① 提出一个更轻量的网络,准确率高。
② 作者提出的这种改进模型效率的思路非常实用,如深度可分离卷积。这些思路可以用于改进当前表现好但是计算复杂的模型。
import torch
from torch import nn
import torch.nn.functional as F
from torchsummary import summary
# ------------------------------------------------------#
# 这个函数的目的是确保Channel个数能被8整除。
# 很多嵌入式设备做优化时都采用这个准则
# ------------------------------------------------------#
def _make_divisible(v, divisor, min_value=None):
if min_value is None:
min_value = divisor
# int(v + divisor / 2) // divisor * divisor:四舍五入到8
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
# Make sure that round down does not go down by more than 10%.
if new_v < 0.9 * v:
new_v += divisor
return new_v
# -------------------------------------------------------------#
# Conv+BN+ReLU经常会用到,组在一起
# 参数顺序:输入通道数,输出通道数...
# 最后的groups参数:groups=1时,普通卷积;
# groups=输入通道数in_planes时,DW卷积=深度可分离卷积
# pytorch官方继承自nn.sequential,想用它的预训练权重,就得听它的
# -------------------------------------------------------------#
class ConvBNReLU(nn.Sequential):
def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1):
padding = (kernel_size - 1) // 2
super(ConvBNReLU, self).__init__(
# 不使用偏置bias,因为使用了BN层,此时偏置不起作用了
nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False),
nn.BatchNorm2d(out_planes),
nn.ReLU6(inplace=True)
)
# ------------------------------------------------------#
# InvertedResidual,先变胖后变瘦
# 参数顺序:输入通道数,输出通道数,步长,变胖倍数(扩展因子)
# ------------------------------------------------------#
class InvertedResidual(nn.Module):
def __init__(self, inp, oup, stride, expand_ratio):
super(InvertedResidual, self).__init__()
self.stride = stride
assert stride in [1, 2]
# 所谓的隐藏维度,其实就是输入通道数*变胖倍数
hidden_dim = int(round(inp * expand_ratio))
# 只有同时满足两个条件时,才使用短连接
self.use_res_connect = self.stride == 1 and inp == oup
layers = []
# 如果扩展因子等于1,就没有第一个1x1的卷积层
if expand_ratio != 1:
layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) # pointwise
layers.extend([
# 3x3 depthwise conv,因为使用了groups=hidden_dim
ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim),
# 1x1 pointwise conv(linear)
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
])
self.conv = nn.Sequential(*layers)
def forward(self, x):
if self.use_res_connect:
return x + self.conv(x)
else:
return self.conv(x)
# MobileNetV2是一个类,继承自nn.module这个父类
class MobileNetV2(nn.Module):
def __init__(self, num_classes=1000, width_mult=1.0, inverted_residual_setting=None, round_nearest=8):
"""
MobileNet V2 main class
Args:
num_classes (int): Number of classes
width_mult (float): Width multiplier - adjusts number of channels in each layer by this amount
inverted_residual_setting: Network structure
round_nearest (int): Round the number of channels in each layer to be a multiple of this number
Set to 1 to turn off rounding
"""
super(MobileNetV2, self).__init__()
block = InvertedResidual
# 保证通道数是 8 的倍数,原因是:适配于硬件优化加速
input_channel = _make_divisible(32 * width_mult, round_nearest)
last_channel = _make_divisible(1280 * width_mult, round_nearest)
if inverted_residual_setting is None:
# t表示扩展因子(变胖倍数);c是通道数;n是block重复几次;
# s:stride步长,只针对第一层,其它s都等于1
inverted_residual_setting = [
# t, c, n, s
# 208,208,32 -> 208,208,16
[1, 16, 1, 1],
# 208,208,16 -> 104,104,24
[6, 24, 2, 2],
# 104,104,24 -> 52,52,32
[6, 32, 3, 2],
# 52,52,32 -> 26,26,64
[6, 64, 4, 2],
# 26,26,64 -> 26,26,96
[6, 96, 3, 1],
# 26,26,96 -> 13,13,160
[6, 160, 3, 2],
# 13,13,160 -> 13,13,320
[6, 320, 1, 1],
]
# only check the first element, assuming user knows t,c,n,s are required
if len(inverted_residual_setting) == 0 or len(inverted_residual_setting[0]) != 4:
raise ValueError("inverted_residual_setting should be non-empty "
"or a 4-element list, got {}".format(inverted_residual_setting))
# conv1 layer
# 416,416,3 -> 208,208,32
features = [ConvBNReLU(3, input_channel, stride=2)]
# building inverted residual blocks
for t, c, n, s in inverted_residual_setting:
output_channel = _make_divisible(c * width_mult, round_nearest)
for i in range(n):
# -----------------------------------#
# s为1或者2 只针对重复了n次的bottleneck 的第一个bottleneck,
# 重复n次的剩下几个bottleneck中s均为1。
# -----------------------------------#
stride = s if i == 0 else 1
# 这个block就是上面那个InvertedResidual函数
features.append(block(input_channel, output_channel, stride, expand_ratio=t))
# 这一层的输出通道数作为下一层的输入通道数
input_channel = output_channel
# building last several layers
features.append(ConvBNReLU(input_channel, last_channel, kernel_size=1))
# *features表示位置信息,将特征层利用nn.Sequential打包成一个整体
self.features = nn.Sequential(*features)
# building classifier
# 自适应平均池化下采样层,输出矩阵高和宽均为1
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.classifier = nn.Sequential(
nn.Dropout(0.2),
nn.Linear(last_channel, num_classes),
)
# weight initialization
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out')
if m.bias is not None:
nn.init.zeros_(m.bias)
elif isinstance(m, nn.BatchNorm2d):
nn.init.ones_(m.weight)
nn.init.zeros_(m.bias)
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight, 0, 0.01) # 正太分布
nn.init.zeros_(m.bias)
def forward(self, x):
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1) # 展平处理
x = self.classifier(x)
return x
def mobilenet_v2(num_classes: int = 1000):
model = MobileNetV2(num_classes=num_classes)
return model
if __name__ == '__main__':
net = MobileNetV2().cuda()
summary(net, (3, 224, 224))
网络结构打印如下:
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 32, 112, 112] 864
BatchNorm2d-2 [-1, 32, 112, 112] 64
ReLU6-3 [-1, 32, 112, 112] 0
Conv2d-4 [-1, 32, 112, 112] 288
BatchNorm2d-5 [-1, 32, 112, 112] 64
ReLU6-6 [-1, 32, 112, 112] 0
Conv2d-7 [-1, 16, 112, 112] 512
BatchNorm2d-8 [-1, 16, 112, 112] 32
InvertedResidual-9 [-1, 16, 112, 112] 0
Conv2d-10 [-1, 96, 112, 112] 1,536
BatchNorm2d-11 [-1, 96, 112, 112] 192
ReLU6-12 [-1, 96, 112, 112] 0
Conv2d-13 [-1, 96, 56, 56] 864
BatchNorm2d-14 [-1, 96, 56, 56] 192
ReLU6-15 [-1, 96, 56, 56] 0
Conv2d-16 [-1, 24, 56, 56] 2,304
BatchNorm2d-17 [-1, 24, 56, 56] 48
InvertedResidual-18 [-1, 24, 56, 56] 0
Conv2d-19 [-1, 144, 56, 56] 3,456
BatchNorm2d-20 [-1, 144, 56, 56] 288
ReLU6-21 [-1, 144, 56, 56] 0
Conv2d-22 [-1, 144, 56, 56] 1,296
BatchNorm2d-23 [-1, 144, 56, 56] 288
ReLU6-24 [-1, 144, 56, 56] 0
Conv2d-25 [-1, 24, 56, 56] 3,456
BatchNorm2d-26 [-1, 24, 56, 56] 48
InvertedResidual-27 [-1, 24, 56, 56] 0
Conv2d-28 [-1, 144, 56, 56] 3,456
BatchNorm2d-29 [-1, 144, 56, 56] 288
ReLU6-30 [-1, 144, 56, 56] 0
Conv2d-31 [-1, 144, 28, 28] 1,296
BatchNorm2d-32 [-1, 144, 28, 28] 288
ReLU6-33 [-1, 144, 28, 28] 0
Conv2d-34 [-1, 32, 28, 28] 4,608
BatchNorm2d-35 [-1, 32, 28, 28] 64
InvertedResidual-36 [-1, 32, 28, 28] 0
Conv2d-37 [-1, 192, 28, 28] 6,144
BatchNorm2d-38 [-1, 192, 28, 28] 384
ReLU6-39 [-1, 192, 28, 28] 0
Conv2d-40 [-1, 192, 28, 28] 1,728
BatchNorm2d-41 [-1, 192, 28, 28] 384
ReLU6-42 [-1, 192, 28, 28] 0
Conv2d-43 [-1, 32, 28, 28] 6,144
BatchNorm2d-44 [-1, 32, 28, 28] 64
InvertedResidual-45 [-1, 32, 28, 28] 0
Conv2d-46 [-1, 192, 28, 28] 6,144
BatchNorm2d-47 [-1, 192, 28, 28] 384
ReLU6-48 [-1, 192, 28, 28] 0
Conv2d-49 [-1, 192, 28, 28] 1,728
BatchNorm2d-50 [-1, 192, 28, 28] 384
ReLU6-51 [-1, 192, 28, 28] 0
Conv2d-52 [-1, 32, 28, 28] 6,144
BatchNorm2d-53 [-1, 32, 28, 28] 64
InvertedResidual-54 [-1, 32, 28, 28] 0
Conv2d-55 [-1, 192, 28, 28] 6,144
BatchNorm2d-56 [-1, 192, 28, 28] 384
ReLU6-57 [-1, 192, 28, 28] 0
Conv2d-58 [-1, 192, 14, 14] 1,728
BatchNorm2d-59 [-1, 192, 14, 14] 384
ReLU6-60 [-1, 192, 14, 14] 0
Conv2d-61 [-1, 64, 14, 14] 12,288
BatchNorm2d-62 [-1, 64, 14, 14] 128
InvertedResidual-63 [-1, 64, 14, 14] 0
Conv2d-64 [-1, 384, 14, 14] 24,576
BatchNorm2d-65 [-1, 384, 14, 14] 768
ReLU6-66 [-1, 384, 14, 14] 0
Conv2d-67 [-1, 384, 14, 14] 3,456
BatchNorm2d-68 [-1, 384, 14, 14] 768
ReLU6-69 [-1, 384, 14, 14] 0
Conv2d-70 [-1, 64, 14, 14] 24,576
BatchNorm2d-71 [-1, 64, 14, 14] 128
InvertedResidual-72 [-1, 64, 14, 14] 0
Conv2d-73 [-1, 384, 14, 14] 24,576
BatchNorm2d-74 [-1, 384, 14, 14] 768
ReLU6-75 [-1, 384, 14, 14] 0
Conv2d-76 [-1, 384, 14, 14] 3,456
BatchNorm2d-77 [-1, 384, 14, 14] 768
ReLU6-78 [-1, 384, 14, 14] 0
Conv2d-79 [-1, 64, 14, 14] 24,576
BatchNorm2d-80 [-1, 64, 14, 14] 128
InvertedResidual-81 [-1, 64, 14, 14] 0
Conv2d-82 [-1, 384, 14, 14] 24,576
BatchNorm2d-83 [-1, 384, 14, 14] 768
ReLU6-84 [-1, 384, 14, 14] 0
Conv2d-85 [-1, 384, 14, 14] 3,456
BatchNorm2d-86 [-1, 384, 14, 14] 768
ReLU6-87 [-1, 384, 14, 14] 0
Conv2d-88 [-1, 64, 14, 14] 24,576
BatchNorm2d-89 [-1, 64, 14, 14] 128
InvertedResidual-90 [-1, 64, 14, 14] 0
Conv2d-91 [-1, 384, 14, 14] 24,576
BatchNorm2d-92 [-1, 384, 14, 14] 768
ReLU6-93 [-1, 384, 14, 14] 0
Conv2d-94 [-1, 384, 14, 14] 3,456
BatchNorm2d-95 [-1, 384, 14, 14] 768
ReLU6-96 [-1, 384, 14, 14] 0
Conv2d-97 [-1, 96, 14, 14] 36,864
BatchNorm2d-98 [-1, 96, 14, 14] 192
InvertedResidual-99 [-1, 96, 14, 14] 0
Conv2d-100 [-1, 576, 14, 14] 55,296
BatchNorm2d-101 [-1, 576, 14, 14] 1,152
ReLU6-102 [-1, 576, 14, 14] 0
Conv2d-103 [-1, 576, 14, 14] 5,184
BatchNorm2d-104 [-1, 576, 14, 14] 1,152
ReLU6-105 [-1, 576, 14, 14] 0
Conv2d-106 [-1, 96, 14, 14] 55,296
BatchNorm2d-107 [-1, 96, 14, 14] 192
InvertedResidual-108 [-1, 96, 14, 14] 0
Conv2d-109 [-1, 576, 14, 14] 55,296
BatchNorm2d-110 [-1, 576, 14, 14] 1,152
ReLU6-111 [-1, 576, 14, 14] 0
Conv2d-112 [-1, 576, 14, 14] 5,184
BatchNorm2d-113 [-1, 576, 14, 14] 1,152
ReLU6-114 [-1, 576, 14, 14] 0
Conv2d-115 [-1, 96, 14, 14] 55,296
BatchNorm2d-116 [-1, 96, 14, 14] 192
InvertedResidual-117 [-1, 96, 14, 14] 0
Conv2d-118 [-1, 576, 14, 14] 55,296
BatchNorm2d-119 [-1, 576, 14, 14] 1,152
ReLU6-120 [-1, 576, 14, 14] 0
Conv2d-121 [-1, 576, 7, 7] 5,184
BatchNorm2d-122 [-1, 576, 7, 7] 1,152
ReLU6-123 [-1, 576, 7, 7] 0
Conv2d-124 [-1, 160, 7, 7] 92,160
BatchNorm2d-125 [-1, 160, 7, 7] 320
InvertedResidual-126 [-1, 160, 7, 7] 0
Conv2d-127 [-1, 960, 7, 7] 153,600
BatchNorm2d-128 [-1, 960, 7, 7] 1,920
ReLU6-129 [-1, 960, 7, 7] 0
Conv2d-130 [-1, 960, 7, 7] 8,640
BatchNorm2d-131 [-1, 960, 7, 7] 1,920
ReLU6-132 [-1, 960, 7, 7] 0
Conv2d-133 [-1, 160, 7, 7] 153,600
BatchNorm2d-134 [-1, 160, 7, 7] 320
InvertedResidual-135 [-1, 160, 7, 7] 0
Conv2d-136 [-1, 960, 7, 7] 153,600
BatchNorm2d-137 [-1, 960, 7, 7] 1,920
ReLU6-138 [-1, 960, 7, 7] 0
Conv2d-139 [-1, 960, 7, 7] 8,640
BatchNorm2d-140 [-1, 960, 7, 7] 1,920
ReLU6-141 [-1, 960, 7, 7] 0
Conv2d-142 [-1, 160, 7, 7] 153,600
BatchNorm2d-143 [-1, 160, 7, 7] 320
InvertedResidual-144 [-1, 160, 7, 7] 0
Conv2d-145 [-1, 960, 7, 7] 153,600
BatchNorm2d-146 [-1, 960, 7, 7] 1,920
ReLU6-147 [-1, 960, 7, 7] 0
Conv2d-148 [-1, 960, 7, 7] 8,640
BatchNorm2d-149 [-1, 960, 7, 7] 1,920
ReLU6-150 [-1, 960, 7, 7] 0
Conv2d-151 [-1, 320, 7, 7] 307,200
BatchNorm2d-152 [-1, 320, 7, 7] 640
InvertedResidual-153 [-1, 320, 7, 7] 0
Conv2d-154 [-1, 1280, 7, 7] 409,600
BatchNorm2d-155 [-1, 1280, 7, 7] 2,560
ReLU6-156 [-1, 1280, 7, 7] 0
AdaptiveAvgPool2d-157 [-1, 1280, 1, 1] 0
Dropout-158 [-1, 1280] 0
Linear-159 [-1, 1000] 1,281,000
================================================================
Total params: 3,504,872
Trainable params: 3,504,872
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 152.88
Params size (MB): 13.37
Estimated Total Size (MB): 166.82
----------------------------------------------------------------
Process finished with exit code 0
上周我们学习了MobileNetV1和MobileNetV2,本文的MobileNetV3,它首先引入MobileNetV1的深度可分离卷积,然后引入MobileNetV2的具有线性瓶颈的倒残差结构,后来使用了网络搜索算法,并引入了SE模块以及H-Swish激活函数等,可谓集大成者。
引用大佬的描述:MobileNet V3 = MobileNet v2 + SE结构 + hard-swish activation +网络结构头尾微调。
我们展示了基于互补搜索技术和新颖架构设计相结合的下一代MobileNets。MobileNetV3通过结合硬件感知网络架构搜索(NAS)和NetAdapt算法对移动电话cpu进行调优,然后通过新的架构改进对其进行改进。本文开始探索自动化搜索算法和网络设计如何协同工作,利用互补的方法来提高整体水平。通过这个过程,我们创建了两个新的发布的MobileNet模型:MobileNetV3-Large和MobileNetV3-Small,它们针对高资源和低资源用例。然后将这些模型应用于目标检测和语义分割。针对语义分割(或任何密集像素预测)任务,我们提出了一种新的高效分割解码器Lite reduce Atrous Spatial Pyramid Pooling (LR-ASPP)。我们实现了移动分类、检测和分割的最新技术成果。与MobileNetV2相比,MobileNetV3-Large在ImageNet分类上的准确率提高了3.2%,同时延迟降低了15%。与MobileNetV2相比,MobileNetV2- small的准确率高4.6%,同时延迟降低了5%。MobileNetV3-Large检测速度比MobileNetV2快25%,在COCO检测上的精度大致相同。MobileNetV3-Large LR-ASPP的速度比MobileNetV2 R-ASPP快30%,在城市景观分割的精度类似。
本文主要工作
(1)MobileNet V3通过结合NetAdapt算法辅助的硬件NAS和新颖的架构来优化到移动端的CPU上
(2)本文创建了两个新的MobileNet模型,应用于对象检测和语义分割的任务
(3)分割任务中提出了一种高效的轻量级空间金字塔池化策略Lite Reduced ASPP
(4)与MobileNetV2相比,MobileNetV3准确率更高,速度更快
高效的神经网络在移动应用程序中变得无处不在,从而实现全新的设备上体验。它们也是个人隐私的关键推动者,允许用户获得神经网络的好处,而不需要将数据发送到服务器进行评估。神经网络效率的提高不仅通过更高的精度和更低的延迟来改善用户体验,还通过降低功耗来帮助保持电池寿命。
本文描述了我们开发MobileNetV3大型和小型模型的方法,以提供下一代高精度高效的神经网络模型来驱动设备上的计算机视觉。新的网络推动了最新技术的发展,并展示了如何将自动化搜索与新的体系结构进步结合起来,以构建有效的模型。
本文的目标是开发最佳的移动计算机视觉架构,以优化移动设备上的精确延迟交换。为了实现这一点,我们引入了(1)互补搜索技术,(2)适用于移动设备的非线性的新高效版本,(3)新的高效网络设计,(4)一个新的高效分割解码器。我们提供了深入的实验,以证明每种技术在广泛的用例和移动电话上评估的有效性和价值。
论文组织如下。我们从第2节中有关工作的讨论开始。第3节回顾了用于移动模型的高效构建块。第4节回顾了体系结构搜索以及MnasNet和NetAdapt算法的互补性。第5节描述了通过联合搜索提高模型效率的新型架构设计。第6节介绍了大量的分类、检测和分割实验,以证明有效性和理解不同元素的贡献。第7节载有结论和今后的工作。
本文目标
开发最佳的移动计算机视觉架构,以优化移动设备上的精确延迟交换
引入的技术
(1)网络模型搜索技术
(2)对于移动配置来说,新的高效版本的非线性实践
(3)新的高效网络设计
(4)新的高效分割解码器
设计深度神经网络结构来实现精度和效率之间的最优平衡是近年来一个活跃的研究领域。无论是新颖的手工结构还是算法神经结构搜索,都在这一领域发挥了重要作用。
SqueezeNet[20]广泛使用1x1卷积与挤压和扩展模块,主要集中于减少参数的数量。最近的工作将关注点从减少参数转移到减少操作的数量(MAdds)和实际测量的延迟。MobileNetV1[17]采用深度可分离卷积,大大提高了计算效率。MobileNetV2[37]在此基础上进行了扩展,引入了一个具有反向残差和线性瓶颈的资源高效块。ShuffleNet[47]利用组卷积和信道洗牌操作进一步减少MAdds。冷凝集[19]在训练阶段学习组卷积,以保持层与层之间有用的紧密连接,以便功能重用。ShiftNet[44]提出了与点向卷积交织的移位操作,以取代昂贵的空间卷积。
为了使体系结构设计过程自动化,首先引入了增强学习(RL)来搜索具有竞争力的精度的高效体系结构[51,52,3,25,33]。一个完全可配置的搜索空间可能会以指数级增长且难以处理。因此,早期的架构搜索工作主要关注单元级结构搜索,并且在所有层中重用相同的单元。最近,[41]探索了一个块级分层搜索空间,允许在网络的不同分辨率块上使用不同的层结构。为了降低搜索的计算成本,在[26,5,43]中使用了可微架构搜索框架,并进行了基于梯度的优化。针对现有网络适应受限移动平台的问题,[46,14,12]提出了更高效的自动化网络简化算法。
量化[21,23,45,39,49,50,35]是另一个重要的补充努力,以提高网络效率通过降低精度的算法。最后,知识蒸馏[4,15]提供了一种附加的补充方法,在大型“教师”网络的指导下生成精确的小型“学生”网络。
移动模式已经建立在越来越高效的基础之上。MobileNetV1[17]引入深度可分离卷积作为传统卷积层的有效替代。深度可分卷积通过将空间滤波与特征生成机制分离,有效地分解了传统卷积。深度可分卷积由两个独立的层定义:用于空间滤波的轻量级深度卷积和用于特征生成的较重的1x1点卷积。
MobileNetV2[37]引入了线性瓶颈和反转残余结构,以便利用问题的低秩性质使层结构更加有效。这个结构如图3所示,由1x1展开卷积、深度卷积和1x1投影层定义。当且仅当输入和输出具有相同数量的通道时,才用剩余连接连接它们。这种结构在输入和输出处保持了紧凑的表示,同时在内部扩展到高维特征空间,以增加非线性每个通道转换的表达能力。
MnasNet[41]建立在MobileNetV2结构上,通过在瓶颈结构中引入基于挤压和激励的轻量级注意模块。注意,与[18]中提出的基于ResNet的模块相比,挤压和激励模块集成在不同的位置。模块位于展开中的深度过滤器之后,以便注意应用于最大的表示,如图4所示。
对于MobileNetV3,我们使用这些层的组合作为构建块,以便构建最有效的模型。层也升级与修改的swish非线性[34]。挤压和激励以及swish非线性都使用了sigmoid,它的计算效率很低,而且很难在定点算法中保持精度,因此我们将其替换为硬sigmoid[2,11],如5.2节所讨论的。
之前的工作
本文的方法
(1)上述三种结构相结合构建块来实现更高效的模型
(2)引入了改进的swish作为非线性函数
(3)将Sigmoid替换为hard Sigmoid
**(1)资源受限的NAS(platform-aware NAS):**计算和参数量受限的前提下搜索网络的各个模块,所以称之为模块级的搜索(Block-wise Search)。
**(2)NetAdapt:**用于对各个模块确定之后网络层的微调。
与[41]类似,我们采用平台感知神经结构方法来寻找全局网络结构。由于我们使用相同的基于Rnn-based的控制器和相同的分解层次搜索空间,所以对于目标延迟在80ms左右的大型移动模型,我们发现了与[41]类似的结果。因此,我们只需重用与初始大型移动模型相同的MnasNet-A1[41],然后在其上应用NetAdapt[46]和其他优化。
然而,我们发现最初的奖励设计并没有针对小型手机模型进行优化。具体来说,它使用一个多目标奖励
来近似pareto最优解,根据目标延迟TAR为每个模型m平衡模型精度ACC(m)和延迟LAT(m)。我们观察到精度变化更显著延迟小模型;因此,我们需要一个较小的重量系数w =-0.15 (vs原始w=-0.07[41])来弥补大精度变化不同的延迟。在新的权重因子w的增强下,我们从头开始一个新的架构搜索,以找到初始的seed模型,然后应用NetAdapt和其他优化来获得最终的MobilenetV3-Small模型。
尝试的方法
重用了相同的MnasNet-A1模型作为初始的移动端Large模型,之后采用了NetAdapt和基于此的其它优化。
MnasNet: 移动平台感知神经结构搜索是谷歌发表在CVPR2019的一篇论文。
本篇论文提出了一个自动的移动神经网络架构搜索方法(MNAS),它明确地把模型延迟纳入考虑,得到一个在精度和延迟之间达到平衡的模型。不像之前的网络选择延迟的一个不精确的度量——比如FLOPS,而是直接通过在移动手机上推理模型来衡量真实的延迟。为了进一步在灵活性和搜索空间尺寸上取得平衡,提出了一个新颖的分解式层次搜索空间,它鼓励整个网络层的多样性。
MnasNet同样在COCO目标检测上也取得了比MobileNets更好的MAP。
不足
上述方法对小型移动端模型不是最优的,小模型的精度会随着时延变化得更剧烈。
本文改进的方法
使用w = − 0.15代替原文的w = − 0.07
从头开始一个新的架构搜索,以找到初始的 seed 模型,然后应用 NetAdapt 和其他优化来获得最终的 MobileNetV3-Small模型。
我们在架构搜索中使用的第二种技术是NetAdapt[46]。这种方法是对平台感知NAS的补充:它允许以顺序的方式对单个层进行微调,而不是试图推断出粗糙但全局的体系结构。详情请参阅原文。简而言之,这项技术的进展如下:
从平台感知NAS发现的种子网络体系结构开始。
为每个步骤:
(a)提出一套新的建议。每个建议都表示对体系结构的修改,与前一步相比,该体系结构至少可以减少延迟。
(b)对于每一个提案,我们使用前一步骤的预先训练的模型,并填充新提出的架构,适当地截断和随机初始化缺失的权重。对T步的每个建议进行微调,以获得对精度的粗略估计。
©根据某种标准选择最佳建议。
在[46]中,度量是为了最小化精度的变化。我们修改了这个算法,使延迟变化和精度变化的比例最小化。也就是说,对于每个NetAdapt步骤中生成的所有建议,我们选择一个最大化的建议:ΔACC/|Δlatency|,延迟满足2(a)中的约束。直觉告诉我们,由于我们的建议是离散的,所以我们更喜欢最大化权衡曲线斜率的建议。
这个过程重复进行,直到延迟达到目标,然后从头开始重新培训新的体系结构。我们使用与在[46]中为MobilenetV2使用相同的提案生成器。具体来说,我们允许以下两种提议:
在我们的实验中,我们使用T = 10000,并发现虽然它增加了建议的初始微调的准确性。然而,当从零开始训练时,它通常不会改变最终的精度。设δ = 0.01 ∣L∣ ,其中L为种子模型的延迟。
NetAdapt: 一种新的网络压缩算法。
NetAdapt的网络优化以自动的方式进行,它使用一个预训练好的模型在固定计算资源的手机平台上进行压缩试验,逐渐降低预训练网络的资源消耗,直接采集压缩之后的性能表现(作为feedback,得到一系列满足资源限制、同时最大化精度的简化网络。)
本文对NetAdapt的处理如下
(1)从平台感知 NAS 发现的种子网络体系结构开始
(2)对于每一个步骤:
(3)重复前面的步骤,直到达到目标延迟
两个建议
(1)减小扩展层的尺寸;
(2)保持残差连接,减小瓶颈的尺寸。
一旦通过架构搜索找到模型,我们就会发现,一些最后的层以及一些较早的层比其他层更昂贵。我们建议对体系结构进行一些修改,以减少这些慢层的延迟,同时保持准确性。这些修改超出了当前搜索空间的范围。
第一个修改将重新处理网络的最后几层是如何交互的,以便生成最终层功能更有效率。目前的模型基于MobileNetV2的倒瓶颈结构和变体,使用1x1卷积作为最后一层,以扩展到高维特征空间。这一层非常重要,因为它具有丰富的预测功能。然而,这是以额外的延迟为代价的。
为了减少延迟并保留高维特性,我们将该层移到最终的平均池之后。最后一组特征现在计算为1x1空间分辨率,而不是7x7空间分辨率。这种设计选择的结果是,在计算和延迟方面,特性的计算变得几乎是免费的。
一旦降低了该特性生成层的成本,就不再需要以前的瓶颈投影层来减少计算量。该观察允许我们删除前一个瓶颈层中的投影和过滤层,从而进一步降低计算复杂度。原始阶段和优化后的阶段如图5所示。有效的最后一个阶段将延迟减少了10毫秒,即15%的运行时间,并将操作数量减少了3000万个madd,几乎没有损失精度。第6节包含了详细的结果。
另一个昂贵的层是初始过滤器集。目前的移动模型倾向于在一个完整的3x3卷积中使用32个滤波器来构建初始滤波器库进行边缘检测。通常这些过滤器是彼此的镜像。我们尝试减少滤波器的数量,并使用不同的非线性来尝试减少冗余。我们决定对这一层使用硬swish非线性,因为它的表现和其他非线性测试。我们能够将过滤器的数量减少到16个,同时保持与使用ReLU或swish的32个过滤器相同的精度。这节省了额外的3毫秒和1000万 MAdds。
MobileNetV2的方法及不足
方法: 在最后一层使用了1×1卷积进行特征空间维度扩展。
不足: 会带来额外的延迟
本文改进的方法
(1)将该层移动到平均池化层之外。最终,维度扩展是在1×1而非7×7的分辨率上进行。
(2)使用hardswish非线性激活函数
(3)将过滤器的数量减少到 16 个
在[34]中引入了一种称为swish的非线性,当作为ReLU的替代时,它可以显著提高神经网络的精度。非线性定义为:
虽然这种非线性提高了精度,但在嵌入式环境中,它的成本是非零的,因为在移动设备上计算sigmoid函数要昂贵得多。我们用两种方法处理这个问题。
最近在[2]中也提出了类似的hard-swish版本。图6显示了sigmoid和swish非线性的软、硬版本的比较。我们选择常量的动机是简单,并且与原始的平滑版本很好地匹配。在我们的实验中,我们发现所有这些函数的硬版本在精度上没有明显的差异,但是从部署的角度来看,它们具有多种优势。首先,几乎所有的软件和硬件框架上都可以使用ReLU6的优化实现。其次,在量子化模式下,它消除了由于近似sigmoid的不同实现而带来的潜在的数值精度损失。最后,即使优化了量化的sigmoid实现,其速度也比相应的ReLU慢得多。在我们的实验中,用量化模式下的swish替换h-swish使推理延迟增加了15%。
swish是Google提出的一种非线性激活函数,在一些深层网络中优于ReLU,如在40层以上的全连接层的效果要明显优于其他激活函数。
Swish 具备无上界有下界、平滑、非单调的特性,但由于sigmoid在硬件设备上无法实现,为此将sigmoid替换为相近的基于ReLU6的计算,即h-swish。
h-swish计算速度比swish更快(但比ReLU慢),更易于量化,精度上没有差异。
而且实验发现h-swish激活在深层次网络中更有效,为此前层网络依旧使用的ReLU。
在[41]中,SE的大小与卷积瓶颈的大小有关。取而代之的是,我们将它们全部替换为固定为膨胀层通道数的1/4。我们发现这样做可以在适当增加参数数量的情况下提高精度,并且没有明显的延迟成本。
SE结构会消耗一定的时间,SE瓶颈的大小与卷积瓶颈的大小有关。将它们全部替换为固定为膨胀层通道数的1/4,可以在适当增加参数数量的情况下提高精度,并且没有明显的延迟成本。
SE模块被放在了Depthwise卷积后面。
MobileNetV3被定义为两个模型:MobileNetV3—大的和MobileNetV3—小的。这些模型分别针对高资源用例和低资源用例。通过应用平台感知的NAS和NetAdapt进行网络搜索,并结合本节定义的网络改进,可以创建模型。我们的网络的完整规范见表1和表2。
MobileNetV3 被定义为两个模型
完整网络结构
MobileNetV3-Large网络结构图
input: 输入层特征矩阵的shape
operator:
表示的是操作,对于第一个卷积层conv2d
exp size 代表的是第一个升维的卷积,我们要将维度升到多少,exp size多少,我们就用第一层1x1卷积升到多少维
#out: 代表的是输出特征矩阵的channel,我们说过在v3版本中第一个卷积核使用的是16个卷积核
NL: 代表的是激活函数,其中HS代表的是hard swish激活函数,RE代表的是ReLU激活函数
s: 代表的DW卷积的步距(s=2,长宽变为原来一半)
beneck: 对应的是下图中的结构
SE: 表示是否使用注意力机制,只要表格中标√所对应的bneck结构才会使用我们的注意力机制,对没有打√就不会使用注意力机制
MobileNetV3-Small
MobileNetV3-Small网络结构图
参数同上。
由于已经成为标准,我们在所有分类实验中都使用ImageNet[36],并将准确度与各种资源使用度量(如延迟和乘法加法(MAdds))进行比较。
我们在4x4 TPU Pod[22]上使用0.9动量的标准tensorflow RMSPropOptimizer进行同步训练。我们使用初始学习率为0.1,批次大小为4096(每个芯片128张图像),学习率衰减率为0.01每3个周期。我们使用dropout of 0.8, l2的权重衰减为1e-5。用于与Inception[40]相同的图像预处理。最后,我们使用衰减为0.9999的指数移动平均。我们所有的卷积层都使用批处理归一化层,平均衰减为0.99。
为了测量延迟,我们使用标准的谷歌像素手机,并通过标准的TFLite基准测试工具运行所有网络。我们在所有测量中都使用单线程大内核。我们没有报告多核推理时间,因为我们发现这种设置对移动应用程序不太实用。
使用了标准的Google-Pixel手机,并使用标准的TFLite-Benchmark-Tool来运行所有的网络。
贡献: 本文为TensorflowLite贡献了H-Swish的原子运算符
图9中展示了优化后的h-swish的影响
如图1所示,我们的模型优于目前的技术状态,如MnasNet[41]、ProxylessNas[5]和MobileNetV2[37]。我们在表3中报告了不同像素手机上的浮点性能。我们在表4中包括量化结果。
在图7中,我们展示了MobileNetV3性能权衡作为乘数和分辨率的函数。请注意,MobileNetV3-Small的性能比MobileNetV3-Large的性能好得多,其乘法器缩放到与性能匹配的倍数接近3%。另一方面,决议提供了比乘数更好的权衡。但是,需要注意的是,分辨率通常是由问题决定的(例如分割和检测问题通常需要更高的分辨率),因此不能总是用作可调参数。
表3展示了在不同Pixel手机上浮点运算的性能结果
表4展示了量化后的结果
结论:
图7展示了MobileNetV3作为平衡放大器和分辨率函数时的均衡性能
**结论:**本文的模型超越了现有的SOTA模型
非线性的影响,在表5和图8中,我们展示了在何处插入h-swish的决定如何影响延迟。特别重要的是,我们注意到,在整个网络上使用h-swish会导致精度略有提高(0.2),同时增加了近20%的延迟,并再次回到有效边界。
另一方面,与ReLU相比,使用h-swish提高了效率边界,尽管仍然比ReLU耗时增加12%左右。最后,我们注意到,当h-swish通过融合到卷积运算符中得到优化时,我们预计h-swish和ReLU之间的延迟差距即使没有消失,也会显著减小。然而,h-swish和swish之间不可能有这样的改进,因为计算sigmoid本来就比较昂贵。
其他组件的影响,在图9中,我们展示了不同组件的引入是如何沿着延迟/准确度曲线移动的。
(1)非线性函数的影响
图8展示了基于非线性函数选择和网络宽度的 efficient frontier
**结论:**MobileNetV3在网络的中间部分使用了H-Swish ,并且明显优于ReLU
(2)其它组件的影响
图9展示了引入不同组件后在latency/accuracy曲线上移动的情况
在SSDLite[37]中,我们使用MobileNetV3作为骨干特征提取器的替代,并与COCO dataset[24]上的其他骨干网络进行了比较。
在MobileNetV2[37]之后,我们将第一层SSDLite附加到输出步长为16的最后一个特征提取器层,并将第二层SSDLite附加到输出步长为32的最后一个特征提取器层。根据检测文献,我们将这两个特征提取层分别称为C4和C5。对于MobileNetV3-Large, C4是第13个瓶颈块的膨胀层。对于MobileNetV3-Small, C4是第9个瓶颈块的膨胀层。对于这两个网络,C5都是池之前的一层。
我们还将C4和C5之间的所有特征层的信道数减少了2。这是因为MobileNetV3的最后几层被调优为输出1000个类,当将90个类转移到COCO时,这可能是多余的。
COCO测试集的结果如表6所示。在信道缩减的情况下,MobileNetV3-Large比具有几乎相同映射的MobileNetV2快25%。在相同的延迟下,MobileNetV2和MnasNet比MobileNetV2和MnasNet高2.4和0.5。对于这两种MobileNetV3模型,通道减少技巧在没有featuremap丢失的情况下可以减少大约15%的延迟,这表明Imagenet分类和COCO对象检测可能更喜欢不同的特征提取器形状。
使用 MobileNet V3作为SSDLite的骨干特征提取器的替代,并与COCO dataset上与其他骨干网络进行了对比。
表6展示了COCO 测试集的结果
**结论:**ImageNet 分类和 COCO对象检测可能更喜欢不同的特征提取器形状。
在本小节中,我们使用MobileNetV2[37]和提出的MobileNetV3作为移动语义分割的网络骨架。此外,我们比较了两个分割头。第一个是在[37]中提出的R-ASPP。R-ASPP是一种无源空间金字塔池化模块的简化设计[7,8,9],它只采用由11个卷积和一个全局平均池化操作组成的两个分支[27,48]。在本文中,我们提出了另一种轻量级分割头,称为Lite R-ASPP(或LR-ASPP),如图10所示。Lite R-ASPP是对R-ASPP的改进,它部署全局平均池的方式类似于挤压-激励模块[18],其中我们使用了一个大的池内核,具有较大的步长(以节省一些计算),并且模块中只有一个11个卷积。我们对MobileNetV3的最后一个块应用atrous convolution[16,38,31, 6]来提取更密集的特性,并进一步从底层特性添加一个跳过连接[28]来捕获更详细的信息。
我们使用度量mIOU[13]对Cityscapes数据集[10]进行了实验,只使用了“fine”注释。我们采用与[8,37]相同的培训方案。我们所有的模型都是从零开始训练,没有在ImageNet[36]上进行预训练,并使用单尺度输入进行评估。与目标检测类似,我们发现我们可以在不显著降低性能的情况下,将网络主干最后一块的信道减少2倍。我们认为这是因为主干网设计了1000类ImageNet图像分类[36],而Cityscapes只有19类,这意味着主干网中存在一定的信道冗余。
我们在表7中报告Cityscapes验证集的结果。如表所示,我们观察到(1)减少网络骨干的渠道在最后一块2倍显著提高速度的同时保持类似的表现(第一行和第二行和行5与行6),(2)提出分割头LR-ASPP略高于R-ASPP[37]虽然性能改善(第二行和第三行,行6 vs行7),(3)减少分割的过滤器头从256年到128年提高了速度稍差的成本性能(第3行与行4,行7与行8),(4)当使用相同的设置,MobileNetV3模型变体实现类似的性能时略高于MobileNetV2同行(第1行和5行,第二行与行6,第3行与行7日和行4与行8),(5)MobileNetV3-Small达到类似性能MobileNetV2 - 0.5时更快,(6) mobilenetv2 - small明显优于MobileNetV2-0.35,且速度相近。
使用 MobileNetV2 和提出的 MobileNetV3作为移动语义分割的网络骨架。此外,比较了两个分割头。
图10展示的Lite R-ASPP是对 R-ASPP的改进
**结论:**主干网络存在一定的通道冗余。
在本文中,我们介绍了MobileNetV3大大小小的模型,展示了在移动分类、检测和分割方面的最新技术。我们描述了我们的努力,利用多种类型的网络架构搜索以及先进的网络设计,以交付下一代移动模型。我们还展示了如何适应非线性,如swish和应用压缩和激励的量化友好和有效的方式,将它们作为有效的工具引入移动模型领域。我们还介绍了一种新的轻量级分割解码器,称为LR-ASPP。尽管如何将自动搜索技术与人类直觉最好地结合起来仍然是一个悬而未决的问题,但我们很高兴地展示了这些初步的积极结果,并将在未来的工作中继续改进这些方法。
针对不同规模的嵌入式应用,本文提出了MobileNetV3-Large和MobileNetv3-Small两种轻量级模型用于图像分类、分割、物体检测的嵌入式或移动端应用。
主要创新点在于SE Module、h-swish和NAS的应用。在分割任务中还提出了新的解码结构LR-ASPP。未来还将进一步精简网络。
import torch
import torch.nn as nn
import torchvision
import torch.nn.functional as F
from torchsummary import summary
class HardSwish(nn.Module):
def __init__(self, inplace=True):
super(HardSwish, self).__init__()
self.relu6 = nn.ReLU6(inplace)
def forward(self, x):
return x*self.relu6(x+3)/6
def ConvBNActivation(in_channels,out_channels,kernel_size,stride,activate):
return nn.Sequential(
nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=(kernel_size-1)//2, groups=in_channels),
nn.BatchNorm2d(out_channels),
nn.ReLU6(inplace=True) if activate == 'relu' else HardSwish()
)
def Conv1x1BNActivation(in_channels,out_channels,activate):
return nn.Sequential(
nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1),
nn.BatchNorm2d(out_channels),
nn.ReLU6(inplace=True) if activate == 'relu' else HardSwish()
)
def Conv1x1BN(in_channels,out_channels):
return nn.Sequential(
nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1),
nn.BatchNorm2d(out_channels)
)
class SqueezeAndExcite(nn.Module):
def __init__(self, in_channels, out_channels,se_kernel_size, divide=4):
super(SqueezeAndExcite, self).__init__()
mid_channels = in_channels // divide
self.pool = nn.AvgPool2d(kernel_size=se_kernel_size,stride=1)
self.SEblock = nn.Sequential(
nn.Linear(in_features=in_channels, out_features=mid_channels),
nn.ReLU6(inplace=True),
nn.Linear(in_features=mid_channels, out_features=out_channels),
HardSwish(inplace=True),
)
def forward(self, x):
b, c, h, w = x.size()
out = self.pool(x)
out = out.view(b, -1)
out = self.SEblock(out)
out = out.view(b, c, 1, 1)
return out * x
class SEInvertedBottleneck(nn.Module):
def __init__(self, in_channels, mid_channels, out_channels, kernel_size, stride,activate, use_se, se_kernel_size=1):
super(SEInvertedBottleneck, self).__init__()
self.stride = stride
self.use_se = use_se
# mid_channels = (in_channels * expansion_factor)
self.conv = Conv1x1BNActivation(in_channels, mid_channels,activate)
self.depth_conv = ConvBNActivation(mid_channels, mid_channels, kernel_size,stride,activate)
if self.use_se:
self.SEblock = SqueezeAndExcite(mid_channels, mid_channels, se_kernel_size)
self.point_conv = Conv1x1BNActivation(mid_channels, out_channels,activate)
if self.stride == 1:
self.shortcut = Conv1x1BN(in_channels, out_channels)
def forward(self, x):
out = self.depth_conv(self.conv(x))
if self.use_se:
out = self.SEblock(out)
out = self.point_conv(out)
out = (out + self.shortcut(x)) if self.stride == 1 else out
return out
class MobileNetV3(nn.Module):
def __init__(self, num_classes=1000,type='large'):
super(MobileNetV3, self).__init__()
self.type = type
self.first_conv = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=2, padding=1),
nn.BatchNorm2d(16),
HardSwish(inplace=True),
)
if type=='large':
self.large_bottleneck = nn.Sequential(
SEInvertedBottleneck(in_channels=16, mid_channels=16, out_channels=16, kernel_size=3, stride=1,activate='relu', use_se=False),
SEInvertedBottleneck(in_channels=16, mid_channels=64, out_channels=24, kernel_size=3, stride=2, activate='relu', use_se=False),
SEInvertedBottleneck(in_channels=24, mid_channels=72, out_channels=24, kernel_size=3, stride=1, activate='relu', use_se=False),
SEInvertedBottleneck(in_channels=24, mid_channels=72, out_channels=40, kernel_size=5, stride=2,activate='relu', use_se=True, se_kernel_size=28),
SEInvertedBottleneck(in_channels=40, mid_channels=120, out_channels=40, kernel_size=5, stride=1,activate='relu', use_se=True, se_kernel_size=28),
SEInvertedBottleneck(in_channels=40, mid_channels=120, out_channels=40, kernel_size=5, stride=1,activate='relu', use_se=True, se_kernel_size=28),
SEInvertedBottleneck(in_channels=40, mid_channels=240, out_channels=80, kernel_size=3, stride=1,activate='hswish', use_se=False),
SEInvertedBottleneck(in_channels=80, mid_channels=200, out_channels=80, kernel_size=3, stride=1,activate='hswish', use_se=False),
SEInvertedBottleneck(in_channels=80, mid_channels=184, out_channels=80, kernel_size=3, stride=2,activate='hswish', use_se=False),
SEInvertedBottleneck(in_channels=80, mid_channels=184, out_channels=80, kernel_size=3, stride=1,activate='hswish', use_se=False),
SEInvertedBottleneck(in_channels=80, mid_channels=480, out_channels=112, kernel_size=3, stride=1,activate='hswish', use_se=True, se_kernel_size=14),
SEInvertedBottleneck(in_channels=112, mid_channels=672, out_channels=112, kernel_size=3, stride=1,activate='hswish', use_se=True, se_kernel_size=14),
SEInvertedBottleneck(in_channels=112, mid_channels=672, out_channels=160, kernel_size=5, stride=2,activate='hswish', use_se=True,se_kernel_size=7),
SEInvertedBottleneck(in_channels=160, mid_channels=960, out_channels=160, kernel_size=5, stride=1,activate='hswish', use_se=True,se_kernel_size=7),
SEInvertedBottleneck(in_channels=160, mid_channels=960, out_channels=160, kernel_size=5, stride=1,activate='hswish', use_se=True,se_kernel_size=7),
)
self.large_last_stage = nn.Sequential(
nn.Conv2d(in_channels=160, out_channels=960, kernel_size=1, stride=1),
nn.BatchNorm2d(960),
HardSwish(inplace=True),
nn.AvgPool2d(kernel_size=7, stride=1),
nn.Conv2d(in_channels=960, out_channels=1280, kernel_size=1, stride=1),
HardSwish(inplace=True),
)
else:
self.small_bottleneck = nn.Sequential(
SEInvertedBottleneck(in_channels=16, mid_channels=16, out_channels=16, kernel_size=3, stride=2,activate='relu', use_se=True, se_kernel_size=56),
SEInvertedBottleneck(in_channels=16, mid_channels=72, out_channels=24, kernel_size=3, stride=2,activate='relu', use_se=False),
SEInvertedBottleneck(in_channels=24, mid_channels=88, out_channels=24, kernel_size=3, stride=1,activate='relu', use_se=False),
SEInvertedBottleneck(in_channels=24, mid_channels=96, out_channels=40, kernel_size=5, stride=2,activate='hswish', use_se=True, se_kernel_size=14),
SEInvertedBottleneck(in_channels=40, mid_channels=240, out_channels=40, kernel_size=5, stride=1,activate='hswish', use_se=True, se_kernel_size=14),
SEInvertedBottleneck(in_channels=40, mid_channels=240, out_channels=40, kernel_size=5, stride=1,activate='hswish', use_se=True, se_kernel_size=14),
SEInvertedBottleneck(in_channels=40, mid_channels=120, out_channels=48, kernel_size=5, stride=1,activate='hswish', use_se=True, se_kernel_size=14),
SEInvertedBottleneck(in_channels=48, mid_channels=144, out_channels=48, kernel_size=5, stride=1,activate='hswish', use_se=True, se_kernel_size=14),
SEInvertedBottleneck(in_channels=48, mid_channels=288, out_channels=96, kernel_size=5, stride=2,activate='hswish', use_se=True, se_kernel_size=7),
SEInvertedBottleneck(in_channels=96, mid_channels=576, out_channels=96, kernel_size=5, stride=1,activate='hswish', use_se=True, se_kernel_size=7),
SEInvertedBottleneck(in_channels=96, mid_channels=576, out_channels=96, kernel_size=5, stride=1,activate='hswish', use_se=True, se_kernel_size=7),
)
self.small_last_stage = nn.Sequential(
nn.Conv2d(in_channels=96, out_channels=576, kernel_size=1, stride=1),
nn.BatchNorm2d(576),
HardSwish(inplace=True),
nn.AvgPool2d(kernel_size=7, stride=1),
nn.Conv2d(in_channels=576, out_channels=1280, kernel_size=1, stride=1),
HardSwish(inplace=True),
)
self.classifier = nn.Conv2d(in_channels=1280, out_channels=num_classes, kernel_size=1, stride=1)
def init_params(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight)
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.BatchNorm2d) or isinstance(m, nn.Linear):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def forward(self, x):
x = self.first_conv(x)
if self.type == 'large':
x = self.large_bottleneck(x)
x = self.large_last_stage(x)
else:
x = self.small_bottleneck(x)
x = self.small_last_stage(x)
out = self.classifier(x)
out = out.view(out.size(0), -1)
return out
if __name__ == '__main__':
net = MobileNetV3().cuda()
summary(net, (3, 224, 224))
网络结构打印如下:
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 16, 112, 112] 448
BatchNorm2d-2 [-1, 16, 112, 112] 32
ReLU6-3 [-1, 16, 112, 112] 0
HardSwish-4 [-1, 16, 112, 112] 0
Conv2d-5 [-1, 16, 112, 112] 272
BatchNorm2d-6 [-1, 16, 112, 112] 32
ReLU6-7 [-1, 16, 112, 112] 0
Conv2d-8 [-1, 16, 112, 112] 160
BatchNorm2d-9 [-1, 16, 112, 112] 32
ReLU6-10 [-1, 16, 112, 112] 0
Conv2d-11 [-1, 16, 112, 112] 272
BatchNorm2d-12 [-1, 16, 112, 112] 32
ReLU6-13 [-1, 16, 112, 112] 0
Conv2d-14 [-1, 16, 112, 112] 272
BatchNorm2d-15 [-1, 16, 112, 112] 32
SEInvertedBottleneck-16 [-1, 16, 112, 112] 0
Conv2d-17 [-1, 64, 112, 112] 1,088
BatchNorm2d-18 [-1, 64, 112, 112] 128
ReLU6-19 [-1, 64, 112, 112] 0
Conv2d-20 [-1, 64, 56, 56] 640
BatchNorm2d-21 [-1, 64, 56, 56] 128
ReLU6-22 [-1, 64, 56, 56] 0
Conv2d-23 [-1, 24, 56, 56] 1,560
BatchNorm2d-24 [-1, 24, 56, 56] 48
ReLU6-25 [-1, 24, 56, 56] 0
SEInvertedBottleneck-26 [-1, 24, 56, 56] 0
Conv2d-27 [-1, 72, 56, 56] 1,800
BatchNorm2d-28 [-1, 72, 56, 56] 144
ReLU6-29 [-1, 72, 56, 56] 0
Conv2d-30 [-1, 72, 56, 56] 720
BatchNorm2d-31 [-1, 72, 56, 56] 144
ReLU6-32 [-1, 72, 56, 56] 0
Conv2d-33 [-1, 24, 56, 56] 1,752
BatchNorm2d-34 [-1, 24, 56, 56] 48
ReLU6-35 [-1, 24, 56, 56] 0
Conv2d-36 [-1, 24, 56, 56] 600
BatchNorm2d-37 [-1, 24, 56, 56] 48
SEInvertedBottleneck-38 [-1, 24, 56, 56] 0
Conv2d-39 [-1, 72, 56, 56] 1,800
BatchNorm2d-40 [-1, 72, 56, 56] 144
ReLU6-41 [-1, 72, 56, 56] 0
Conv2d-42 [-1, 72, 28, 28] 1,872
BatchNorm2d-43 [-1, 72, 28, 28] 144
ReLU6-44 [-1, 72, 28, 28] 0
AvgPool2d-45 [-1, 72, 1, 1] 0
Linear-46 [-1, 18] 1,314
ReLU6-47 [-1, 18] 0
Linear-48 [-1, 72] 1,368
ReLU6-49 [-1, 72] 0
HardSwish-50 [-1, 72] 0
SqueezeAndExcite-51 [-1, 72, 28, 28] 0
Conv2d-52 [-1, 40, 28, 28] 2,920
BatchNorm2d-53 [-1, 40, 28, 28] 80
ReLU6-54 [-1, 40, 28, 28] 0
SEInvertedBottleneck-55 [-1, 40, 28, 28] 0
Conv2d-56 [-1, 120, 28, 28] 4,920
BatchNorm2d-57 [-1, 120, 28, 28] 240
ReLU6-58 [-1, 120, 28, 28] 0
Conv2d-59 [-1, 120, 28, 28] 3,120
BatchNorm2d-60 [-1, 120, 28, 28] 240
ReLU6-61 [-1, 120, 28, 28] 0
AvgPool2d-62 [-1, 120, 1, 1] 0
Linear-63 [-1, 30] 3,630
ReLU6-64 [-1, 30] 0
Linear-65 [-1, 120] 3,720
ReLU6-66 [-1, 120] 0
HardSwish-67 [-1, 120] 0
SqueezeAndExcite-68 [-1, 120, 28, 28] 0
Conv2d-69 [-1, 40, 28, 28] 4,840
BatchNorm2d-70 [-1, 40, 28, 28] 80
ReLU6-71 [-1, 40, 28, 28] 0
Conv2d-72 [-1, 40, 28, 28] 1,640
BatchNorm2d-73 [-1, 40, 28, 28] 80
SEInvertedBottleneck-74 [-1, 40, 28, 28] 0
Conv2d-75 [-1, 120, 28, 28] 4,920
BatchNorm2d-76 [-1, 120, 28, 28] 240
ReLU6-77 [-1, 120, 28, 28] 0
Conv2d-78 [-1, 120, 28, 28] 3,120
BatchNorm2d-79 [-1, 120, 28, 28] 240
ReLU6-80 [-1, 120, 28, 28] 0
AvgPool2d-81 [-1, 120, 1, 1] 0
Linear-82 [-1, 30] 3,630
ReLU6-83 [-1, 30] 0
Linear-84 [-1, 120] 3,720
ReLU6-85 [-1, 120] 0
HardSwish-86 [-1, 120] 0
SqueezeAndExcite-87 [-1, 120, 28, 28] 0
Conv2d-88 [-1, 40, 28, 28] 4,840
BatchNorm2d-89 [-1, 40, 28, 28] 80
ReLU6-90 [-1, 40, 28, 28] 0
Conv2d-91 [-1, 40, 28, 28] 1,640
BatchNorm2d-92 [-1, 40, 28, 28] 80
SEInvertedBottleneck-93 [-1, 40, 28, 28] 0
Conv2d-94 [-1, 240, 28, 28] 9,840
BatchNorm2d-95 [-1, 240, 28, 28] 480
ReLU6-96 [-1, 240, 28, 28] 0
HardSwish-97 [-1, 240, 28, 28] 0
Conv2d-98 [-1, 240, 28, 28] 2,400
BatchNorm2d-99 [-1, 240, 28, 28] 480
ReLU6-100 [-1, 240, 28, 28] 0
HardSwish-101 [-1, 240, 28, 28] 0
Conv2d-102 [-1, 80, 28, 28] 19,280
BatchNorm2d-103 [-1, 80, 28, 28] 160
ReLU6-104 [-1, 80, 28, 28] 0
HardSwish-105 [-1, 80, 28, 28] 0
Conv2d-106 [-1, 80, 28, 28] 3,280
BatchNorm2d-107 [-1, 80, 28, 28] 160
SEInvertedBottleneck-108 [-1, 80, 28, 28] 0
Conv2d-109 [-1, 200, 28, 28] 16,200
BatchNorm2d-110 [-1, 200, 28, 28] 400
ReLU6-111 [-1, 200, 28, 28] 0
HardSwish-112 [-1, 200, 28, 28] 0
Conv2d-113 [-1, 200, 28, 28] 2,000
BatchNorm2d-114 [-1, 200, 28, 28] 400
ReLU6-115 [-1, 200, 28, 28] 0
HardSwish-116 [-1, 200, 28, 28] 0
Conv2d-117 [-1, 80, 28, 28] 16,080
BatchNorm2d-118 [-1, 80, 28, 28] 160
ReLU6-119 [-1, 80, 28, 28] 0
HardSwish-120 [-1, 80, 28, 28] 0
Conv2d-121 [-1, 80, 28, 28] 6,480
BatchNorm2d-122 [-1, 80, 28, 28] 160
SEInvertedBottleneck-123 [-1, 80, 28, 28] 0
Conv2d-124 [-1, 184, 28, 28] 14,904
BatchNorm2d-125 [-1, 184, 28, 28] 368
ReLU6-126 [-1, 184, 28, 28] 0
HardSwish-127 [-1, 184, 28, 28] 0
Conv2d-128 [-1, 184, 14, 14] 1,840
BatchNorm2d-129 [-1, 184, 14, 14] 368
ReLU6-130 [-1, 184, 14, 14] 0
HardSwish-131 [-1, 184, 14, 14] 0
Conv2d-132 [-1, 80, 14, 14] 14,800
BatchNorm2d-133 [-1, 80, 14, 14] 160
ReLU6-134 [-1, 80, 14, 14] 0
HardSwish-135 [-1, 80, 14, 14] 0
SEInvertedBottleneck-136 [-1, 80, 14, 14] 0
Conv2d-137 [-1, 184, 14, 14] 14,904
BatchNorm2d-138 [-1, 184, 14, 14] 368
ReLU6-139 [-1, 184, 14, 14] 0
HardSwish-140 [-1, 184, 14, 14] 0
Conv2d-141 [-1, 184, 14, 14] 1,840
BatchNorm2d-142 [-1, 184, 14, 14] 368
ReLU6-143 [-1, 184, 14, 14] 0
HardSwish-144 [-1, 184, 14, 14] 0
Conv2d-145 [-1, 80, 14, 14] 14,800
BatchNorm2d-146 [-1, 80, 14, 14] 160
ReLU6-147 [-1, 80, 14, 14] 0
HardSwish-148 [-1, 80, 14, 14] 0
Conv2d-149 [-1, 80, 14, 14] 6,480
BatchNorm2d-150 [-1, 80, 14, 14] 160
SEInvertedBottleneck-151 [-1, 80, 14, 14] 0
Conv2d-152 [-1, 480, 14, 14] 38,880
BatchNorm2d-153 [-1, 480, 14, 14] 960
ReLU6-154 [-1, 480, 14, 14] 0
HardSwish-155 [-1, 480, 14, 14] 0
Conv2d-156 [-1, 480, 14, 14] 4,800
BatchNorm2d-157 [-1, 480, 14, 14] 960
ReLU6-158 [-1, 480, 14, 14] 0
HardSwish-159 [-1, 480, 14, 14] 0
AvgPool2d-160 [-1, 480, 1, 1] 0
Linear-161 [-1, 120] 57,720
ReLU6-162 [-1, 120] 0
Linear-163 [-1, 480] 58,080
ReLU6-164 [-1, 480] 0
HardSwish-165 [-1, 480] 0
SqueezeAndExcite-166 [-1, 480, 14, 14] 0
Conv2d-167 [-1, 112, 14, 14] 53,872
BatchNorm2d-168 [-1, 112, 14, 14] 224
ReLU6-169 [-1, 112, 14, 14] 0
HardSwish-170 [-1, 112, 14, 14] 0
Conv2d-171 [-1, 112, 14, 14] 9,072
BatchNorm2d-172 [-1, 112, 14, 14] 224
SEInvertedBottleneck-173 [-1, 112, 14, 14] 0
Conv2d-174 [-1, 672, 14, 14] 75,936
BatchNorm2d-175 [-1, 672, 14, 14] 1,344
ReLU6-176 [-1, 672, 14, 14] 0
HardSwish-177 [-1, 672, 14, 14] 0
Conv2d-178 [-1, 672, 14, 14] 6,720
BatchNorm2d-179 [-1, 672, 14, 14] 1,344
ReLU6-180 [-1, 672, 14, 14] 0
HardSwish-181 [-1, 672, 14, 14] 0
AvgPool2d-182 [-1, 672, 1, 1] 0
Linear-183 [-1, 168] 113,064
ReLU6-184 [-1, 168] 0
Linear-185 [-1, 672] 113,568
ReLU6-186 [-1, 672] 0
HardSwish-187 [-1, 672] 0
SqueezeAndExcite-188 [-1, 672, 14, 14] 0
Conv2d-189 [-1, 112, 14, 14] 75,376
BatchNorm2d-190 [-1, 112, 14, 14] 224
ReLU6-191 [-1, 112, 14, 14] 0
HardSwish-192 [-1, 112, 14, 14] 0
Conv2d-193 [-1, 112, 14, 14] 12,656
BatchNorm2d-194 [-1, 112, 14, 14] 224
SEInvertedBottleneck-195 [-1, 112, 14, 14] 0
Conv2d-196 [-1, 672, 14, 14] 75,936
BatchNorm2d-197 [-1, 672, 14, 14] 1,344
ReLU6-198 [-1, 672, 14, 14] 0
HardSwish-199 [-1, 672, 14, 14] 0
Conv2d-200 [-1, 672, 7, 7] 17,472
BatchNorm2d-201 [-1, 672, 7, 7] 1,344
ReLU6-202 [-1, 672, 7, 7] 0
HardSwish-203 [-1, 672, 7, 7] 0
AvgPool2d-204 [-1, 672, 1, 1] 0
Linear-205 [-1, 168] 113,064
ReLU6-206 [-1, 168] 0
Linear-207 [-1, 672] 113,568
ReLU6-208 [-1, 672] 0
HardSwish-209 [-1, 672] 0
SqueezeAndExcite-210 [-1, 672, 7, 7] 0
Conv2d-211 [-1, 160, 7, 7] 107,680
BatchNorm2d-212 [-1, 160, 7, 7] 320
ReLU6-213 [-1, 160, 7, 7] 0
HardSwish-214 [-1, 160, 7, 7] 0
SEInvertedBottleneck-215 [-1, 160, 7, 7] 0
Conv2d-216 [-1, 960, 7, 7] 154,560
BatchNorm2d-217 [-1, 960, 7, 7] 1,920
ReLU6-218 [-1, 960, 7, 7] 0
HardSwish-219 [-1, 960, 7, 7] 0
Conv2d-220 [-1, 960, 7, 7] 24,960
BatchNorm2d-221 [-1, 960, 7, 7] 1,920
ReLU6-222 [-1, 960, 7, 7] 0
HardSwish-223 [-1, 960, 7, 7] 0
AvgPool2d-224 [-1, 960, 1, 1] 0
Linear-225 [-1, 240] 230,640
ReLU6-226 [-1, 240] 0
Linear-227 [-1, 960] 231,360
ReLU6-228 [-1, 960] 0
HardSwish-229 [-1, 960] 0
SqueezeAndExcite-230 [-1, 960, 7, 7] 0
Conv2d-231 [-1, 160, 7, 7] 153,760
BatchNorm2d-232 [-1, 160, 7, 7] 320
ReLU6-233 [-1, 160, 7, 7] 0
HardSwish-234 [-1, 160, 7, 7] 0
Conv2d-235 [-1, 160, 7, 7] 25,760
BatchNorm2d-236 [-1, 160, 7, 7] 320
SEInvertedBottleneck-237 [-1, 160, 7, 7] 0
Conv2d-238 [-1, 960, 7, 7] 154,560
BatchNorm2d-239 [-1, 960, 7, 7] 1,920
ReLU6-240 [-1, 960, 7, 7] 0
HardSwish-241 [-1, 960, 7, 7] 0
Conv2d-242 [-1, 960, 7, 7] 24,960
BatchNorm2d-243 [-1, 960, 7, 7] 1,920
ReLU6-244 [-1, 960, 7, 7] 0
HardSwish-245 [-1, 960, 7, 7] 0
AvgPool2d-246 [-1, 960, 1, 1] 0
Linear-247 [-1, 240] 230,640
ReLU6-248 [-1, 240] 0
Linear-249 [-1, 960] 231,360
ReLU6-250 [-1, 960] 0
HardSwish-251 [-1, 960] 0
SqueezeAndExcite-252 [-1, 960, 7, 7] 0
Conv2d-253 [-1, 160, 7, 7] 153,760
BatchNorm2d-254 [-1, 160, 7, 7] 320
ReLU6-255 [-1, 160, 7, 7] 0
HardSwish-256 [-1, 160, 7, 7] 0
Conv2d-257 [-1, 160, 7, 7] 25,760
BatchNorm2d-258 [-1, 160, 7, 7] 320
SEInvertedBottleneck-259 [-1, 160, 7, 7] 0
Conv2d-260 [-1, 960, 7, 7] 154,560
BatchNorm2d-261 [-1, 960, 7, 7] 1,920
ReLU6-262 [-1, 960, 7, 7] 0
HardSwish-263 [-1, 960, 7, 7] 0
AvgPool2d-264 [-1, 960, 1, 1] 0
Conv2d-265 [-1, 1280, 1, 1] 1,230,080
ReLU6-266 [-1, 1280, 1, 1] 0
HardSwish-267 [-1, 1280, 1, 1] 0
Conv2d-268 [-1, 1000, 1, 1] 1,281,000
================================================================
Total params: 5,589,150
Trainable params: 5,589,150
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 153.55
Params size (MB): 21.32
Estimated Total Size (MB): 175.44
----------------------------------------------------------------
MobileNetV1 | MobileNetV2 | MobileNetV3 |
---|---|---|
标准卷积改为深度可分离卷积,降低计算量;ReLU改为ReLU6;引入Width Multiplier(α)和Resolution Multiplier(ρ),调节模型的宽度(卷积核个数)和图像分辨率; | **采用线性瓶颈层:**将深度可分离卷积中的1×1卷积后的ReLU替换成线性激活函数;**采用反向残差结构:**引入了Expansion layer,在进行深度分离卷积之前首先使用1×1卷积进行升维;引入Shortcut结构,在升维的1×1卷积之前与深度可分离卷积中的1×1卷积之后进行shortcut连接; | 采用增加了SE机制的Bottleneck模块结构;使用了一种新的激活函数h-swish(x)替代MobileNetV2中的ReLU6激活函数;网络结构搜索中,结合两种技术:资源受限的NAS(platform-aware NAS)与NetAdapt;修改了MobileNetV2网络端部最后阶段; |