小杰深度学习(seventeen)——视觉-经典神经网络——MObileNetV3
8.MobileNetV3
1. 网络的背景
MobileNetV3 是 2019 年 Google 研究院在韩国首尔举行的 ICCV(International Conference on Computer Vision 国际计算机视觉大会) 上发表的,
MobileNetV3提出了large和small两个版本(区别在于网络结构不同),paper中讲在MobileNetV3 Large在ImageNet分类任务上,较MobileNetV2,TOP1准确率提高了大约3.2%,时间减少了20%。与具有同等延迟的MobileNetV2模型相比,MobileNetV3 Small的准确率高6.6%。
注意:TOP1指的是得到的识别结果中可能性最高的那个预测值与真实值的准确率。
论文地址:https://arxiv.org/abs/1905.02244
Searching for MobileNetV3.pdf
2. 网络的创新
2.1 Block创新
对Block的结构进行了创新,在其中加入了SE模块,即通道注意力机制,以及更新了激活函数。
2.1.1 加入SE(Squeeze-and-Excitation (挤压激励”或“压缩激励) )模块
在MobileNetV2中,采用了倒残差结构,该结构如下图所示:
1首先会通过一个1x1卷积层来进行升维处理,在卷积后会跟有BN和ReLU6激活函数
2紧接着是一个3x3大小DW卷积,卷积后面依旧会跟有BN和ReLU6激活函数
3最后一个卷积层是1x1卷积,起到降维作用,注意卷积后只跟了BN结构,并没有使用ReLU6激活函数。
当stride=1且输入特征矩阵=输出特征矩阵时,使用shortcut结构。
在MobileNetV3中的某些层中(注意不是全部),在进行最后一个1x1卷积之前,要进行一个SE模块注意力机制,MobileNetV3的倒残差如下图所示:
在上图中红框圈出的内容,这是一个SE模块,这里的SE模块是一个通道注意力机制,即对通道进行特征注意。
SE模块的作用是针对于得到的特征矩阵,对每一个channel进行池化处理,有多少channel,得到的池化后的一维向量就有多少个元素,接下来通过第一个全连接层,这个全连接层的节点个数=特征矩阵channel的1/4,接下来是第二个全连接层,它的节点个数=特征矩阵channel。所以SE模块可以理解为:对特征矩阵的每个channel分析出一个权重关系,它把比较重要的channel赋予更大的权重值,把不那么重要的channel赋予比较小的权重值。举例如下:
上图中,Conv1是DW卷积后得到的特征矩阵,该特征矩阵共4个channel,当然实际上多得多,这里只是简单举例,对每个channel进行平均池化操作,得到一个向量,接下来要经过第一次全连接层,全连接层的激活函数为Relu,节点个数=1,即4个channel的1/4,接下来通过第二个全连接层和h-sigmoid激活函数,该激活函数在后续会讲到,节点个数为4,最后得到的向量要与Conv1的每一个元素进行相乘,例如Conv1左上角的0.2,与得到的第一个channel的系数0.5进行相乘,得到Conv2的左上角的0.1,以此类推,就完成了SE模块的功能。
2.1.2 更新了激活函数
在新的Block结构中,更新了激活函数,在下图中的红框内可以看到,激活函数用了NL进行表示,NL指的是非线性激活函数,在不同的层中,使用了不同的激活函数,所以只是用了NL进行表示具体哪一层用了什么激活函数,会在网络的结构中指出。
在MobileNetV2中,使用的几乎都是Relu6激活函数。
但是在很多网络中,使用的是swish x激活函数,swish x激活函数就是x乘以sigmoid激活函数,公式如下:
swish x激活函数确实可以提高准确性,但计算以及求导过于复杂,在MobileNetV3中,提出了一个h-swish[x]的激活函数,图示如下:
公式如下:
h-swish[x]激活函数其实是x乘以h-sigmoid激活函数,从图像中看出,h-swish[x]激活函数与swish x激活函数非常相似,而且公式更为简单,没有幂运算,计算速度更快。
并非整个模型都使用了h-swish,模型的前一半层使用常规ReLU。
2.2 使用NAS搜索参数
NAS(神经网络架构搜索) 是一种自动化设计神经网络结构的技术,核心是通过算法自动优化网络层的类型、参数、连接方式等。
- 目标:自动确定网络的层数、每层类型(如卷积层、池化层)、输入输出通道数、激活函数等参数。
- 实现方式:利用随机搜索、遗传算法、强化学习等算法,在预设的搜索空间中尝试不同结构,通过评估训练 / 验证性能(如准确率、延迟)筛选最优架构。
3. 网络存在的问题
在实际使用过程中,可能会出现MobileNetV3准确率不如MobileNetV2的情况,MobileNetV3是出自于Google,它更关注网络在Android设备上的表现,在其它设备上运行可能无法达到最佳效果。
4. 网络的结构
4.1 网络结构
在paper中的Table 1中给出了网络结构的图,如下图所示:
注意:本处展示的MobileNetV3-Large模型,虚拟仿真流程用该模型,small模型自己去学习。
在上表中:
- Input代表输入当前层的特征矩阵;
- Operator代表操作(bneck就是倒残差结构,后面的3x3或者5x5就是DW卷积的卷积核大小,在最后两层有个NBN参数,NBN代表这两层无batchNorm,意味着其它层的Conv2D以及DWConv2D每一层之后都需要跟batchNorm);
- exp size代表第一个升维的卷积所升到维度;
- #out代表输出矩阵的channel;
- SE代表这一层bneck是否使用注意力机制,对号代表使用;
- NL代表激活函数的类别(HS是h-swish[x]激活函数,RE是Relu激活函数);
- s代表步距,但是在bneck结构中指的是DW卷积的步距,其它层步距依然为1。
这里使用的数据集为ImageNet,所以k是1000。
下面结合虚拟仿真的组件,对MobileNetV3的网络结构进行分析。
4.2 结构组件介绍
4.2.1 卷积
输入特征矩阵是(224 x 224 x 3),本层卷积核的宽、高、通道、个数是(3 x 3 x 3 x 16),步长为2,padding方式为SAME,经过计算可知,输出特征矩阵为(112 x 112 x 16)。
本层之后要经过batchNorm进行归一化,以及h-swish[x]激活函数,如下图:
4.2.2 bneck结构
此层bneck结构无第一层卷积层,有shortcut结构,无SE模块。
本bneck结构输入特征矩阵是(112 x 112 x 16)。
本bneck结构第一层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 16),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(112 x 112 x 16)。
本bneck结构第二层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 16 x 16),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(112 x 112 x 16)。
本结构如下图:
4.2.3 bneck结构
此层bneck结构有第一层卷积层,无shortcut结构,无SE模块。
本bneck结构输入特征矩阵是(112 x 112 x 16)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 16 x 64),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(112 x 112 x 64)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 64),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(56 x 56 x 64)。
本bneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 64 x 24),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(56 x 56 x 24)。
本结构如下图:
4.2.4 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,无SE模块。
本bneck结构输入特征矩阵是(56 x 56 x 24)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 24 x 72),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(56 x 56 x 72)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 72),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(56 x 56 x 72)。
本bneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 72 x 24),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(56 x 56 x 24)。
本结构如下图:
4.2.5 bneck结构
此层bneck结构有第一层卷积层,无shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(56 x 56 x 24)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 24 x 72),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(56 x 56 x 72)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(5 x 5 x 72),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(28 x 28 x 72)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(28 x 28),FC1的节点个数是24,FC2的节点个数是72。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 72 x 40),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(28 x 28 x 40)。
本结构如下图:
4.2.6 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(28 x 28 x 40)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 40 x 120),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(28 x 28 x 120)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(5 x 5 x 120),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(28 x 28 x 120)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(28 x 28),FC1的节点个数是32,FC2的节点个数是120。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 120 x 40),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(28 x 28 x 40)。
本结构如下图:
4.2.7 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(28 x 28 x 40)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 40 x 120),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(28 x 28 x 120)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(5 x 5 x 120),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu激活函数,经计算可知,输出特征矩阵为(28 x 28 x 120)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(28 x 28),FC1的节点个数是32,FC2的节点个数是120。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 120 x 40),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(28 x 28 x 40)。
本结构如下图:
4.2.8 bneck结构
此层bneck结构有第一层卷积层,无shortcut结构,无SE模块。
本bneck结构输入特征矩阵是(28 x 28 x 40)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 40 x 240),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(28 x 28 x 240)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 240),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 240)。
本bneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 240 x 80),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(14 x 14 x 80)。
本结构如下图:
4.2.9 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,无SE模块。
本bneck结构输入特征矩阵是(14 x 14 x 80)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 80 x 200),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 200)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 200),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 200)。
本bneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 200 x 80),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(14 x 14 x 80)。
本结构如下图:
4.2.10 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,无SE模块。
本bneck结构输入特征矩阵是(14 x 14 x 80)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 80 x 184),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 184)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 184),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 184)。
本bneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 184 x 80),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(14 x 14 x 80)。
本结构如下图:
4.2.11 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,无SE模块。
本bneck结构输入特征矩阵是(14 x 14 x 80)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 80 x 184),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 184)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 184),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 184)。
本bneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 184 x 80),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(14 x 14 x 80)。
本结构如下图:
4.2.12 bneck结构
此层bneck结构有第一层卷积层,无shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(14 x 14 x 80)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 80 x 480),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 480)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 480),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 480)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(14 x 14),FC1的节点个数是120,FC2的节点个数是480。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 480 x 112),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(14 x 14 x 112)。
本结构如下图:
4.2.13 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(14 x 14 x 112)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 112 x 672),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 672)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 672),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 672)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(14 x 14),FC1的节点个数是168,FC2的节点个数是672。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 672 x 112),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(14 x 14 x 112)。
本结构如下图:
4.2.14 bneck结构
此层bneck结构有第一层卷积层,无shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(14 x 14 x 112)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 112 x 672),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(14 x 14 x 672)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(5 x 5 x 672),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(7 x 7 x 672)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(7 x 7),FC1的节点个数是168,FC2的节点个数是672。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 672 x 160),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(7 x 7 x 160)。
本结构如下图:
4.2.15 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(7 x 7 x 160)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 160 x 960),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(7 x 7 x 960)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(5 x 5 x 960),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(7 x 7 x 960)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(7 x 7),FC1的节点个数是240,FC2的节点个数是960。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 960 x 160),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(7 x 7 x 160)。
本结构如下图:
4.2.16 bneck结构
此层bneck结构有第一层卷积层,有shortcut结构,有SE模块。
本bneck结构输入特征矩阵是(7 x 7 x 160)。
本bneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 160 x 960),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(7 x 7 x 960)。
本bneck结构第二层DwiseConv2D卷积核的宽、高、个数是(5 x 5 x 960),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及h-swish[x]激活函数,经计算可知,输出特征矩阵为(7 x 7 x 960)。
本bneck结构第三层SE模块,包含AvgPool、FC1以及FC2,其中,AvgPool池化核大小为(7 x 7),FC1的节点个数是240,FC2的节点个数是960。
本bneck结构第四层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 960 x 160),padding方式为SAME,步长为1,经过batchNorm进行归一化,经计算可知,输出特征矩阵为(7 x 7 x 160)。
本结构如下图:
4.2.17 卷积
输入特征矩阵是(7 x 7 x 160),本层卷积核的宽、高、通道、个数是(1 x 1 x 160 x 960),步长为1,padding方式为SAME,经过计算可知,输出特征矩阵为(7 x 7 x 960)。
本层之后要经过batchNorm进行归一化,以及h-swish[x]激活函数,如下图:
4.2.18 池化-降采样
池化方式为AvgPool,输入特征矩阵是(7 x 7 x 960),池化核大小为7,步长为1,经过计算可知,输出特征矩阵为(1 x 1 x 960)。
如下图:
4.2.19 卷积
输入特征矩阵是(1 x 1 x 960),本层卷积核的宽、高、通道、个数是(1 x 1 x 960 x 1280),步长为1,padding方式为SAME,经过计算可知,输出特征矩阵为(1 x 1 x 1280)。
本层之后要经过h-swish[x]激活函数,如下图:
4.2.20 卷积
输入特征矩阵是(1 x 1 x 1280),本层卷积核的宽、高、通道、个数是(1 x 1 x 1280 x 1000),步长为1,padding方式为SAME,经过计算可知,输出特征矩阵为(1 x 1 x 1000)。
本层之后要经过Squeeze进行展平,即使输出矩阵(1 x 1 x 1000)转换成1000个节点,如下图:
4.2.21 Softmax
最后通过Softmax实现将多分类的输出值转换为范围在[0, 1]和为1的概率分布,如下图:
4.2.22 实验验证
本实验主要学习MobileNetV3的网络的相关知识,最后需要进行点击“验证”,验证成功即代表网络结构连接是正确的。
代码实现
import torch
import torch.nn as nn#SE模块
class SE(nn.Module):def __init__(self,in_channels):super(SE,self).__init__()self.avg_pool=nn.AdaptiveAvgPool2d(1) #按照通道压缩成一个值self.fc1_conv=nn.Conv2d(in_channels=in_channels,out_channels=in_channels//4,kernel_size=1,bias=False)#压缩成输入通道的1/4self.fc1_bn=nn.BatchNorm2d(in_channels//4)self.fc1_relu=nn.ReLU(inplace=True)self.fc2_conv=nn.Conv2d(in_channels=in_channels//4,out_channels=in_channels,kernel_size=1,bias=False)self.fc2_bn=nn.BatchNorm2d(in_channels)self.fc2_hardsigmoid=nn.Hardsigmoid()def forward(self,x):weight=self.avg_pool(x)weight=self.fc1_relu(self.fc1_bn(self.fc1_conv(weight)))weight=self.fc2_hardsigmoid(self.fc2_bn(self.fc2_conv(weight)))return weight*xclass Bottleneck(nn.Module):def __init__(self,in_channels,kernel_size,exp_size,out,se,nl,s):super(Bottleneck,self).__init__()self.exp_conv=nn.Conv2d(in_channels=in_channels,out_channels=exp_size,kernel_size=1,)self.exp_bn=nn.BatchNorm2d(exp_size)self.exp_act=nl() if nl is not None else nn.Identity #处理无激活的情况self.depth_wise_conv=nn.Conv2d(in_channels=exp_size,out_channels=exp_size,kernel_size=kernel_size,stride=s,padding=kernel_size//2,groups=exp_size,bias=False)self.depth_wise_bn=nn.BatchNorm2d(exp_size)self.depth_wise_act=nl() if nl is not None else nn.Identity()self.se=SE(in_channels=exp_size) if se else Noneself.point_wise_conv=nn.Conv2d(in_channels=exp_size,out_channels=out,kernel_size=1,bias=False)self.point_wise_bn=nn.BatchNorm2d(out)self.is_res = s == 1 and in_channels == outdef forward(self,x):identity=xx=self.exp_act(self.exp_bn(self.exp_conv(x)))x=self.depth_wise_act(self.depth_wise_bn(self.depth_wise_conv(x)))if self.se is not None:x=self.se(x)x=self.point_wise_bn(self.point_wise_conv(x))if self.is_res:x=x+identityreturn xclass MobileNetV3_Large(nn.Module):def __init__(self,num_classes=1000):super(MobileNetV3_Large,self).__init__()#初始化卷积层self.c1_conv=nn.Conv2d(in_channels=3,out_channels=16,kernel_size=3,stride=2,padding=1,bias=False)self.c1_bn=nn.BatchNorm2d(16)self.c1_act=nn.Hardswish()# Bottleneck Blocks (根据MobileNetV3 Large结构调整参数)self.bneck1=Bottleneck(in_channels=16,kernel_size=3,exp_size=16,out=16,se=False,nl=nn.ReLU,s=1)self.bneck2 = Bottleneck(in_channels=16,kernel_size=3, exp_size=64, out=24, se=False, nl=nn.ReLU, s=2) # 层2: 下采样, 无SEself.bneck3 = Bottleneck(in_channels=24, kernel_size=3, exp_size=72, out=24, se=False, nl=nn.ReLU, s=1) # 层3: 无SEself.bneck4 = Bottleneck(in_channels=24, kernel_size=5, exp_size=72, out=40, se=True, nl=nn.ReLU, s=2) # 层4: 有SE,self.bneck5 = Bottleneck(in_channels=40, kernel_size=5, exp_size=120, out=40, se=True, nl=nn.ReLU, s=1) # 层5: 有SEself.bneck6 = Bottleneck(in_channels=40, kernel_size=5, exp_size=120, out=40, se=True, nl=nn.ReLU, s=1) # 层6: 有SEself.bneck7 = Bottleneck(in_channels=40, kernel_size=3, exp_size=240, out=80, se=False, nl=nn.Hardswish, s=2) # 层7: 下采样, 无SEself.bneck8 = Bottleneck(in_channels=80, kernel_size=3, exp_size=200, out=80, se=False, nl=nn.Hardswish,s= 1) # 层8: 无SEself.bneck9 = Bottleneck(in_channels=80, kernel_size=3, exp_size=184, out=80, se=False, nl=nn.Hardswish, s=1) # 层9: 无SEself.bneck10 = Bottleneck(in_channels=80, kernel_size=3, exp_size=184,out= 80, se=False, nl=nn.Hardswish, s=1) # 层10: 无SEself.bneck11 = Bottleneck(in_channels=80, kernel_size=3, exp_size=480, out=112, se=True, nl=nn.Hardswish, s=1) # 层11: 有SEself.bneck12 = Bottleneck(in_channels=112, kernel_size=3, exp_size=672,out= 112, se=True, nl=nn.Hardswish, s=1) # 层12: 有SEself.bneck13 = Bottleneck(in_channels=112, kernel_size=5, exp_size=672, out=160,se= True, nl=nn.Hardswish, s=2) # 层13: 下采样, 有SEself.bneck14 = Bottleneck(in_channels=160, kernel_size=5, exp_size=960,out= 160, se=True, nl=nn.Hardswish, s=1) # 层14: 有SEself.bneck15 = Bottleneck(in_channels=160, kernel_size=5, exp_size=960, out=160, se=True, nl=nn.Hardswish, s=1) # 层15: 有SE#最后几层self.c2_conv=nn.Conv2d(in_channels=160,out_channels=960,kernel_size=1,bias=False)self.c2_bn=nn.BatchNorm2d(960)self.c2_act=nn.Hardswish()self.avg_pool = nn.AdaptiveAvgPool2d(1)self.classifier_conv1=nn.Conv2d(in_channels=960,out_channels=1280,kernel_size=1,bias=False)self.classifier_act1 = nn.Hardswish()self.classifier_conv2=nn.Conv2d(in_channels=1280,out_channels=num_classes,kernel_size=1)def forward(self,x):x = self.c1_act(self.c1_bn(self.c1_conv(x)))# 按顺序通过所有瓶颈块x = self.bneck1(x)x = self.bneck2(x)x = self.bneck3(x)x = self.bneck4(x)x = self.bneck5(x)x = self.bneck6(x)x = self.bneck7(x)x = self.bneck8(x)x = self.bneck9(x)x = self.bneck10(x)x = self.bneck11(x)x = self.bneck12(x)x = self.bneck13(x)x = self.bneck14(x)x = self.bneck15(x)# 最后几层处理x = self.c2_act(self.c2_bn(self.c2_conv(x)))x = self.avg_pool(x)# 分类器x = self.classifier_act1(self.classifier_conv1(x))x = self.classifier_conv2(x)x = x.flatten(start_dim=1)return xif __name__ == '__main__':model= MobileNetV3_Large(num_classes=1000)x=torch.randn(5,3,224,224)#5个样本print(model(x).shape) #输出[5,1000]