小杰深度学习(sixteen)——视觉-经典神经网络——MobileNetV2
7.MobileNetV2
1. 网络的背景
MobileNetV1 还不够轻量和高性能,为了让移动设备有更好的体验,Google 团队提出了 MobileNetV2 架构
MobileNetV2网络是由谷歌团队在2018年提出的,它相对于MobileNetV1而言,有着更高的准确率和更小的网络模型。
论文地址:https://arxiv.org/abs/1801.04381
Inverted Residuals and Linear Bottlenecks.pdf
2. 网络的创新
在paper中的Table 2中给出了网络结构的架构表,如下图所示(网络的结构):
上表中:
Input是每一层结构的输入矩阵尺寸和channel;Operator是操作;t是拓展因子,也就是倒置残差第一个 1x1 卷积,将原来通道放大几倍;c是输出通道数channel;n是bottleneck模块重复的次数;
s是步距,如果bottleneck重复,但只针对于第一次bottleneck的DW卷积,其他为1。
由网络结构的图可以看到最后一层是卷积层,起的是全连接层的作用,k是输出的类别,如果是ImageNet数据集,那么k就是1000。
注意:在每个DW卷积之后都有batchNorm操作。
注意:在第一个bottleneck结构中,由于t=1,所以并没有进行升维操作,即没有第一个Conv2D层。
拓展因子在MobileNetV2中第一层倒残差结构中是1,其它倒残差结构采取的是6
2.1 Inverted Residuals(倒残差结构)
ResNet残差结构是先用1x1的卷积降维,再升维。而在MobileNetV2中,是先升维,再降维,所以该结构叫倒残差结构,网络结构表格中的bottleneck就是倒残差结构。
先看一下残差结构,如下图所示:
残差结构的过程是:
11x1卷积降维
23x3卷积
31x1卷积升维
对输入特征矩阵进行利用1x1卷积进行降维,减少输入特征矩阵的channel,然后通过3x3的卷积核进行处理提取特征,最后通过1x1的卷积核进行升维,它的结构为两边深,中间浅的结构。
在MobileNetV2网络结构中,采用了倒残差结构,它的结构中间深,两边浅,如下图所示:
倒残差结构的过程是:
1首先会通过一个1x1卷积层来进行升维处理,在卷积后会跟有BN和Relu6激活函数
2紧接着是一个3x3大小DW卷积,卷积后面依旧会跟有BN和Relu6激活函数
3最后一个卷积层是1x1卷积,起到降维作用,注意卷积后只跟了BN结构,并没有使用Relu6激活函数。倒残差结构的具体过程如下图所示:
在MobileNetV1中,DW卷积的个数局限于上一层的输出通道数,无法自由改变,但是加入PW卷积之后,也就是升维卷积之后,DW卷积的个数取决于PW卷积的输出通道数,而这个通道数是可以任意指定的,因此解除了3x3卷积核个数的限制。
2.2 Relu6
从上图的倒残差结构图中可以看到,卷积之后跟着的激活函数不再是MobileNetV1的Relu激活函数,而是Relu6激活函数,它的表达式是:
图像如下:
可以看到Relu6激活函数是Relu的变种,在输入小于0时,结果为0,输入大于6时:结果为6。在0到6之间时:
y=x
。
为什么要是用Relu6替代Relu?
主要因为在移动端设备float16的低精度的时候,也能有很好的数值分辨率,因为Relu的输出范围为0到正无穷,如果对Relu的激活范围不加限制,激活值非常大,分布在一个很大的范围内,则低精度的float16无法很好地精确描述如此大范围的数值,带来精度损失,如果进行量化,Relu6能够有更好的量化表现和更小的精度下降。
2.3 Linear Bottlenecks
在倒残差结构的最后一个1x1的卷积层,使用的激活函数为线性激活函数(即不采用非线性激活,例如Relu或者Relu6,直接输出)
2.4 Shortcut
如下图所示,左侧为有shortcut连接的倒残差结构,右侧是无shortcut连接的倒残差结构:
shortcut将输入与输出直接进行相加,可以使得网络在较深的时候依旧可以进行训练。
注意:这里只有stride=1且输入特征矩阵与输出特征矩阵shape相同时才有shortcut连接。
2.5 拓展因子
paper的Table 1给出了倒残差结构的特征矩阵,如下图所示:
上图中的h和w是输入特征矩阵的高和宽,k是channel,t是拓展因子,即升维时的比例,例如输入特征矩阵维度是32,那么升维后就是32×t,即表格中的tk,k′ 是降维时卷积核的个数。
3.2 结构组件介绍
3.2.1 卷积
输入特征矩阵是(224 x 224 x 3),本层卷积核的宽、高、通道、个数是(3 x 3 x 3 x 32),步长为2,padding方式为SAME,经过计算可知,输出特征矩阵为(112 x 112 x 32)。
本层之后要经过Relu6激活函数,如下图:
3.2.2 bottleneck结构
此层bottleneck结构无第一层卷积层,无shortcut结构。
本bottleneck结构输入特征矩阵是(112 x 112 x 32)。
本bottleneck结构第一层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 32),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(112 x 112 x 32)。
本bottleneck结构第二层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 32 x 16),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(112 x 112 x 16)。
本结构如下图:
3.2.3 bottleneck结构
此层bottleneck结构有第一层卷积层,无shortcut结构。
本bottleneck结构输入特征矩阵是(112 x 112 x 16)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 16 x 96),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(112 x 112 x 16)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 96),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(56 x 56 x 96)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 96 x 24),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(56 x 56 x 24)。
本结构如下图:
3.2.4 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(56 x 56 x 24)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 24 x 144),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(56 x 56 x 144)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 144),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(56 x 56 x 144)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 144 x 24),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(56 x 56 x 24)。
本结构如下图:
3.2.5 bottleneck结构
此层bottleneck结构有第一层卷积层,无shortcut结构。
本bottleneck结构输入特征矩阵是(56 x 56 x 24)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 24 x 144),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(56 x 56 x 144)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 144),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(28 x 28 x 144)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 144 x 32),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(28 x 28 x 32)。
本结构如下图:
3.2.6 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(28 x 28 x 32)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 32 x 192),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(28 x 28 x 192)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 192),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(28 x 28 x 192)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 192 x 32),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(28 x 28 x 32)。
本结构如下图:
3.2.7 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(28 x 28 x 32)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 32 x 192),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(28 x 28 x 192)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 192),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(28 x 28 x 192)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 192 x 32),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(28 x 28 x 32)。
本结构如下图:
3.2.8 bottleneck结构
此层bottleneck结构有第一层卷积层,无shortcut结构。
本bottleneck结构输入特征矩阵是(28 x 28 x 32)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 32 x 192),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(28 x 28 x 192)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 192),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 192)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 192 x 64),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(14 x 14 x 64)。
本结构如下图:
3.2.9 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(14 x 14 x 64)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 64 x 384),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 384),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 384 x 64),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(14 x 14 x 64)。
本结构如下图:
3.2.10 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(14 x 14 x 64)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 64 x 384),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 384),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 384 x 64),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(14 x 14 x 64)。
本结构如下图:
3.2.11 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(14 x 14 x 64)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 64 x 384),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 384),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 384 x 64),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(14 x 14 x 64)。
本结构如下图:
3.2.12 bottleneck结构
此层bottleneck结构有第一层卷积层,无shortcut结构。
本bottleneck结构输入特征矩阵是(14 x 14 x 64)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 64 x 384),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 384),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 384)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 384 x 96),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(14 x 14 x 96)。
本结构如下图:
3.2.13 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(14 x 14 x 96)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 96 x 576),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 576)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 576),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 576)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 576 x 96),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(14 x 14 x 96)。
本结构如下图:
3.2.14 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(14 x 14 x 96)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 96 x 576),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 576)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 576),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 576)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 576 x 96),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(14 x 14 x 96)。
本结构如下图:
3.2.15 bottleneck结构
此层bottleneck结构有第一层卷积层,无shortcut结构。
本bottleneck结构输入特征矩阵是(14 x 14 x 96)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 96 x 576),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(14 x 14 x 576)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 576),padding方式为SAME,步长为2,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(7 x 7 x 576)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 576 x 160),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(7 x 7 x 160)。
本结构如下图:
3.2.16 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(7 x 7 x 160)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 160 x 960),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(7 x 7 x 960)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 960),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(7 x 7 x 960)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 960 x 160),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(7 x 7 x 160)。
本结构如下图:
3.2.17 bottleneck结构
此层bottleneck结构有第一层卷积层,有shortcut结构。
本bottleneck结构输入特征矩阵是(7 x 7 x 160)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 160 x 960),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(7 x 7 x 960)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 960),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(7 x 7 x 960)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 960 x 160),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(7 x 7 x 160)。
本结构如下图:
3.2.18 bottleneck结构
此层bottleneck结构有第一层卷积层,无shortcut结构。
本bottleneck结构输入特征矩阵是(7 x 7 x 160)。
本bottleneck结构第一层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 160 x 960),padding方式为SAME,步长为1,经过Relu6激活函数,经过计算可知,输出特征矩阵为(7 x 7 x 960)。
本bottleneck结构第二层DwiseConv2D卷积核的宽、高、个数是(3 x 3 x 960),padding方式为SAME,步长为1,经过batchNorm进行归一化,以及Relu6激活函数,经过计算可知,输出特征矩阵为(7 x 7 x 960)。
本bottleneck结构第三层Conv2D卷积核的宽、高、通道、个数是(1 x 1 x 960 x 320),padding方式为SAME,步长为1,经过计算可知,输出特征矩阵为(7 x 7 x 320)。
本结构如下图:
3.2.19 卷积
输入特征矩阵是(7 x 7 x 320),本层卷积核的宽、高、通道、个数是(1 x 1 x 320 x 1280),步长为1,padding方式为SAME,经过计算可知,输出特征矩阵为(7 x 7 x 1280)。
本层之后要经过Relu6激活函数,如下图:
3.2.20 池化-降采样
池化方式为AvgPool,输入特征矩阵是(7 x 7 x 1280),池化核大小为7,步长为1,经过计算可知,输出特征矩阵为(1 x 1 x 1280)。
如下图:
3.2.21 卷积
输入特征矩阵是(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个节点,如下图:
3.2.22 Softmax
最后通过Softmax实现将多分类的输出值转换为范围在[0, 1]和为1的概率分布,如下图:
4 代码展示
from torch import nn
import torch#卷积 +批归一化+激活函数的组合模块
class ConvBNRelu(nn.Module):def __init__(self,in_channels, out_channels, kernel_size=3, stride=1, padding=0, groups=1,activation=True):super(ConvBNRelu,self).__init__()if padding=='same':padding=(kernel_size-1)//2#卷积层,groups=1为标准卷积,groups=in_channels为深度可分离卷积self.layers=[nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=kernel_size,stride=stride,padding=padding,groups=groups,bias=False)]self.layers.append(nn.BatchNorm2d(out_channels))#激活函数层,使用relu6限制激活函数范围,适合移动端部署if activation:self.layers.append(nn.ReLU6(inplace=True))self.layers2=nn.Sequential(*self.layers)def forward(self,x):return self.layers2(x)#倒残差块:MobileNetV2的核心组件,先升维再降维
class InvertedResidualBlock(nn.Module):def __init__(self,in_channels, t, c, s):"""参数:in_channels: 输入通道数t: 扩展因子,决定升维倍数c: 输出通道数s:"""super(InvertedResidualBlock,self).__init__()#扩展卷积:使用1x1卷积增加通道数self.exp=ConvBNRelu(in_channels=in_channels,out_channels=in_channels*t,kernel_size=1,stride=1)#现在设置stride为1#深度卷积,使用3x3深度可分离卷积进行处理空间信息self.depthwise=ConvBNRelu(in_channels=in_channels*t,out_channels=in_channels*t,kernel_size=3,stride=s,padding=1,groups=in_channels*t)#逐点卷积,使用1x1卷积降低通道数,不使用激活函数保留特征信息self.pointwise=ConvBNRelu(in_channels=in_channels*t,out_channels=c,kernel_size=1,activation=False)#是否进行残差连接:输入输出通道数相同且步长为1时,使用残差链接self.is_res=in_channels==c and s==1def forward(self,x):identity=x #保存输入作为残差x=self.exp(x)x=self.depthwise(x)x=self.pointwise(x)if self.is_res:x = identity + x # 残差连接return xclass BottleneckLayer(nn.Module):def __init__(self,in_channels, t, c, n, s):"""参数:in_channels: 输入通道数t: 扩展因子c: 输出通道数n: 堆叠的残差块数量s: 第一个残差块的步长,其余残差块步长为1"""super(BottleneckLayer,self).__init__()self.stack=nn.Sequential(InvertedResidualBlock(in_channels,t,c,s), # 第一个残差块可能改变通道数和分辨率#后续残差块保持通道数和分辨率不变,仅使用步长为1*(InvertedResidualBlock(c,t,c,1)for _ in range(n-1)))def forward(self,x):return self.stack(x)# MobileNetV2主模型
class MobileNetV2(nn.Module):def __init__(self,num_classes=1000):super(MobileNetV2,self).__init__()#初始化卷积层:提取基础的特征self.c1=ConvBNRelu(in_channels=3,out_channels=32,kernel_size=3,stride=2,padding=1)#7个瓶颈层模块(倒残差结构),逐步提取特征并降低空间分辨率(hxw的尺寸)self.bottleneck1=BottleneckLayer(32,1,16,1,1)#第一层不进行下采样self.bottleneck2 = BottleneckLayer(16, 6, 24, 2, 2)self.bottleneck3 = BottleneckLayer(24, 6, 32, 3, 2)self.bottleneck4 = BottleneckLayer(32, 6, 64, 4, 2)self.bottleneck5 = BottleneckLayer(64, 6, 96, 3, 1) # 保持分辨率不变self.bottleneck6 = BottleneckLayer(96, 6, 160, 3, 2)self.bottleneck7 = BottleneckLayer(160, 6, 320, 1, 1)#最终特征提取层# 最终特征提取层self.c2 = ConvBNRelu(320, 1280, 1)#全局平均池化,将特征压缩为1x1self.avg_pool=nn.AdaptiveAvgPool2d((1,1))#使用普通池化转化成1x1# self.avg_pool2=nn.AvgPool2d((7,7))#输出层 分类器,使用1x1卷积直接映射为类别数self.c3=nn.Conv2d(in_channels=1280,out_channels=num_classes,kernel_size=1)def forward(self,x):# 特征提取路径x = self.c1(x)x = self.bottleneck1(x)x = self.bottleneck2(x)x = self.bottleneck3(x)x = self.bottleneck4(x)x = self.bottleneck5(x)x = self.bottleneck6(x)x = self.bottleneck7(x)x = self.c2(x)# 分类路径x = self.avg_pool(x)x = self.c3(x)out = x.view(x.size(0), -1) # 展平为二维张量return out
if __name__ == '__main__':#模型测试 输入5张 224x224的RGB图像model=MobileNetV2()x=torch.rand(5,3,224,224)print(model(x).shape) #输出形状[5,1000]