当前位置:   article > 正文

YOLOv5改进系列(三) 本文(7万字) | 更换主干backbone | MobileNetV3 | ShuffleNetV2 | EfficientNetv2 | GhostNet | 等 |_yolov5添加mobilenetv3

yolov5添加mobilenetv3

点击进入专栏:
《人工智能专栏》 Python与Python | 机器学习 | 深度学习 | 目标检测 | YOLOv5及其改进 | YOLOv8及其改进 | 关键知识点 | 各种工具教程


代码函数调用关系图(全网最详尽-重要)

因文档特殊,不能在博客正确显示,请移步以下链接!

图解YOLOv5_v7.0代码结构与调用关系(点击进入可以放大缩小等操作)

预览:
在这里插入图片描述


文章目录

  • MobileNetV3
    • 一、MobileNetV3原理
      • 1.1 MobileNetV3简介
      • 1.2 MobileNetV3相关技术
    • 二、YOLOv5结合MobileNetV3_small
      • 2.1 添加顺序
      • 2.2 具体添加步骤
        • 第①步:在common.py中添加MobileNetV3模块
        • 第②步:在yolo.py文件里的parse_model函数加入类名
        • 第③步:创建自定义的yaml文件
        • 第④步:验证是否加入成功
        • 第⑤步:修改train.py中 ‘--cfg’默认参数
    • 三、YOLOv5结合MobileNetV3_large
        • 第③步:创建自定义的yaml文件
  • ShuffleNetV2
    • 一、ShuffleNet介绍
      • 1.1 ShuffleNet V1
      • 1.2 ShuffleNet V2
    • 二、YOLOv5结合ShuffleNet V2
      • 2.1 添加顺序
      • 2.2 具体添加步骤
        • 第①步:在common.py中添加ShuffleNet V2模块
        • 第②步:在yolo.py文件里的parse_model函数加入类名
        • 第③步:创建自定义的yaml文件
        • 第④步:验证是否加入成功
        • 第⑤步:修改train.py中 ‘--cfg’默认参数
  • EfficientNetv2
    • 一、EfficientNet介绍
      • 1.1 EfficientNet V1
      • 1.2 EfficientNet V2
    • 二、YOLOv5结合EfficientNetv2
      • 2.1 添加顺序
      • 2.2 具体添加步骤
        • 第①步:在common.py中添加EfficientNetv2模块
        • 第②步:在yolo.py文件里的parse_model函数加入类名
        • 第③步:创建自定义的yaml文件
        • 第④步:验证是否加入成功
        • 第⑤步:修改train.py中 ‘--cfg’默认参数
  • GhostNet
    • 一、GhostNet介绍
      • 1.1 简介
      • 1.2 基本单元
      • 1.3 网络结构
    • 二、YOLOv5结合GhostNet
      • 2.1 添加顺序
      • 2.2 具体添加步骤
        • 第①步:在common.py中添加GhostNet模块
        • 第②步:在yolo.py文件里的parse_model函数加入类名
        • 第③步:创建自定义的yaml文件
        • 第④步:验证是否加入成功
        • 第⑤步:修改train.py中 ‘--cfg’默认参数
  • MobileViTv1
    • 一、MobileViT v1介绍
      • 1.1 简介
      • 1.2 网络结构
        • (1)MV2
        • (2)MobileViTblock
      • 1.3 实验
        • (1)和CNN对比
        • (2)和ViT对比
        • (3)移动端目标检测
        • (4)移动端实例分割
        • (5)移动设备的性能
    • 二、具体添加方法
        • 第①步:在common.py中添加MobileViTv1模块
        • 第②步:修改yolo.py文件
        • 第③步:创建自定义的yaml文件
        • 第④步 验证是否加入成功
  • MobileViTv3
    • 一、MobileViT v3介绍
    • 二、具体添加方法
        • 第①步:在common.py中添加MobileViT v3模块
        • 第②步:修改yolo.py文件
        • 第③步:创建自定义的yaml文件
        • 第④步 验证是否加入成功
  • SwinTransformer
      • Swin Transformer 对比 Vision Transformer
      • Swin Transformer 结构
      • 不同尺寸的 Swin Transformer 结构参数对比
      • 在 `YOLO` 项目中添加 `SwinTransformer` 主干的方式
        • 第一步
        • 第二步
        • 第三步
          • YOLOv5中单独加一层
          • 替换整个 YOLOv5 主干


MobileNetV3

一、MobileNetV3原理

1.1 MobileNetV3简介

MobileNetV3,是谷歌在2019年3月21日提出的轻量化网络架构,在前两个版本的基础上,加入神经网络架构搜索(NAS)和h-swish激活函数,并引入SE通道注意力机制,性能和速度都表现优异,受到学术界和工业界的追捧。

引用大佬的描述:MobileNet V3 = MobileNet v2 + SE结构 + hard-swish activation +网络结构头尾微调

img

MobileNetV1&MobileNetV2&MobileNetV3总结

MobileNetV1MobileNetV2MobileNetV3
标准卷积改为深度可分离卷积,降低计算量;ReLU改为ReLU6;引入Width Multiplier(α)和Resolution Multiplier(ρ),调节模型的宽度(卷积核个数)和图像分辨率;采用线性瓶颈层:将深度可分离卷积中的1×1卷积后的ReLU替换成线性激活函数;采用反向残差结构:引入Expansion layer,在进行深度分离卷积之前首先使用1×1卷积进行升维;引入Shortcut结构,在升维的1×1卷积之前与深度可分离卷积中的1×1卷积之后进行shortcut连接;采用增加了SE机制的Bottleneck模块结构;使用了一种新的激活函数h-swish(x)替代MobileNetV2中的ReLU6激活函数;网络结构搜索中,结合两种技术:资源受限的NAS(platform-aware NAS)与NetAdapt;修改了MobileNetV2网络端部最后阶段;

1.2 MobileNetV3相关技术

(1)引入MobileNetV1的深度可分离卷积
(2)引入MobileNetV2的具有线性瓶颈的倒残差结构
(3)引入基于squeeze and excitation结构的轻量级注意力模型(SE)
(4)使用了一种新的激活函数h-swish(x)
(5)网络结构搜索中,结合两种技术:资源受限的NAS(platform-aware NAS)与NetAdapt
(6)修改了MobileNetV2网络端部最后阶段

更多介绍,还是看上面的链接吧~


二、YOLOv5结合MobileNetV3_small

MobileNetV3详细解读
MobileNetV3详解2

2.1 添加顺序

之前在讲添加注意力机制时我们就介绍过改进网络的顺序,替换主干网络也是大同小异的。
(1)models/common.py --> 加入新增的网络结构

(2) models/yolo.py --> 设定网络结构的传参细节,将MobileNetV3类名加入其中。(当新的自定义模块中存在输入输出维度时,要使用qw调整输出维度)
(3) models/yolov5*.yaml --> 修改现有模型结构配置文件

  • 当引入新的层时,要修改后续的结构中的from参数
  • 当仅替换主千网络时,要注意特征图的变换,/8,/16,/32

(4) train.py --> 修改‘–cfg’默认参数,训练时指定模型结构配置文件


2.2 具体添加步骤

第①步:在common.py中添加MobileNetV3模块

将以下代码复制粘贴到common.py文件的末尾

# Mobilenetv3Small
# ——————MobileNetV3——————
import torch
import torch.nn as nn

# 定义一个Hard Sigmoid函数,用于SELayer中
class h_sigmoid(nn.Module):
    def __init__(self, inplace=True):
        super(h_sigmoid, self).__init__()
        self.relu = nn.ReLU6(inplace=inplace)

    def forward(self, x):
        return self.relu(x + 3) / 6

# 定义一个Hard Swish函数,用于SELayer中
class h_swish(nn.Module):
    def __init__(self, inplace=True):
        super(h_swish, self).__init__()
        self.sigmoid = h_sigmoid(inplace=inplace)

    def forward(self, x):
        return x * self.sigmoid(x)

# 定义Squeeze-and-Excitation(SE)模块
class SELayer(nn.Module):
    def __init__(self, channel, reduction=4):
        super(SELayer, self).__init__()
        # Squeeze操作:全局平均池化
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        # Excitation操作(FC+ReLU+FC+Sigmoid)
        self.fc = nn.Sequential(
            nn.Linear(channel, channel // reduction),  # 全连接层,将通道数降低为channel // reduction
            nn.ReLU(inplace=True),
            nn.Linear(channel // reduction, channel),  # 全连接层,恢复到原始通道数
            h_sigmoid()  # 使用Hard Sigmoid激活函数
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x)  # 对输入进行全局平均池化
        y = y.view(b, c)  # 将池化后的结果展平为二维张量
        y = self.fc(y).view(b, c, 1, 1)  # 通过全连接层计算每个通道的权重,并将其变成与输入相同的形状
        return x * y  # 将输入与权重相乘以实现通道注意力机制

# 定义卷积-批归一化-激活函数模块
class conv_bn_hswish(nn.Module):
    def __init__(self, c1, c2, stride):
        super(conv_bn_hswish, self).__init__()
        self.conv = nn.Conv2d(c1, c2, 3, stride, 1, bias=False)  # 3x3卷积层
        self.bn = nn.BatchNorm2d(c2)  # 批归一化层
        self.act = h_swish()  # 使用Hard Swish激活函数

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))  # 卷积 - 批归一化 - 激活函数

    def fuseforward(self, x):
        return self.act(self.conv(x))  # 融合版本的前向传播,省略了批归一化

# 定义MobileNetV3的基本模块
class MobileNetV3(nn.Module):
    def __init__(self, inp, oup, hidden_dim, kernel_size, stride, use_se, use_hs):
        super(MobileNetV3, self).__init__()
        assert stride in [1, 2]  # 断言,要求stride必须是1或2
        self.identity = stride == 1 and inp == oup  # 如果stride为1且输入通道数等于输出通道数,则为恒等映射

        if inp == hidden_dim:
            self.conv = nn.Sequential(
                nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
                          bias=False),  # 深度可分离卷积层
                nn.BatchNorm2d(hidden_dim),  # 批归一化层
                h_swish() if use_hs else nn.ReLU(inplace=True),  # 使用Hard Swish或ReLU激活函数
                SELayer(hidden_dim) if use_se else nn.Sequential(),  # 使用SELayer或空的Sequential
                nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),  # 1x1卷积层
                nn.BatchNorm2d(oup)  # 批归一化层
            )
        else:
            self.conv = nn.Sequential(
                nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),  # 1x1卷积层,用于通道扩张
                nn.BatchNorm2d(hidden_dim),  # 批归一化层
                h_swish() if use_hs else nn.ReLU(inplace=True),  # 使用Hard Swish或ReLU激活函数
                nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
                          bias=False),  # 深度可分离卷积层
                nn.BatchNorm2d(hidden_dim),  # 批归一化层
                SELayer(hidden_dim) if use_se else nn.Sequential(),  # 使用SELayer或空的Sequential
                h_swish() if use_hs else nn.ReLU(inplace=True),  # 使用Hard Swish或ReLU激活函数
                nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),  # 1x1卷积层
                nn.BatchNorm2d(oup)  # 批归一化层
            )

    def forward(self, x):
        y = self.conv(x)  # 通过卷积层
        if self.identity:
            return x + y  # 恒等映射
        else:
            return y  # 非恒等映射
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

如下图所示:

img


第②步:在yolo.py文件里的parse_model函数加入类名

首先找到yolo.py里面parse_model函数的这一行

img

加入h_sigmoidh_swishSELayerconv_bn_hswishMobileNetV3五个模块

img


第③步:创建自定义的yaml文件

首先在models文件夹下复制yolov5s.yaml 文件,粘贴并重命名为 yolov5s_MobileNetv3.yaml

img 然后根据MobileNetv3的网络结构来修改配置文件。

img

根据网络结构我们可以看出MobileNetV3模块包含六个参数[out_ch, hidden_ch, kernel_size, stride, use_se, use_hs]:

  • out_ch: 输出通道
  • hidden_ch: 表示在Inverted residuals中的扩张通道数
  • kernel_size: 卷积核大小
  • stride: 步长
  • use_se: 表示是否使用 SELayer,使用了是1,不使用是0
  • use_hs: 表示使用 h_swish 还是 ReLU,使用h_swish是1,使用 ReLU是0

修改的时候,需要注意/8,/16,/32等位置特征图的变换

img

同样的,head部分这几个concat的层也要做修改:

img

yaml文件修改后代码如下:

# YOLOv5 
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/578865
推荐阅读
相关标签