(三)MobileNet_v2----2018CVPR论文解读

MobileNetV2: Inverted Residuals and Linear Bottlenecks

MobileNetV2:残差和线性瓶颈

Abstract

在本文中,我们描述了一种新的移动体系结构MobileNetV2,该体系结构可提高移动模型在多个任务和基准以及跨不同模型大小的范围内的最新性能。我们还描述了在称为SSDLite的新颖框架中将这些移动模型应用于对象检测的有效方法。此外,我们演示了如何通过简化形式的DeepLabv3(我们称为Mobile DeepLabv3)构建移动语义细分模型。

基于反向残差结构,其中快捷方式连接位于薄瓶颈层之间。中间扩展层使用轻量级的深度卷积来过滤作为非线性源的特征。另外,我们发现删除窄层中的非线性以保持代表性很重要。我们证明了这样做可以提高性能,并提供导致这种设计的直觉。

最后,我们的方法允许将输入/输出域与转换的表达方式分离,这为进一步分析提供了方便的框架。我们在ImageNet [1]分类,COCO对象检测[2],VOC图像分割[3]上衡量我们的性能。我们评估精度与乘加(MAdd)度量的操作数,实际延迟以及参数数之间的权衡。

1 Introduction

神经网络彻底改变了机器智能的许多领域,从而使超人的准确性可用于具有挑战性的图像识别任务。但是,提高准确性的驱动力通常是有代价的:现代最先进的网络需要超出许多移动和嵌入式应用程序能力的大量计算资源。

本文介绍了一种新的神经网络体系结构,该体系结构专门针对移动和资源受限的环境而量身定制。我们的网络通过显着减少所需的操作和内存数量,同时保持相同的精度,推动了移动量身定制的计算机视觉模型的发展。

我们的主要贡献是新颖的图层模块:具有线性瓶颈的倒置残差。该模块将低维压缩表示作为输入,首先将其压缩为高维并使用轻量级的深度卷积进行过滤。随后将特征投影回具有线性卷积的低维表示。官方实现可在[4]中作为TensorFlow-Slim模型库的一部分获得。

该模块可以使用任何现代框架中的标准操作来有效地实现,并允许我们的模型使用标准基准在多个性能点上超越最新技术。此外,该卷积模块特别适合于移动设计,因为它可以通过从未完全实现大的中间张量来显着减少推理过程中所需的存储空间。这减少了许多嵌入式硬件设计中对主存储器访问的需求,这些设计提供了少量非常快的软件控制的高速缓存。

2 Related Work

在过去的几年中,调整深层神经体系结构以在准确性和性能之间达到最佳平衡一直是活跃的研究领域。由众多团队进行的手动体系结构搜索和训练算法的改进都导致了对早期设计(如AlexNet [5],VGGNet [6],GoogLeNet [7])的显着改进。和ResNet [8]。最近,算法体系结构探索方面取得了许多进展,包括超参数优化[9,10,11]以及各种arXiv:1801.04381v4 [cs.CV] 2019年3月21日网络修剪的方法[12,13,14,15, 16,16,17]和连通性学习[18,19]。大量工作还致力于改变内部卷积块的连通性结构,例如在ShuffleNet [20]中或引入稀疏性[21]和其他[22]。

最近,[23,24,25,26]开辟了一个新方向,将包括遗传算法和强化学习在内的优化方法引入建筑搜索。但是,缺点之一是最终的网络非常复杂。在本文中,我们追求的目标是更好地了解神经网络的运行方式,并以此来指导最简单的网络设计。我们的方法应被视为[23]和相关工作中描述的方法的补充。在这种情况下,我们的方法类似于[20,22]所采用的方法,可以进一步提高性能,同时可以一窥其内部操作。我们的网络设计基于MobileNetV1 [27]。它保留了其简单性,并且不需要任何特殊的运算符,同时极大地提高了其准确性,实现了针对移动应用程序的多个图像分类和检测任务的最新技术。

3 Preliminaries, discussion and intuition

3.1. Depthwise Separable Convolutions

**深度可分离卷积是许多有效的神经网络体系结构的关键组成部分[27、28、20],我们在当前工作中也使用它们。**基本思想是用分解卷积的运算符代替完整的卷积算子,该分解器将卷积分为两个单独的层。第一层称为深度卷积,它通过对每个输入通道应用单个卷积滤波器来执行轻量级滤波。第二层是1×1卷积,称为逐点卷积,它负责通过计算输入通道的线性组合来构建新特征。

标准卷积采用hi×wi×diinput张量Li,并应用卷积核K∈Rk×k×di×dj产生hi×wi×djoutput张量Lj。标准卷积层的计算成本为hi·wi·di·dj·k·k。

深度可分离卷积是标准卷积层的直接替代。从经验上讲,它们的工作原理几乎与常规卷积一样好,但仅需付出代价:
h i w i d i ( k 2 + d j ) 1 h_{i} \cdot w_{i} \cdot d_{i}\left(k^{2}+d_{j}\right) (1)
这是深度卷积和1×1点卷积的总和。与传统层相比,有效的深度可分离卷积将计算量减少了几乎k21倍。 MobileNetV2使用k = 3(3×3深度可分离卷积),因此计算成本是标准卷积的8到9倍,而精度却只有很小的降低[27]。

3.2. Linear Bottlenecks

考虑一个由多层Lieach组成的深层神经网络,其中的Lieach具有尺寸为hi×wi×di的激活张量。在本节中,我们将讨论这些激活张量的基本属性,我们将它们视作具有高维数的hi×wi“像素”的容器。非正式地说,对于一组真实图像的输入,我们说这组层激活(对于任何Li层)形成“感兴趣的流形”。长期以来,人们一直认为神经网络中感兴趣的流形可以嵌入到低维子空间中。换句话说,当我们查看一个深卷积层的所有单个d通道像素时,这些值中编码的信息实际上位于某个流形中,而这些流又可以嵌入到低维子空间中2。

乍一看,然后可以通过简单地减小层的尺寸从而减小操作空间的尺寸来捕获和利用这样的事实。这已被MobileNetV1成功利用[27]通过宽度乘数参数有效地在计算和精度之间进行权衡,并且也已被并入其他网络的有效模型设计中[20]。按照这种直觉,宽度乘数法允许减小激活空间的维数,直到感兴趣的歧管跨越整个空间。但是,当我们回想起深度卷积神经网络实际上具有非线性的每个坐标转换(例如ReLU)时,这种直觉就破裂了。例如,将ReLU应用于一维空间中的线会产生“射线”,而在Rnspace中,它通常会生成具有n个接头的分段线性曲线。
在这里插入图片描述
容易看出,通常,如果层变换的结果ReLU(Bx)具有非零的体积S,则映射到internalS的点是通过输入的线性变换B获得的,因此表明 对应于一维输出的输入空间仅限于线性变换。换句话说,深层网络仅在输出域的非零体积部分具有线性分类器的功能。 我们参考补充材料以获得更正式的说明。

另一方面,当ReLU使通道崩溃时,它不可避免地会丢失该通道中的信息。但是,如果我们有很多通道,并且激活歧管中有一个结构,那么信息可能仍会保留在其他通道中。在补充材料中,我们表明,如果可以将输入流形嵌入到激活空间的一个明显较低维度的子空间中,那么ReLU转换将保留信息,同时将所需的复杂性引入可表达的函数集中。

总而言之,我们重点介绍了两个属性,这些属性指示所需的流形应位于高维激活空间的低维子空间中:

1.如果感兴趣的流形在ReLU转换后仍保持非零体积,它对应于线性变换。

2.ReLU能够保留有关输入歧管的完整信息,但前提是输入歧管位于输入空间的低维子空间中。
在这里插入图片描述
这两个见解为我们提供了优化现有神经体系结构的经验提示:假设感兴趣的流形是低维的,我们可以通过将线性瓶颈层插入到卷积块中来捕获这一点。实验证据表明,使用线性层至关重要,因为它可以防止非线性破坏过多的信息。在第6节中,我们通过经验证明,在瓶颈中使用非线性层确实会降低性能百分之几,从而进一步验证了我们的假设3。我们注意到,在[29]中报道了类似的报告,其中非线性得到了帮助,其中从传统残差块的输入中去除了非线性,从而改善了CIFAR数据集的性能。

在本文的其余部分,我们将利用瓶颈卷积。我们将输入瓶颈的大小与内部大小之间的比率称为扩展比率。

3.3. Inverted residuals

瓶颈块看起来类似于残余块,其中每个块包含一个输入,然后是多个瓶颈,然后是扩展[8]。然而,受直觉的启发,瓶颈实际上包含所有必要的信息,而扩展层仅充当张量的非线性转换的实现细节,我们直接在瓶颈之间使用快捷方式。

图3提供了设计差异的示意图。插入快捷方式的动机与经典残差连接的动机相似:我们要提高渐变跨乘数层传播的能力。但是,反向设计的内存效率要高得多(有关详细信息,请参见第5节),并且在我们的实验中效果更好。

瓶颈卷积的运行时间和参数计数 基本实现结构如表1所示。对于大小为h×w,扩展因子为t且内核大小为k且具有d0输入通道和d00输出通道的块,所需的乘加总数为h ·w·d0·t(d0 + k2 + d00)。与(1)相比,此表达式有一个附加项,因为确实有一个额外的1×1卷积,但是我们网络的性质使我们可以利用更小的输入和输出维度。在表3中,我们比较了MobileNetV1,MobileNetV2和ShuffleNet每种分辨率所需的大小。

3.4. Information flow interpretation

我们的体系结构的一个有趣的特性是,它在构件块(瓶颈层)的输入/输出域与层转换之间提供了自然的分隔,层转换是将输入转换为输出的非线性函数。前者可以看作是网络在每一层的容量,而后者则是表现力。这与常规的和可分离的传统卷积块相反,在传统的卷积块中,表现力和容量都纠缠在一起,并且是输出层深度的函数。

特别是在我们的情况下,当内层深度为0时,由于快捷连接,底层的卷积就是身份函数。当扩展比小于1时,这是经典的残差卷积块[8,30]。但是,出于我们的目的,我们表明大于1的扩展比是最有用的。这

种解释使我们能够将网络的表现力与容量分开研究,并且我们认为有必要进一步研究这种分离,以更好地了解网络的性质。

4 Model Architecture

在这里插入图片描述
现在,我们详细描述我们的体系结构。 如前一部分所述,基本构建块是具有残差的瓶颈深度可分离卷积。 表1中显示了此模块的详细结构。MobileNetV2的体系结构包含具有32个滤波器的初始全卷积层,以及表2中所述的19个残留瓶颈层。由于其鲁棒性,我们使用ReLU6作为非线性。 当与低精度计算一起使用时[27]。 我们始终使用3×3的内核大小作为现代网络的标准,并在训练过程中利用丢包和批处理规范化。

除第一层外,我们在整个网络中使用恒定的扩展速率。在我们的实验中,我们发现5到10之间的扩展速率会导致几乎相同的性能曲线,较小的网络在较小的扩展速率下效果更好,而较大的网络在较大的扩展速率下性能稍好。

对于我们所有的主要实验,我们使用输入因子张量的扩展因子6。例如,对于采用64通道输入张量并产生具有128通道张量的瓶颈层,则中间扩展层为64·6 = 384通道。

权衡的超参数如[27]中所述,通过使用输入图像分辨率和宽度乘数作为可调超参数,我们可以针对不同的性能点调整体系结构,可以根据所需的精度/性能权衡进行调整。我们的主要网络(宽度乘数1,224×224)的计算成本为3亿个乘法加法,并使用了340万个参数。我们探讨了性能折衷,对于96至224的输入分辨率和0.35至1.4的宽度乘数。网络计算成本范围从7乘加到585M MAdd,而模型大小在1.7M和6.9M参数之间变化。
在这里插入图片描述
在这里插入图片描述
与[27]相比,一个较小的实现差异是对于小于一的乘数,除最后一个卷积层外,我们将宽度乘数应用于所有层。这提高了较小型号的性能。

5 Implementation Notes

5.1. Memory efficient inference

在这里插入图片描述
倒置的残留瓶颈层允许特别有效的内存实现,这对于移动应用程序非常重要。** 使用例如TensorFlow [31]或Caffe [32]的推理的标准有效实现,构建有向非循环计算超图G,其由代表操作的边和代表中间计算的张量的节点组成。 为了使需要存储在存储器中的张量总数最小化,对计算进行了调度。 在最一般的情况下,它会搜索所有可能的计算阶数Σ(G),然后选择最小化的阶数
M ( G ) = min π S ( G ) max i 1 , m [ A R ( i , π , G ) A ] + size ( π i ) M(G)=\min _{\pi \in \mathbb{S}(G)} \max _{i \in 1, m}\left[\sum_{A \in R(i, \pi, G)}|A|\right]+\operatorname{size}\left(\pi_{i}\right)
其中R(i,π,G)是连接到πi的中间张量的列表。 。 。 πn个节点,| A | 表示张量A的大小,而size(i)是操作i期间内部存储所需的内存总量。

对于仅具有平凡并行结构(例如残差连接)的图,只有一个不平凡的可行计算顺序,因此可以简化推理计算图G所需的总量和内存上的界限:
M ( G ) = max o p G [ A o p i n p A + B  op out  B + o p ] 2 M(G)=\max _{o p \in G}\left[\sum_{A \in \mathrm{op}_{\mathrm{inp}}}|A|+\sum_{B \in \text { op out }}|B|+|o p|\right] (2)
还是要重申一下,内存量只是所有操作中组合的输入和输出的最大总大小。接下来的内容表明,如果将瓶颈残差块视为单个操作(并将内部卷积视为一次性张量),则总内存量将由瓶颈张量的大小决定,而不是由张量的大小决定。是瓶颈的内部(甚至更大)。

瓶颈残差块 图3b中所示的瓶颈块算子F(x)可以表示为三个算子F(x)= [A◦N◦B] x的组成,其中A是线性变换A:Rs×s× k→Rs×s×n,N是每个通道的非线性变换:N:Rs×s×n→Rs0×s0×n,B再次是对输出域的线性变换:B:Rs0×s0 ×n→Rs0×s0×k0。

对于我们的网络,N = ReLU6◦dwise◦ReLU6,但结果适用于任何每通道转换。假设输入域的大小为| x |。并且输出域的大小为| y |,则计算F(X)所需的内存可以低至| s2k |。 + | s02k0 | + O(max(s2,s02))。

该算法基于以下事实:内部张量I可以表示为t个张量的串联,每个张量为n / t,然后我们的函数可以表示为
F ( x ) = i = 1 ( A i N B i ) ( x ) \mathcal{F}(x)=\sum_{i=1}^{\ell}\left(A_{i} \circ N \circ B_{i}\right)(x)
通过累加和,我们仅需要始终将一个大小为n / t的中间块保留在内存中。使用n = t,我们最终必须始终保持中间表示的单个通道。使我们能够使用此技巧的两个约束是(a)内部变换(包括非线性和深度方向)是按通道的事实,以及(b)连续的非按通道运算符的显着比率为输入大小到输出。对于大多数传统的神经网络,这种技巧不会产生明显的改善。

我们注意到,使用t路分割来计算F(X)所需的乘法加法运算符的数量与t无关,但是在现有实现中,我们发现用几个较小的乘法代替一个矩阵乘法会因运行时内部运算而损害运行时性能。 增加的缓存未命中。 我们发现这种方法最有用,因为t是2到5之间的一个小常数。它显着降低了内存需求,但仍然允许人们利用通过高度优化的矩阵乘法和卷积运算符获得的大部分效率 由深度学习框架提供。 特殊的框架级别优化是否可以导致进一步的运行时改进还有待观察。
在这里插入图片描述

6 Experiments

6.1. ImageNet Classification

训练设置 我们使用TensorFlow [31]训练模型。我们使用衰减和动量均设置为0.9的标准RMSPropOptimizer。我们在每一层之后都使用批量归一化,并且标准权重衰减设置为0.00004。遵循MobileNetV1 [27]设置后,我们使用0.045的初始学习速率,每个时期的学习速率衰减速率为0.98。我们使用16个GPU异步工作程序,并且批处理大小为96。

结果 我们将网络与MobileNetV1,ShuffleNet和NASNet-A模型进行了比较。表4中显示了一些选定模型的统计数据,图5中显示了完整的性能图。

6.2. Object Detection

我们评估并比较了MobileNetV2和MobileNetV1作为特征提取器[33]的性能,以及对COCO数据集[2]的单次检测器(SSD)[34]的修改版本。我们还将YOLOv2 [35]和原始SSD(以VGG-16 [6]作为基础网络)作为基准进行比较。由于我们专注于移动/实时模型,因此我们无法将其性能与其他架构(例如Faster-RCNN [36]和RFCN [37])进行比较。

SSDLite:在本文中,我们介绍了常规SSD的移动友好型。我们在SSD预测层中将所有常规卷积替换为可分离的卷积(深度后跟1×1投影)。此设计与MobileNets的总体设计一致,并且在计算效率上要高得多。我们将此修改版本称为SSDLite。与常规SSD相比,SSDLite大大减少了参数数量和计算成本,如表5所示。对于MobileNetV1,我们遵循[33]中的设置。

对于MobileNetV2,SSDLite的第一层连接到第15层的扩展(输出跨度为16)。 SSDLite层的第二层和最顶层位于最后一层的顶部(输出步幅为32)。此设置与MobileNetV1一致,因为所有图层都附加到相同输出步幅的要素图上。
在这里插入图片描述
在这里插入图片描述
这两个MobileNet模型都使用开源TensorFlow对象检测API [38]进行了训练和评估。两种模型的输入分辨率均为320×320。我们对mAP(COCO挑战指标),参数数量和乘加数量进行基准测试和比较。结果显示在表6中。MobileNetV2 SSDLite不仅是效率最高的模型,而且还是这三种模型中最精确的模型。值得注意的是,MobileNetV2 SSDLite的效率提高了20倍,而体积却缩小了10倍,但在COCO数据集上仍胜过YOLOv2。

6.3. Semantic Segmentation

在本节中,我们将用作特征提取器的MobileNetV1和MobileNetV2模型与DeepLabv3 [39]进行了比较,以完成移动语义分割的任务。 DeepLabv3采用了atrous卷积[40、41、42],这是一个功能强大的工具,可显式控制计算后的特征图的分辨率,并构建了五个并行头,包括(a)包含三个3×3的Atrous空间金字塔池化模块(ASPP)[43]。卷积具有不同的原子率,(b)1×1卷积头,以及(c)图像级特征[44]。我们通过输出步幅来表示输入图像空间分辨率与最终输出分辨率的比率,该比率通过适当地应用原子卷积来控制。对于语义分割,对于更密集的特征图,我们通常采用输出步幅= 16或8。我们在PASCAL VOC 2012数据集[3]上进行了实验,并使用了[45]中的附加注释图像和评估指标mIOU。

为了构建移动模型,我们尝试了三种设计变体:(1)不同的特征提取器;(2)简化DeepLabv3头以加快计算速度;以及(3)不同的推理策略以提高性能。我们的结果总结在表7中。我们已经观察到:(a)推理策略,包括多尺度输入和添加左右翻转图像,会显着增加MAdds,因此不适合在设备上应用;(b)使用输出步幅= 16比输出步幅= 8更有效率(c)MobileNetV1已经是功能强大的功能提取器,仅需要比ResNet-101 [M]少4.9-5.7倍的MAdds(例如,mIOU:78.56 vs 82.70,以及MAdds:941.9B与4870.6B),(d)在MobileNetV2的倒数第二个特征图之上构建DeepLabv3头比在原始的最后一层特征图上构建更有效,因为倒数第二个特征图包含320个通道而不是1280,这样我们就可以达到类似的性能,但是所需的操作却比MobileNetV1的要少约2.5倍,并且(e)DeepLabv3磁头在计算上很昂贵,并且删除ASPP模块大大降低了MAdds的性能。退化。在表7的最后,我们确定了设备上应用程序的潜在候选对象(黑体字),其mIOU达到75.32%,仅需要2.75B MAdds。

6.4. Ablation study

反向残余连接。残余连接的重要性已被广泛研究[8,30,46]。本文报告的新结果是,快捷方式连接瓶颈的性能比连接扩展层的快捷方式更好(请参见图6b进行比较)。

线性瓶颈的重要性。线性瓶颈模型严格地不如具有非线性模型的强大,因为激活总是可以在线性范围内进行,只要对偏差和比例进行适当的更改即可。但是,图6a中所示的实验表明,线性瓶颈可以改善性能,从而为非线性破坏低维空间中的信息提供了支持。
在这里插入图片描述

7 Conclusions and future work

我们描述了一个非常简单的网络体系结构,该体系结构使我们能够构建一系列高效的移动模型。我们的基本建筑单元具有多种属性,使其特别适合于移动应用程序。

它允许非常有效的内存推断,并依赖于利用所有神经框架中存在的标准操作。对于ImageNet数据集,我们的体系结构改善了各种性能点的技术水平。

对于目标检测任务,我们的网络在准确性和模型复杂性方面均优于COCO数据集上的最新实时检测器。值得注意的是,与YOLOv2相比,我们的体系结构与SSDLite检测模块相结合,计算量减少了20倍,参数减少了10倍。

从理论上讲:所提出的卷积块具有独特的属性,可以将网络表示性(由扩展层编码)与其容量(由瓶颈输入编码)分开。探索这一点是未来研究的重要方向。致谢我们要感谢Matt Streeter和Sergey Ioffe的有益反馈和讨论。

总结

MobileNetV2网络是2018年04月发布的,沿用了V1的深度可分离卷积,主要创新点是逆转残差(Inverted Residuals)和线性瓶颈层(Linear Bottlenecks)。

线性瓶颈层(Linear Bottlenecks)
指的是在bottleneck模块的最后使用的是线性转换而不是ReLU。作者分析认为ReLU破坏了特征图的通道,导致丢失了信息。但是如果有很多的通道,激活信息仍然被保留在其他的通道。
作者认为满足感兴趣流形区域(manifold of interest)坐落在高维激活空间的低维子空间的条件下:
1.感兴趣流形区域在ReLU之后保持了非0,相当于线性转换。
2.ReLU能够保持输入流性的完整性。

于是,作者提出了线性瓶颈层( Linear Bottlenecks),有效地防止过多信息被破坏。实验也确实验证了猜想,bottleneck的非线性会破坏性能。

逆转残差(Inverted Residuals)
在这里插入图片描述
在这里插入图片描述
(a)是普通的残差bottleneck模块,输入的特征图经过1x1和3x3的卷积进行压缩,再使用1x1的卷积进行扩张还原厚度,且每经过卷积处理都需要进行ReLU的非线性激活;

(b)是逆转的残差bottleneck模块,输入的特征图经过1x1和3x3的卷积进行扩张(即为expansion factor t>1t>1t>1,若t<1t<1t<1,则为传统的残差卷积模块),再使用1x1的卷积进行压缩还原厚度,且最后的1x1卷积使用的是线性处理,确保了信息不被丢失。

对于逆转的残差bottleneck模块,使用shortcut层的目的于传统的残差模块相同,为了提升梯度传播的能力。同时,逆转的设计非常大的内存效率,同时轻微地提升了性能。

整体的网络结构

在这里插入图片描述
其中ccc表示输出特征图的channel,nnn表示层的重复次数,sss表示stride。使用ReLU6作为非线性,因为在低精度下运算下比较鲁棒。输出是全卷积而非 softmax,k 就是识别目标的类别数目。
实现代码:

import torch
import torch.nn as nn
import torch.nn.functional as F


class Block(nn.Module):
    '''expand + depthwise + pointwise'''
    def __init__(self, in_planes, out_planes, expansion, stride):
        super(Block, self).__init__()
        self.stride = stride

        planes = expansion * in_planes
        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, 
                               stride=1, padding=0, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, 
                               stride=stride, padding=1, groups=planes, 
                               bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.conv3 = nn.Conv2d(planes, out_planes, kernel_size=1, 
                               stride=1, padding=0, bias=False)
        self.bn3 = nn.BatchNorm2d(out_planes)

        self.shortcut = nn.Sequential()
        if stride == 1 and in_planes != out_planes:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_planes, out_planes, kernel_size=1, 
                          stride=1, padding=0, bias=False),
                nn.BatchNorm2d(out_planes),
            )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = F.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))
        out = out + self.shortcut(x) if self.stride==1 else out
        return out


class MobileNetV2(nn.Module):
    # (expansion, out_planes, num_blocks, stride)
    cfg = [(1,  16, 1, 1),
           (6,  24, 2, 1),  # NOTE: change stride 2 -> 1 for CIFAR10
           (6,  32, 3, 2),
           (6,  64, 4, 2),
           (6,  96, 3, 1),
           (6, 160, 3, 2),
           (6, 320, 1, 1)]

    def __init__(self, num_classes=10):
        super(MobileNetV2, self).__init__()
        # NOTE: change conv1 stride 2 -> 1 for CIFAR10
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, 
                               padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.layers = self._make_layers(in_planes=32)
        self.conv2 = nn.Conv2d(320, 1280, kernel_size=1, stride=1, 
                               padding=0, bias=False)
        self.bn2 = nn.BatchNorm2d(1280)
        self.linear = nn.Linear(1280, num_classes)

    def _make_layers(self, in_planes):
        layers = []
        for expansion, out_planes, num_blocks, stride in self.cfg:
            strides = [stride] + [1]*(num_blocks-1)
            for stride in strides:
                layers.append(
                    Block(in_planes, out_planes, expansion, stride))
                in_planes = out_planes
        return nn.Sequential(*layers)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.layers(out)
        out = F.relu(self.bn2(self.conv2(out)))
        # NOTE: change pooling kernel_size 7 -> 4 for CIFAR10
        out = F.avg_pool2d(out, 4)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out


def test():
    net = MobileNetV2()
    x = torch.randn(2,3,32,32)
    y = net(x)
    print(y.size())

# test()


总结部分,转自:https://blog.csdn.net/winycg/article/details/87474788

发布了66 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_18315295/article/details/104229222