MobileNetV2原理说明及实践落地

这篇具有很好参考价值的文章主要介绍了MobileNetV2原理说明及实践落地。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文参考:

轻量级网络——MobileNetV2_Clichong的博客-CSDN博客_mobilenetv2

1、MobileNetV2介绍

MobileNetV1主要是提出了可分离卷积的概念,大大减少了模型的参数个数,从而缩小了计算量。但是在CenterNet算法中作为BackBone效果并不佳,模型收敛效果不好导致目标检测的准确率不高。

MobileNetV2在MobileNetV1的DW和PW的基础上进行了优化,使得准确率更高,作为CenterNet算法的BackBone效果也可以。它的两个亮点是:

  • Inverted Residuals:倒残差结构
  • Linear Bottlenecks:结构的最后一层采用线性层

2、MobileNetV2的结构

(1)倒残差结构

mobilenetv2,神经网络,深度学习,计算机视觉,目标检测

ResNet网络:残差结构是先用1*1卷积降维 再升维 的操作,所以两头大中间小。

MobileNetV2中,残差结构是先用1*1卷积升维 再降维 的操作,所以两头小中间大。

在MobileNetV2中采用了新的激活函数:ReLU6,它的定义如下:

mobilenetv2,神经网络,深度学习,计算机视觉,目标检测

(2)线性Bottlenecks

针对倒残差结构最后一层的卷积层,采用了线性的激活函数(f(x)=x,可以认为没有激活函数),而不是使用ReLU6激活函数。

原因解释:

mobilenetv2,神经网络,深度学习,计算机视觉,目标检测

 ReLU激活函数对于低维的信息可能会造成比较大的损失,而对于高维的特征信息造成的损失很小。而且由于倒残差结构是两头小中间大,所以输出的是一个低维的特征信息。所以使用一个线性的激活函数避免特征损失。

低维解释:低维针对的是channel,低维意味着[batch, channel, height, width]中的height和width还较大。

(3)整体结构

mobilenetv2,神经网络,深度学习,计算机视觉,目标检测

当stride=1且输入特征矩阵与输出特征矩阵shape相同时才有shortcut链接。

 3、MobileNetV2的pytorch实现

import torch
import torch.nn as nn
import torchvision

# 分类个数
num_class = 5

# DW卷积
def Conv3x3BNReLU(in_channels, out_channels, stride, groups):
    return nn.Sequential(
        # stride=2,wh减半; stride=1,wh不变
        nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, stride=stride, padding=1, groups=groups),
        nn.BatchNorm2d(out_channels),
        nn.ReLU6(inplace=True)
    )

# PW卷积
def Conv1x1BNReLU(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),
        nn.ReLU6(inplace=True)
    )

# PW卷积(Linear)没有使用激活函数
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 InvertedResidual(nn.Module):
    # t为扩展因子
    def __init__(self, in_channels, out_channels, expansion_factor, stride):
        super(InvertedResidual, self).__init__()
        self.stride = stride
        self.in_channels = in_channels
        self.out_channels = out_channels
        mid_channels = (in_channels * expansion_factor)

        # 先1*1卷积升维,再1*1卷积降维
        self.bottleneck = nn.Sequential(
            # 升维操作
            Conv1x1BNReLU(in_channels, mid_channels),
            # DW卷积,降低参数量
            Conv3x3BNReLU(mid_channels, mid_channels, stride, groups=mid_channels),
            # 降维操作
            Conv1x1BN(mid_channels, out_channels)
        )

        # stride=1才有shortcut,此方法让原本不相同的channels相同
        if self.stride == 1:
            self.shortcut = Conv1x1BN(in_channels, out_channels)

    def forward(self, x):
        out = self.bottleneck(x)
        out = (out + self.shortcut(x)) if self.stride == 1 else out
        return out

class MobileNetV2(nn.Module):
    def make_layer(self, in_channels, out_channels, stride, factor, block_num):
        layers = []
        layers.append(InvertedResidual(in_channels, out_channels, factor, stride))
        for i in range(block_num):
            layers.append(InvertedResidual(out_channels, out_channels, factor, 1))
        return nn.Sequential(*layers)

    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 __init__(self, num_classes=num_class, t=6):
        super(MobileNetV2, self).__init__()

        self.first_conv = Conv3x3BNReLU(3, 32, 2, groups=1)
        # 32 -> 16 stride=1 wh不变
        self.layer1 = self.make_layer(in_channels=32, out_channels=16, stride=1, factor=1, block_num=1)
        # 16 -> 24 stride=2 wh减半
        self.layer2 = self.make_layer(in_channels=16, out_channels=24, stride=2, factor=t, block_num=2)
        # 24 -> 32 stride=2 wh减半
        self.layer3 = self.make_layer(in_channels=24, out_channels=32, stride=2, factor=t, block_num=3)
        # 32 -> 64 stride=2 wh减半
        self.layer4 = self.make_layer(in_channels=32, out_channels=64, stride=2, factor=t, block_num=4)
        # 64 -> 96 stride=1 wh不变
        self.layer5 = self.make_layer(in_channels=64, out_channels=96, stride=1, factor=t, block_num=3)
        # 96 -> 160 stride=2 wh减半
        self.layer6 = self.make_layer(in_channels=96, out_channels=160, stride=2, factor=t, block_num=3)
        # 160 -> 320 stride=1 wh不变
        self.layer7 = self.make_layer(in_channels=160, out_channels=320, stride=1, factor=t, block_num=1)
        # 320 -> 1280 单纯的升维操作
        self.last_conv = Conv1x1BNReLU(320, 1280)

        self.avgpool = nn.AvgPool2d(kernel_size=7, stride=1)
        self.dropout = nn.Dropout2d(p=0.2)
        self.linear = nn.Linear(in_features=1280, out_features=num_classes)
        self.init_params()

    def forward(self, x):
        x = self.first_conv(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = self.layer5(x)
        x = self.layer6(x)
        x = self.layer7(x)
        x = self.last_conv(x)
        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.dropout(x)
        x = self.linear(x)
        return x

if __name__ == '__main__':
    model = MobileNetV2()

    input = torch.randn(1, 3, 224, 224)
    out = model(input)
    print(out.shape)

4、MobileNetV2作为CenterNet的BackBone

import torch
import torch.nn as nn

# DW卷积
def Conv3x3BNReLU(in_channels, out_channels, stride, groups):
    return nn.Sequential(
        # stride=2,wh减半; stride=1,wh不变
        nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, stride=stride, padding=1, groups=groups),
        nn.BatchNorm2d(out_channels),
        nn.ReLU6(inplace=True)
    )

# PW卷积
def Conv1x1BNReLU(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),
        nn.ReLU6(inplace=True)
    )

# PW卷积(Linear)没有使用激活函数
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 InvertedResidual(nn.Module):
    # t为扩展因子
    def __init__(self, in_channels, out_channels, expansion_factor, stride):
        super(InvertedResidual, self).__init__()
        self.stride = stride
        self.in_channels = in_channels
        self.out_channels = out_channels
        mid_channels = (in_channels * expansion_factor)

        # 先1*1卷积升维,再1*1卷积降维
        self.bottleneck = nn.Sequential(
            # 升维操作
            Conv1x1BNReLU(in_channels, mid_channels),
            # DW卷积,降低参数量
            Conv3x3BNReLU(mid_channels, mid_channels, stride, groups=mid_channels),
            # 降维操作
            Conv1x1BN(mid_channels, out_channels)
        )

        # stride=1才有shortcut,此方法让原本不相同的channels相同
        if self.stride == 1:
            self.shortcut = Conv1x1BN(in_channels, out_channels)

    def forward(self, x):
        out = self.bottleneck(x)
        out = (out + self.shortcut(x)) if self.stride == 1 else out
        return out

class MobileNetV2(nn.Module):
    def make_layer(self, in_channels, out_channels, stride, factor, block_num):
        layers = []
        layers.append(InvertedResidual(in_channels, out_channels, factor, stride))
        for i in range(block_num):
            layers.append(InvertedResidual(out_channels, out_channels, factor, 1))
        return nn.Sequential(*layers)

    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 __init__(self, heads, t=6):
        super(MobileNetV2, self).__init__()

        num_classes = heads['hm']

        self.first_conv = Conv3x3BNReLU(3, 32, 2, groups=1)
        # 32 -> 16 stride=1 wh不变
        self.layer1 = self.make_layer(in_channels=32, out_channels=16, stride=1, factor=1, block_num=1)
        # 16 -> 24 stride=2 wh减半
        self.layer2 = self.make_layer(in_channels=16, out_channels=24, stride=2, factor=t, block_num=2)
        # 24 -> 32 stride=2 wh减半
        self.layer3 = self.make_layer(in_channels=24, out_channels=32, stride=2, factor=t, block_num=3)
        # 32 -> 64 stride=2 wh减半
        self.layer4 = self.make_layer(in_channels=32, out_channels=64, stride=2, factor=t, block_num=4)
        # 64 -> 96 stride=1 wh不变
        self.layer5 = self.make_layer(in_channels=64, out_channels=96, stride=1, factor=t, block_num=3)
        # 96 -> 160 stride=2 wh减半
        self.layer6 = self.make_layer(in_channels=96, out_channels=160, stride=2, factor=t, block_num=3)
        # 160 -> 320 stride=1 wh不变
        self.layer7 = self.make_layer(in_channels=160, out_channels=320, stride=1, factor=t, block_num=1)
        # 320 -> 1280 单纯的升维操作
        self.last_conv = Conv1x1BNReLU(320, 1280)
        self.init_params()

        self.hm = nn.Conv2d(20, num_classes, kernel_size=1)
        self.wh = nn.Conv2d(20, 2, kernel_size=1)
        self.reg = nn.Conv2d(20, 2, kernel_size=1)


    def forward(self, x):
        x = self.first_conv(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = self.layer5(x)
        x = self.layer6(x)
        x = self.layer7(x)
        x = self.last_conv(x)

        y = x.view(x.shape[0], -1, 128, 128)
        z = {}
        z['hm'] = self.hm(y)
        z['wh'] = self.wh(y)
        z['reg'] = self.reg(y)
        return [z]

if __name__ == '__main__':
    heads = {'hm': 10, 'wh': 2, 'reg': 2}
    model = MobileNetV2(heads)

    input = torch.randn(1, 3, 512, 512)
    out = model(input)
    print(out.shape)

5、MobileNetV2在CenterNet目标检测落地情况

(1)训练情况

训练loss,mobilenetV1在batch_size=16时最少达到4.0左右,而mobileNetV2在batch_size=16时最少达到0.5以下。与DLASeg的效果基本接近。

mobilenetv2,神经网络,深度学习,计算机视觉,目标检测

(2)目标检测效果

mobilenetv2,神经网络,深度学习,计算机视觉,目标检测

检测效果也与DLASeg基本接近

(3)模型参数量

DLASeg为2000W个左右

MobileNetV1为320W个左右

MobileNetV2为430W个左右,总模型大小为17M

(4)CPU运行时间

DLASeg为1.2s

MobileNetV1为250ms

MobileNetV2为600ms

 文章来源地址https://www.toymoban.com/news/detail-720235.html

到了这里,关于MobileNetV2原理说明及实践落地的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 机器学习笔记 - 基于MobileNetV2的迁移学习训练关键点检测器

            StanfordExtra数据集包含12000张狗的图像以及关键点和分割图图。 GitHub - benjiebob/StanfordExtra:12k标记的野外狗实例,带有2D关键点和分割。我们的 ECCV 2020 论文发布的数据集:谁把狗排除在外?3D 动物重建,循环中期望最大化。 https://github.com/benjiebob/StanfordExtra      

    2024年02月10日
    浏览(46)
  • DeepLabV3+:Mobilenetv2的改进以及浅层特征和深层特征的融合

    目录 Mobilenetv2的改进 浅层特征和深层特征的融合 完整代码 参考资料 在DeeplabV3当中,一般不会5次下采样,可选的有3次下采样和4次下采样。因为要进行五次下采样的话会损失较多的信息。 在这里mobilenetv2会从之前写好的模块中得到,但注意的是,我们在这里获得的特征是[-

    2024年01月19日
    浏览(56)
  • MobileNetv1,v2网络详解并使用pytorch搭建MobileNetV2及基于迁移学习训练(超详细|附训练代码)

    目录 前言 学习资料 一、MobilnetV1 二、MobileNetV2 倒残差结构:         那么什么是relu6激活函数呢​编辑  Linear Bottlenecks 三、MobileNetV3 SE模块:  更新激活函数: 重新设计耗时层结构: 使用pytorch搭建MobileNetv2网络结构 3.1 model.py 3.2 train.py 3.3 predict.py  3.4 class_indices.json 使用

    2024年02月05日
    浏览(49)
  • MobileNetV3详细原理(含torch源码)

    作者:爱笑的男孩。 个人简介:打工人。 持续分享:机器学习、深度学习、python相关内容、日常BUG解决方法及WindowsLinux实践小技巧。 如发现文章有误,麻烦请指出,我会及时去纠正。有其他需要可以私信我或者发我邮箱:zhilong666@foxmail.com 目录 MobileNetV3原理 MobileNetV3创新点

    2023年04月20日
    浏览(41)
  • MobileNetV1详细原理(含torch源码)

    目录 MobileNetV1原理 MobileNet V1的网络结构如下: 为什么要设计MobileNet: MobileNetV1的主要特点如下: MobileNetV1的创新点: MobileNetV1源码(torch版) 训练10个epoch的效果         MobileNet V1是一种轻量级的卷积神经网络,能够在保持较高准确率的情况下具有较少的参数量和计算时

    2023年04月19日
    浏览(38)
  • DDD技术方案落地实践

    从接触领域驱动设计的初学阶段,到实现一个旧系统改造到DDD模型,再到按DDD规范落地的3个的项目。对于领域驱动模型设计研发,从开始的各种疑惑到吸收各种先进的理念,目前在技术实施这一块已经基本比较成熟。在既往经验中总结了一些在开发中遇到的技术问题和解决方

    2024年02月05日
    浏览(49)
  • 弹性伸缩落地实践

    HPA(Horizontal Pod Autoscaler)是 Kubernetes 中的一种资源自动伸缩机制,用于根据某些指标动态调整 Pod 的副本数量。 负载波动:当您的应用程序的负载经常发生波动时,HPA 可以自动调整 Pod 的副本数量,以适应负载的变化。例如,对于 Web 应用程序,在高峰期需要更多的副本以处

    2024年02月05日
    浏览(98)
  • 2. 云计算的落地实践(上)

    云计算如何落地实践 物理机与虚拟机 容器虚拟技术 Docker 横空出世 PaaS 时代来临 业界实践 我们上一节讲了有关云计算的概念,那么我们思考一下,云计算在现实中是如何落地实践的呢? 如今云计算落地实践中,已经形成了一套有效的体系。一家公司的传统业务如数据库、计

    2024年02月09日
    浏览(42)
  • 3. 云计算的落地实践(下)

    云计算如何落地实践 ISO镜像文件 创建虚拟机入门 创建数据节点 配置 VMWare创建虚拟机三种网络模式 上一章我们讲了云计算的业界实践,即: 搭建IaaS后,用于创建虚拟机,在虚拟机上部署PaaS,用于管理同时部署在虚拟机上的容器,这就是业界普遍的云计算实践 。 ok,但是我

    2024年02月02日
    浏览(29)
  • 云原生落地实践的25个步骤

    一、什么是云原生? 云原生从字面意思上来看可以分成云和原生两个部分。 云是和本地相对的,传统的应用必须跑在本地服务器上,现在流行的应用都跑在云端,云包含了IaaS,、PaaS和SaaS。 原生就是土生土长的意思,我们在开始设计应用的时候就考虑到应用将来是运行云环境

    2024年02月14日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包