MobileNetv_3网络结构+代码

前言 

MobileNet系列由Google提出,2019年Google正式提出MobileNetv_3,论文题目为《Searching for MobileNetV3》

论文地址:https://arxiv.org/pdf/1905.02244.pdf

代码地址:https://hub.fastgit.org/xiaolai-sqlai/mobilenetv3hub.fastgit.org为Github的镜像网站,可加速进入)

图1   MobileNetV3 论文标题

MobileNetv_3是MnasNet的改进版,研究人员公布了MobileNetv_3的两个版本,MobileNetv_3-small和MobileNetv_3-Large,网络的亮点为:

  • 更新了bneck块
  • 使用NAS(Neural Architecture Search)搜索参数
  • 重新设计了耗时层结构

我们直接手撕代码来分析论文核心点。

 

重新设计激活函数

作者发现使用非线性函数swish时,能够显著提高神经网络的精度。swish函数定义如下:

图2   非线性swish函数表达式

其中,\delta (x) =\frac{1}{1+e^{-x}},即在分类中经常见到的sigmoid function。论文中提到“虽然swish这种非线性提高了精度,但它在嵌入式环境中需要成本,因为它在移动端设备上计算sigmoid function或对其求导代价高昂。作者想到了一个函数h-sigmoid(x)=\frac{ReLU6(x+3)}{6},可以解决sigmoid function带来的计算复杂和求导复杂。文中采用的激活函数为

图3   h-swish(x)函数表达式

激活函数图像如下:

图4   四种激活函数图像对比

代码如下:

class hswish(nn.Module):
    def forward(self, x):
        out = x * F.relu6(x+3, inplace=True) / 6
        return out

class hsigmoid(nn.Module):
    def forward(self, x):
        out = F.relu6(x + 3, inplace=True) / 6
        return out

 

加入SE模块

SE通道注意力机制,它是最后一届ImageNet冠军模型,原论文为《Squeeze-and-Excitation Networks》,论文链接:https://arxiv.org/pdf/1709.01507.pdf

一个SEblock可以分为 Squeeze(压缩) 和 Excitation(激发) 两个步骤:

  • Squeeze(压缩) :对 C×H×W 进行 global average pooling,得到 1×1×C 大小的特征图,这个特征图可以理解为具有全局感受野。
  • Excitation(激发) :通过两层全连接的bottleneck结构得到Feature Map中每个通道的权值,并将加权后的Feature Map作为下一层网络的输入。
图5   SE结构块

SE和Inception、ResNet结合后的模型图如下:

图6   SE同Inception和ResNet结合模型

SE的代码实现:

# 定义SE块
class SE_Module(nn.Module):
    def __init__(self, in_size, reduction=4):
        super(SE_Module, self).__init__()

        self.se = nn.Sequential(
            nn.AdaptiveAvgPool2d(1),
            nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(in_size // reduction),
            nn.ReLU(inplace=True),

            nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(in_size),
            nn.ReLU(inplace=True),

            hsigmoid(),
        )

    def forward(self, x):
        return x * self.se(x)
  • 代码中reduction为通道减小倍数,在MobileNetv_3中,假设进入SE模块时feature的通道为C,经过自适应平均池化后,feature map通道未发生改变,仍为C,在经过第一层FC后(其实就是做1×1卷积),通道变为C/reduction,论文中reduction=4。然后feature map再次经过一层FC(1×1卷积),通道数又变为C,即把特征通道数升回到原来的维度。再通入h-sigmoid激活函数中,得到C个取值在(0, 1)的权重值。最后通过scale操作将每个权重值加权到每个通道的特征上(就是把每个权重值分别乘以对应特征矩阵上的每个元素得到一个新的feature map)

 

  • nn.AvgPool2d()  VS nn.AdaptiveAvgPool2d() 
    • 第一个原型为 class torch.nn.AvgPool2d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True),意思是对信号的输入通道,提供2维的平均池化.

      • 参数介绍:

        • kernel_size(int or tuple) - 池化窗口大小
        • stride(int or tupleoptional) - max pooling的窗口移动的步长。默认值是kernel_size
        • padding(int or tupleoptional) - 输入的每一条边补充0的层数
        • dilation(int or tupleoptional) – 一个控制窗口中元素步幅的参数
        • ceil_mode - 如果等于True,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作
        • count_include_pad - 如果等于True,计算平均池化时,将包括padding填充的0
    • 第二个原型为 class torch.nn.AdaptiveAvgPool2d(output_size),对输入信号,提供2维的自适应平均池化操作 对于任何输入大小的输入,可以将输出尺寸指定为H*W,但是输入和输出特征的数目不会变化。
      • 参数介绍:
        • output_size: 输出信号的尺寸,可以用(H,W)表示H*W的输出,也可以使用耽搁数字H表示H*H大小的输出

 

 

Block设计

Mobilenet V3 block结构如下图所示:

图7   Mobilenet V3 block块结构

解释图7中一个block的执行流程:

输入特征首先经过1×1卷积进行升维,上升的维度为图10结构表中exp size参数,所使用的非线性激活函数也请参考图10中的NL列。接着将输出特征通入3×3的dw卷积,使用非线性激活函数NL,通道数exp size不发生改变。然后将输出特征通入SE块中,先对每个通道进行全局平均池化,得到1*1*exp size的向量,再将该向量通入第一层FC,激活函数为ReLU,得到输出为1*1*(exp size/4)。接着将输出通入第二层FC,激活函数为h-sigmoid,得到1*1*exp size的向量,其中向量内元素取值为(0,1),类似于概率值。将该向量中的每个元素乘以“输入到SE层的feature map”的对应通道特征矩阵,得到一个和feature map一样大小的新feature。最后进行1×1卷积降维处理,输出维度请参考图10中out列。

注意:只有当步长s=1且输入通道==输出通道时,才有图中的shortcut连接。

Mobilenet V2 block结构如下图所示:

图8   Mobilenet V2 倒残差结构

V3对比V2,可以发现V3的block块第一个1×1卷积后的激活函数为NL,即非线性激活函数,针对不同的层使用不同的激活函数。另外,V3在Dwise卷积之后引入了SE模块,具体网络实现细节还得参考网络结构。

block设计的代码如下:

class Bneck(nn.Module):
    '''expand + depthwise + pointwise'''
    def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, s):
        super(Bneck, self).__init__()

        self.stride = s
        self.se = semodule

        self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn1 = nn.BatchNorm2d(expand_size)
        self.nolinear1 = nolinear
        self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=s,
                               padding=kernel_size // 2, groups=expand_size, bias=False)
        self.bn2 = nn.BatchNorm2d(expand_size)
        self.nolinear2 = nolinear
        self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn3 = nn.BatchNorm2d(out_size)

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

    def forward(self, x):
        out = self.nolinear1(self.bn1(self.conv1(x)))
        out = self.nolinear2(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))
        if self.se != None:
            out = self.se(out)
        out += self.shortcut(x) if self.stride == 1 else out
        return out

 

重新设计耗时层结构

  • 减少第一个卷积层卷积核的个数(32 --> 16)
    • 作者发现使用16个卷积核代替之前的32个卷积核(V1和V2版本都是使用的32个卷积核),得到的精确度是一样的,那当然就选16个卷积核啊,因为可以减小参数量呀。作者在实验发现通过这一步优化后,能节省2毫秒的时间和1000万次MAdds运算。具体请参考网络结构表的第一行。
  • 简化Last Stage
    • 作者发现之前的Last Stage比较耗时,就把Original Last Stage改造成了Efficient Last Stage,改造后发现时间能减少7毫秒,这7毫秒占总运行时间的11%,而且MAdds运算次数也减少了许多,但精度几乎没有什么损失。
图9   Last Stage改进图

 

网络结构表

图10   MobileNetV3-Large结构表

上表为MobileNetV3-Large的结构表,对表解释如下:

Operator表示该层采用的操作,conv2d为标准卷积,bneck为上面介绍的V3的block块,后面的n×n表示使用的卷积核大小;

exp_size表示每一个bneck块中第一层1×1 conv2d的卷积核的个数,即对输入特征进行1×1卷积升维,上升的维度就是exp_size;

NBN表示该层卷积不使用Batch Normalization;

out表示输出特征通道数;

SE表示在哪几个bneck使用SE模块,图中打“√”的为使用SE;

NL表示每层所使用的非线性激活函数,其中HS表示h-swish函数,RE表示使用ReLu激活函数。

 

下表为MobileNetV3-Small的网络结构。相比MobileNetV3-Large的15层bneck块,MobileNetV3-Small比其少了4层,在参数量和计算量上也减少了许多。

图11   MobileNetV3-Small结构表

MobileNetV3-Large代码如下:

class MobileNetV3_Large(nn.Module):
    def __init__(self, num_classes=1000):
        super(MobileNetV3_Large, self).__init__()
        self.init_params()

        # 224*224**3 --> 112*112*16
        self.top = nn.Sequential(
            nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False),
            nn.BatchNorm2d(16),
            hswish()
        )

        self.bneck = nn.Sequential(
            Bneck(kernel_size=3, in_size=16, expand_size=16, out_size=16, nolinear=nn.ReLU(True), semodule=None, s=1),
            Bneck(kernel_size=3, in_size=16, expand_size=64, out_size=24, nolinear=nn.ReLU(True), semodule=None, s=2),
            Bneck(kernel_size=3, in_size=24, expand_size=72, out_size=24, nolinear=nn.ReLU(True), semodule=None, s=1),

            Bneck(5, 24, 72, 40, nn.ReLU(True), SE_Module(40), 2),
            Bneck(5, 40, 120, 40, nn.ReLU(True), SE_Module(40), 1),
            Bneck(5, 40, 120, 40, nn.ReLU(True), SE_Module(40), 1),

            Bneck(3, 40, 240, 80, hswish(), None, 2),
            Bneck(3, 80, 200, 80, hswish(), None, 1),
            Bneck(3, 80, 184, 80, hswish(), None, 1),
            Bneck(3, 80, 184, 80, hswish(), None, 1),
            Bneck(3, 80, 480, 112, hswish(), SE_Module(112), 1),
            Bneck(3, 112, 672, 112, hswish(), SE_Module(112), 1),

            Bneck(5, 112, 672, 160, hswish(), SE_Module(160), 1),
            Bneck(5, 160, 672, 160, hswish(), SE_Module(160), 2),
            Bneck(5, 160, 960, 160, hswish(), SE_Module(160), 1),
        )

        self.bottom = nn.Sequential(
            nn.Conv2d(160, 960, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(960),
            hswish()
        )

        self.last = nn.Sequential(
            nn.Linear(960, 1280),
            nn.BatchNorm1d(1280),
            hswish()
        )

        self.linear = nn.Linear(1280, num_classes)

    def init_params(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                init.normal_(m.weight, std=0.001)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def forward(self, x):
        out = self.top(x)
        out = self.bneck(out)
        out = self.bottom(out)

        out = F.avg_pool2d(out, 7)
        out = out.view(out.size(0), -1)

        out = self.last(out)
        out = self.linear(out)
        return out

 

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于Faster_RCNN网络模型的车辆、行人及交通信号目标检测算法python源码+数据集+项目报告+详细注释.zip ## 环境配置: * Python3.6/3.7/3.8 * Pytorch1.6(注意:必须是1.6.0或以上,因为使用官方提供的混合精度训练1.6.0后才支持) * pycocotools(Linux:```pip install pycocotools```; Windows:```pip install pycocotools-windows```(不需要额外安装vs)) * Ubuntu或Centos(不建议Windows) * 最好使用GPU训练 * 详细环境配置见```requirements.txt``` ## 文件结构: ``` ├── backbone: 特征提取网络,可以根据自己的要求选择 ├── network_files: Faster R-CNN网络(包括Fast R-CNN以及RPN等模块) ├── train_utils: 训练验证相关模块(包括cocotools) ├── my_dataset.py: 自定义dataset用于读取VOC数据集 ├── train_mobilenet.py: 以MobileNetV2做为backbone进行训练 ├── train_resnet50_fpn.py: 以resnet50+FPN做为backbone进行训练 ├── train_multi_GPU.py: 针对使用多GPU的用户使用 ├── predict.py: 简易的预测脚本,使用训练好的权重进行预测测试 ├── validation.py: 利用训练好的权重验证/测试数据的COCO指标,并生成record_mAP.txt文件 └── pascal_voc_classes.json: pascal_voc标签文件 ``` ## 预训练权重下载地址(下载后放入backbone文件夹中): * MobileNetV2 backbone: https://download.pytorch.org/models/mobilenet_v2-b0353104.pth * ResNet50+FPN backbone: https://download.pytorch.org/models/fasterrcnn_resnet50_fpn_coco-258fb6c6.pth * 注意,下载的预训练权重记得要重命名,比如在train_resnet50_fpn.py中读取的是```fasterrcnn_resnet50_fpn_coco.pth```文件, 不是```fasterrcnn_resnet50_fpn_coco-258fb6c6.pth``` ## 数据集,本例程使用的是PASCAL VOC2012数据集 * Pascal VOC2012 train/val数据集下载地址:http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar * 使用ResNet50+FPN以及迁移学习在VOC2012数据集上得到的权重: 链接:https://pan.baidu.com/s/1ifilndFRtAV5RDZINSHj5w 提取码:dsz8 ## 训练方法 * 确保提前准备好数据集 ......
### 回答1: MobileNetV3 是一种轻量级的卷积神经网络,适用于移动设备和嵌入式设备上的图像分类任务。它采用了一些新的设计策略,如倒残差结构和自适应宽度等,以提高模型的准确性和效率。如果您需要更详细的信息,可以查看相关的论文和代码实现。 ### 回答2: MobileNetV3 是一种轻量级的卷积神经网络架构,用于在移动设备和嵌入式系统上进行实时图像分类和目标检测任务。它是MobileNet系列的第三个版本,其设计目标是在保持轻量级的同时提供更高的精确度和更好的性能。 MobileNetV3通过结合一系列的优化策略和网络组件来实现高效的模型设计。首先,它使用了多种类型的卷积层,包括普通卷积、深度可分离卷积和线性Bottleneck卷积,以在不同层级上平衡模型的精度和计算代价。此外,MobileNetV3还引入了h-swish激活函数和SE模块,以增强网络的表达能力和特征重要性的捕捉能力。 在MobileNetV3中,还有一种名为Searchable Transformer的自动搜索方法,用于高效地搜索和调整网络架构。该方法通过控制搜索空间和模型复杂度,自动选择最佳的网络结构,从而实现更好的性能表现。这种自动搜索方法在保持模型轻量化的同时,还可以适应各种应用场景和硬件平台的需求。 总的来说,MobileNetV3是一个优秀的轻量级神经网络模型,适用于移动设备和嵌入式系统上的实时图像分类和目标检测任务。它通过多种优化策略和网络组件的结合,提供了更高的精确度和更好的性能。此外,它还采用了搜索方法来自动优化网络结构,以满足不同应用场景的需求。 ### 回答3: MobileNetV3 是一种轻量级的卷积神经网络模型,用于图像分类和目标检测任务。它是Google在MobileNet系列的最新版本,旨在提供更高的准确性和更快的推断速度。 MobileNetV3 主要引入了两个新的技术:倒残差结构(inverted residuals)和线性注意力模块(linear bottlenecks)。倒残差结构能够在保持网络深度和计算效率的基础上提高神经网络的表达能力,从而提高准确性。而线性注意力模块则可以动态地调整网络在不同特征层上的注意力分配,以更好地适应输入图像的特征分布。 MobileNetV3 有不同的变体,分别面向不同的平台和应用场景。其中,MobileNetV3 Large 适合要求较高准确性但计算资源相对充足的场景,而MobileNetV3 Small 则更适合要求推断速度较快的场景。 相比于先前的MobileNet版本,MobileNetV3 在准确性和推断速度之间取得了更好的平衡。基于此,MobileNetV3 可以被广泛应用于嵌入式设备、移动端应用和其他计算资源有限的场景,以提供高质量的图像分类和目标检测能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值