图像分类:Pytorch图像分类之-- MobileNet系列模型

news2024/10/5 5:19:48

文章目录

      • 前言
      • MobileNetV1模型介绍
        • DW(Depthwise Convolution)卷积
        • PW (Pointwise Convolution)卷积
        • 深度可分离卷积(DW+PW)
        • ReLU6激活函数的介绍
        • MobileNet V1网络结构
        • MobileNet V1程序
      • MobileNetV2模型介绍
        • Inverted residual block介绍
        • Inverted residual block 和 residual block的区别
        • MobileNetV2网络结构
        • MobileNetV2程序

前言

 MobileNet网络专注于移动端或者嵌入式设备中的轻量级CNN,相比于传统卷积神经网络,在准确率小幅度降低的前提下大大减少模型参数与运算量。

MobileNetV1模型介绍

 MobileNetV1提出了 Depthwise Separable Convolutions(深度可分离卷积);深度可分离卷积过程被拆分成了两个部分:深度卷积(Depthwise Convolution)与逐点卷积层(Pointwise Convolution)。

DW(Depthwise Convolution)卷积

 DW卷积中,每个卷积核的channel都为1,每个卷积核只负责与输入特征矩阵中的1个channel进行卷积运算,然后再得到相应的输出特征矩阵中的1个channel。
如下图所示:
在这里插入图片描述

PW (Pointwise Convolution)卷积

PW卷积的特点是卷积核的维度是1*1,从输入到输出可以改变维度,DW和PW通常是一起使用。

深度可分离卷积(DW+PW)

1、深度可分离卷积的计算量
 对于深度可分离卷积的DW部分,设输入特征图通道数为M,对特征图进行卷积的核大小为K,则该部分的计算量:
K ∗ K ∗ M K*K*M KKM
 对于深度可分离卷积的PW部分,设输出特征图通道数为N,该部分的计算量: 1 ∗ 1 ∗ M ∗ N 1*1*M*N 11MN
则深度可分离卷积的计算量为:
K ∗ K ∗ M + 1 ∗ 1 ∗ M ∗ N K*K*M + 1*1*M*N KKM+11MN

在这里插入图片描述

2、传统卷积的计算量
 设输入特征图像的通道为M,输出特征图像的通道为N,对输入特征图像进行卷积的卷积核大小为K,则传统卷积的计算量为: K ∗ K ∗ M ∗ N K*K*M*N KKMN
在这里插入图片描述
3、深度可分离卷积和传统卷积比值
 深度可分离卷积的计算量要比传统卷积计算量小,从以下比值可以可以看出:
深度可分离卷积 传统卷积 = K ∗ K ∗ M + 1 ∗ 1 ∗ M ∗ N K ∗ K ∗ M ∗ N = 1 N + 1 K 2 {深度可分离卷积\over 传统卷积}={K*K*M + 1*1*M*N \over K*K*M*N }= {1\over N }+{1\over K^2 } 传统卷积深度可分离卷积=KKMNKKM+11MN=N1+K21

理论上普通卷积计算量是DW+PW的8到9倍。

ReLU6激活函数的介绍

ReLU6就是普通的ReLU但是限制最大输出值为6(对输出值做clip),这是为了在移动端设备float16的低精度的时候,也能有很好的数值分辨率,如果对ReLU的激活范围不加限制,输出范围为0到正无穷,如果激活值非常大,分布在一个很大的范围内,则低精度的float16无法很好地精确描述如此大范围的数值,带来精度损失。

如下图所示:
在这里插入图片描述

MobileNet V1网络结构

在这里插入图片描述

MobileNet V1程序

import torch
import torch.nn as nn
from torchinfo import summary


def conv_bn(in_channel, out_channel, stride=1):
    """

    传统卷积块:conv + BN + Act
    """
    return nn.Sequential(
        nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=3,
                  stride=stride, padding=1, bias=False),
        nn.BatchNorm2d(out_channel),
        nn.ReLU6(inplace=True)
    )


def conv_dsc(in_channel, out_channel, stride=1):
    """

    深度可分离卷积:DW+PW
    """
    return nn.Sequential(
        nn.Conv2d(in_channels=in_channel, out_channels=in_channel,kernel_size=3,
                  stride=stride, padding=1, groups=in_channel, bias=False),
        nn.BatchNorm2d(in_channel),
        nn.ReLU6(inplace=True),


        nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=1,
                  stride=1, padding=0, bias=False),
        nn.BatchNorm2d(out_channel),
        nn.ReLU6(inplace=True)

    )
class MobileNetV1(nn.Module):
    def __init__(self, in_channel=3, num_classes=1000):
        super(MobileNetV1, self).__init__()
        self.num_classes = num_classes
        self.stage1 = nn.Sequential(
            conv_bn(in_channel=in_channel, out_channel=32, stride=2),
            conv_dsc(in_channel=32, out_channel=64, stride=1),
            conv_dsc(in_channel=64, out_channel=128, stride=2),
            conv_dsc(in_channel=128, out_channel=128, stride=1),
            conv_dsc(in_channel=128, out_channel=256, stride=2),
            conv_dsc(in_channel=256, out_channel=256, stride=1),
            conv_dsc(in_channel=256, out_channel=512, stride=2)
        )

        self.stage2 = nn.Sequential(
            conv_dsc(in_channel=512, out_channel=512,  stride=1),
            conv_dsc(in_channel=512, out_channel=512, stride=1),
            conv_dsc(in_channel=512, out_channel=512, stride=1),
            conv_dsc(in_channel=512, out_channel=512, stride=1),
            conv_dsc(in_channel=512, out_channel=512, stride=1)
        )

        self.stage3 = nn.Sequential(
            conv_dsc(in_channel=512, out_channel=1024, stride=2),
            conv_dsc(in_channel=1024, out_channel=1024, stride=2)
        )
        self.avg1 = nn.AdaptiveAvgPool2d((1, 1))
        self.fc1 = nn.Linear(1024, num_classes)

    def forward(self, x):
        x = self.stage1(x)
        x = self.stage2(x)
        x = self.stage3(x)
        x = self.avg1(x)
        x = x.view(-1, 1024)
        x = self.fc1(x)

        return x

MobileNetV2模型介绍

 MobileNetV2是MobileNet的升级版,它具有一个非常重要的特点就是使用了Inverted resblock,整个mobilenetv2都由Inverted resblock组成。

Inverted residual block介绍

 通道越少,卷积层的乘法计算量就越小。那么如果整个网络都是低维的通道,那么整体计算速度就会很快。然而,这样效果并不好,没有办法提取到整体的足够多的信息。所以,如果提取特征数据的话,我们可能更希望有高维的通道来做这个事情。MobileNetV2就设计这样一个结构来达到平衡。
 MobileNetV2中首先扩展维度,然后用depthwise conv来提取特征,最后再压缩数据,让网络变小。
在这里插入图片描述
 Inverted residual block可以分为两个部分:主要包括Expansion layer,Depthwise Convolution,Projection layer。
Expansion layer表示扩展层,使用1x1卷积,目的是将低维空间映射到高维空间(升维)。
Projection layer表示投影层,使用1x1卷积,目的是把高维特征映射到低维空间去(降维)。
Depthwise Convolution表示深度可分离卷积,完成卷积功能,降低计算量、参数量。

在这里插入图片描述

Inverted residual block 和 residual block的区别

1、结构的不同如下图

2、激活函数的不同:Inverted residual block中使用的是ReLu6;residual block中使用的是ReLu。

MobileNetV2网络结构

在这里插入图片描述

MobileNetV2程序

import torch
import torch.nn as nn
from torchinfo import summary
import torchvision.models.mobilenetv2
# ------------------------------------------------------#
#   这个函数的目的是确保Channel个数能被8整除。
#	很多嵌入式设备做优化时都采用这个准则
# ------------------------------------------------------#
def _make_divisible(v, divisor, min_value=None):
    if min_value is None:
        min_value = divisor
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    if new_v < 0.9 * v:
        new_v += divisor
    return new_v

# -------------------------------------------------------------#
#   Conv+BN+ReLU经常会用到,组在一起
#   参数顺序:输入通道数,输出通道数...
#		最后的groups参数:groups=1时,普通卷积;
#						 groups=输入通道数in_planes时,DW卷积=深度可分离卷积
#	pytorch官方继承自nn.sequential,想用它的预训练权重,就得听它的
# -------------------------------------------------------------#
class ConvBNReLU(nn.Sequential):
    def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1):
        padding = (kernel_size - 1) // 2
        super(ConvBNReLU, self).__init__(
            nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False),
            nn.BatchNorm2d(out_planes),
            nn.ReLU6(inplace=True)
        )

# ------------------------------------------------------#
#   InvertedResidual,先升维后降维
#   参数顺序:输入通道数,输出通道数,步长,变胖倍数(扩展因子)
# ------------------------------------------------------#
class InvertedResidual(nn.Module):
    def __init__(self, inp, oup, stride, expand_ratio):
        super(InvertedResidual, self).__init__()
        self.stride = stride
        assert stride in [1, 2]

        hidden_dim = int(round(inp * expand_ratio))
        self.use_res_connect = self.stride == 1 and inp == oup

        layers = []
        if expand_ratio != 1:
            layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1))
        layers.extend([
            ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim),
            nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
            nn.BatchNorm2d(oup),
        ])
        self.conv = nn.Sequential(*layers)

    def forward(self, x):
        if self.use_res_connect:
            return x + self.conv(x)
        else:
            return self.conv(x)

class MobileNetV2(nn.Module):
    def __init__(self, num_classes=1000, width_mult=1.0, inverted_residual_setting=None, round_nearest=8):
        super(MobileNetV2, self).__init__()
        block = InvertedResidual
        input_channel = 32
        last_channel = 1280

        if inverted_residual_setting is None:
            inverted_residual_setting = [
                # t表示扩展因子(变胖倍数);c是通道数;n是block重复几次;
                #	s:stride步长,只针对第一层,其它s都等于1
                [1, 16, 1, 1],
                [6, 24, 2, 2],
                [6, 32, 3, 2],
                [6, 64, 4, 2],
                [6, 96, 3, 1],
                [6, 160, 3, 2],
                [6, 320, 1, 1],
            ]

        if len(inverted_residual_setting) == 0 or len(inverted_residual_setting[0]) != 4:
            raise ValueError("inverted_residual_setting should be non-empty "
                             "or a 4-element list, got {}".format(inverted_residual_setting))

        input_channel = _make_divisible(input_channel * width_mult, round_nearest)
        self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest)
        features = [ConvBNReLU(3, input_channel, stride=2)]

        for t, c, n, s in inverted_residual_setting:
            output_channel = _make_divisible(c * width_mult, round_nearest)
            for i in range(n):
                stride = s if i == 0 else 1
                features.append(block(input_channel, output_channel, stride, expand_ratio=t))
                input_channel = output_channel

        features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1))
        self.features = nn.Sequential(*features)

        self.classifier = nn.Sequential(
            nn.Dropout(0.2),
            nn.Linear(self.last_channel, num_classes),
        )

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.ones_(m.weight)
                nn.init.zeros_(m.bias)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.zeros_(m.bias)

    def forward(self, x):
        x = self.features(x)
        x = x.mean([2, 3])
        x = self.classifier(x)
        return x

model = MobileNetV2()
summary(model)

如果有错误欢迎指正,如果帮到您请点赞加收藏哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/421586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

链接、包管理工具、polyrepo、monorepo以及Lerna 工具的使用

nodejs 链接、包管理工具、多包管理以及Lerna 工具的使用jcLee95&#xff1a;https://blog.csdn.net/qq_28550263?spm1001.2101.3001.5343 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/129903902 目 录1. 概述…

bjdctf_2020_babyrop2-fmt-leak canary

1,三连 分析:开了canary&#xff0c;先想办法获取canary值。 2&#xff0c;IDA静态分析&#xff0c;查看可以泄露canary的地方&#xff0c;否则只能爆破了 发现可以格式化字符串函数泄露的地方&#xff1a; 栈帧结构&#xff1a; 高地址 -------------- gift_ret栈帧 ------…

【算法宇宙——在故事中学算法】背包dp之01背包问题

唯手熟尔方成艺&#xff0c;唯读书能致卓越。勤学苦练方可成&#xff0c;路漫漫其修远兮&#xff01; 文章目录前言正文故事总结前言 尽管计算机是门严谨的学科&#xff0c;但正因为严谨&#xff0c;所以要有趣味才能看得下去。在笔者的前几篇算法类文章中&#xff0c;都采用了…

智慧公厕系统的应用示例

近几年&#xff0c;在一些高速服务区或者一些城市的公共厕所当中&#xff0c;总会看见一些富有科技感的硬件&#xff0c;比如厕位有无人指示灯、厕所除臭杀菌机、智能取纸机、智能洗手台镜面广告机等。现在在衡量城市发展的过程中&#xff0c;总会以城市的建设&#xff0c;城市…

Weblogic远程代码执行漏洞 CVE-2023-21839

漏洞简介 WebLogic Core远程代码执行漏洞&#xff08;CVE-2023-21839&#xff09;&#xff0c;该漏洞允许未经身份验证的远程攻击者通过T3/IIOP协议进行 JNDI lookup 操作&#xff0c;破坏易受攻击的WebLogic服务器&#xff0c;成功利用此漏洞可能导致Oracle WebLogic服务器被接…

MySQL可重复读事务隔离具体是怎么实现的

事务的启动会有的操作 事务的隔离等级有四种&#xff0c;现在说默认的可重复读&#xff0c;可重复读就是一个事务执行过程中看到的数据&#xff0c;总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下&#xff0c;未提交变更对其他事务也是不可见的。 可重复…

Java阶段一Day22

Java阶段一Day22 文章目录Java阶段一Day22线程安全synchronized教师总结新单词多线程多线程并发安全问题概念例synchronized关键字同步方法同步块在静态方法上使用synchronized互斥锁总结重点:多线程并发安全问题聊天室(续)实现服务端发送消息给客户端服务端转发消息给所有客户…

内网穿透实现在外远程连接RabbitMQ服务

文章目录前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址转载自远控源码文章&#xff1a;无公网IP&#xff0c;…

Linux Systemd type=simple和type=forking的区别

Typeforking 使用Typeforking时&#xff0c;要求ExecStart启动的命令自身就是以daemon模式运行的。 而以daemon模式运行的进程都有一个特性&#xff1a;总是会有一个瞬间退出的中间父进程&#xff0c;例如&#xff0c;nginx命令默认以daemon模式运行&#xff0c;所以可直接将其…

Nodejs vm/vm2沙箱逃逸

文章目录什么是沙箱以及VM&#xff1f;vm模块nodejs作用域vm沙箱vm沙箱逃逸vm2例题分析&#xff1a;&#xff08;待补充&#xff09;[HFCTF2020]JustEscape[HZNUCTF 2023 final]eznode參考文章:什么是沙箱以及VM&#xff1f; 什么是沙箱&#xff1a; 沙箱就是能够像一个集装箱…

Ansys Speos | 联合 optiSLang 背光板设计优化方案

在这个例子中&#xff0c;讲述如何建模一个典型的背光单元及其与亮度和均匀性有关的照度分布。其中一个关键特点是使用了Speos 3D Texture功能&#xff0c;这是最初开发的用于背光单元产品&#xff0c;并可用于设计导光板&#xff0c;亮度增强膜(BEF)和由数千/数百万组成的背光…

《程序员面试金典(第6版)》面试题 10.03. 搜索旋转数组(二分法,分钟思想,入门题目)

题目描述 搜索旋转数组。给定一个排序后的数组&#xff0c;包含n个整数&#xff0c;但这个数组已被旋转过很多次了&#xff0c;次数不详。请编写代码找出数组中的某个元素&#xff0c;假设数组元素原先是按升序排列的。若有多个相同元素&#xff0c;返回索引值最小的一个。 示例…

C学习笔记2

1、二进制由 0 和 1 两个数字组成&#xff0c;使用时必须以0b或0B&#xff08;不区分大小写&#xff09;开头 2、符号位进制形式进制数据 &#xff08;进制形式决定后面的数据是哪种进制&#xff09; 3、合法的二进制 int a 0b101; // 0b是二进制的进制形式 101是进制…

buildroot使用外部编译链编译bluez蓝牙工具

在开发ublox w263 wifi蓝牙时&#xff0c;之前是使用yocto系统集成编译出的bluez工具&#xff0c;减少了自己编译工具软件和依赖库的工作&#xff0c;切换项目使用原生linux系统后&#xff0c;所以的软件需要自己编译&#xff0c;不想编译每个依赖文件和库&#xff0c;所以使用…

Pytorch深度学习笔记(三)线性模型

目录 1.机械学习的过程 2.线性模型 推荐课程&#xff1a;2.线性模型_哔哩哔哩_bilibili 1.机械学习的过程 机械学习的过程&#xff1a; 1.准备数据集DataSet——>2.选择模型Model——>3.训练Training——>4.推理Infering 监督学习&#xff1a;用已知标签的训练样本训…

Spark大数据处理讲课笔记3.1 掌握RDD的创建

文章目录零、本节学习目标一、RDD为何物&#xff08;一&#xff09;RDD概念&#xff08;二&#xff09;RDD示例&#xff08;三&#xff09;RDD主要特征二、做好准备工作&#xff08;一&#xff09;准备文件1、准备本地系统文件2、启动HDFS服务3、上传文件到HDFS&#xff08;二&…

4年软件测试工作经验,跳槽之后面试20余家公司的总结

先说一下自己的个人情况&#xff0c;普通二本计算机专业毕业&#xff0c;懂python&#xff0c;会写脚本&#xff0c;会selenium&#xff0c;会性能&#xff0c;然而离职后到今天都没有收到一份offer&#xff01;一直在待业中&#xff0c;从离职第一天就开始准备简历&#xff0c…

快排的非递归实现

其思想与递归实现快排完全相同&#xff0c;可以先将第一次要排序的右边界和左边界先后入栈&#xff0c;然后判断栈 是否为空&#xff0c;不为空就出栈顶元素&#xff0c;并删除一次&#xff0c;由于栈是先进的后出&#xff0c;所以先出来的应该是左界&#xff0c; 再进行一次…

【C++】STL——unordered_map和unordered_set的介绍和使用

unordered_set和unordered_map的介绍和使用 文章目录unordered_set和unordered_map的介绍和使用一、unordered系列关联式容器二、unordered_set1.unordered_set的介绍2.unordered_set的构造方式3.unordered_set的函数接口说明4.unordered_multiset的介绍及使用三、unordered_ma…

【建议收藏】数据库 SQL 入门——约束(内附演示)

文章目录&#x1f4da;引言&#x1f4d6;约束&#x1f4d1;非空约束&#x1f4d1;唯一约束&#x1f4d1;主键约束&#x1f4d1;默认约束&#x1f4d1;检查约束&#x1f4d1;外键约束&#x1f516;外键的添加&#x1f516;删除/更新外键&#x1f4cd;总结&#x1f4da;引言 &…