YOLOv9改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型

一、本文介绍

本文记录的是基于MobileNet V3的YOLOv9目标检测轻量化改进方法研究MobileNet V3的模型结构是通过网络搜索得来的,其中的基础模块结合了MobileNet V1的深度可分离卷积、MobileNet V2的线性瓶颈和倒置残差结构以及MnasNet中基于挤压和激励的轻量级注意力模块,使模型在性能、效率和灵活性方面都具有显著的优势。

模型参数量计算量推理速度(bs=32)
YOLOv9-c50.69M236.6GFLOPs32.1ms
Improved42.05M192.3GFLOPs28.1ms


二、MoblieNet V3设计原理

MobileNet V3是基于一系列互补的搜索技术和新颖的架构设计而提出的新一代神经网络模型,其设计的原理和优势主要包括以下几个方面:

  1. 原理
    • 网络搜索
      • 平台感知的NAS(Platform - Aware NAS):用于搜索全局网络结构,通过优化每个网络块来实现。对于大型移动模型,复用了MnasNet - A1的结构,并在此基础上应用NetAdapt和其他优化。对于小型移动模型,观察到原奖励设计未针对其优化,因此调整了权重因子w,重新进行架构搜索以找到初始种子模型。
      • NetAdapt:用于逐层搜索过滤器的数量,是对平台感知的NAS的补充。它从平台感知的NAS找到的种子网络架构开始,通过生成新的提案并根据某些指标选择最佳提案,逐步微调单个层,直到达到目标延迟。在选择提案时,修改了算法以最小化延迟变化和准确率变化的比率。
    • 网络改进
      • 重新设计昂贵层:对网络末尾和开头的一些昂贵层进行修改。对于末尾的层,将产生最终特征的层移动到最终平均池化之后,以降低延迟并保持高维特征,同时去除了之前瓶颈层中的投影和过滤层,进一步降低计算复杂度。对于初始的滤波器层,实验发现使用hard swish非线性函数并将滤波器数量减少到16时,能在保持准确率的同时减少延迟和计算量。
      • 非线性函数:引入了名为h-swish的非线性函数,它是swish非线性函数的改进版本,计算更快且更有利于量化。通过将sigmoid函数替换为分段线性的hard版本(如h - swish [x] = x * ReLU6(x + 3) / 6),并在网络的后半部分使用h-swish,减少了计算成本,同时在准确率上与原始版本没有明显差异。
      • 大的挤压 - 激励(Large squeeze - and - excite):将挤压 - 激励瓶颈的大小固定为扩展层通道数的1 / 4,在增加少量参数的情况下提高了准确率,且没有明显的延迟成本。
    • 高效的移动构建块:结合了MobileNet V1的深度可分离卷积、MobileNet V2的线性瓶颈和倒置残差结构以及MnasNet中基于挤压和激励的轻量级注意力模块,同时升级了这些层,使用修改后的swish非线性函数以提高效率。

在这里插入图片描述

  1. 优势

    • MobileNet V3通过网络搜索和改进,结合了多种技术的优势,在性能、效率和灵活性方面都具有显著的优势,适用于移动设备上的各种计算机视觉任务。并且定义了MobileNetV3 - LargeMobileNetV3 - Small两个模型,分别针对高资源和低资源使用场景,可根据不同需求进行选择和应用。

论文:https://arxiv.org/abs/2211.12905
源码:https://github.com/huawei-noah/Efficient-AI-Backbones/tree/master/ghostnetv2_pytorch

三、GhostModuleV2模块的实现代码

GhostModuleV2模块的实现代码如下:

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
 
 
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)
 
 
class SELayer(nn.Module):
    def __init__(self, channel, reduction=4):
        super(SELayer, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(channel, channel // reduction),
            nn.ReLU(inplace=True),
            nn.Linear(channel // reduction, channel),
            h_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)
        self.bn = nn.BatchNorm2d(c2)
        self.act = h_swish()
 
    def forward(self, x):
        return self.act(self.bn(self.conv(x)))
 
    def fuseforward(self, x):
        return self.act(self.conv(x))
 
 
class MobileNet_Block(nn.Module):
    def __init__(self, inp, oup, hidden_dim, kernel_size, stride, use_se, use_hs):
        super(MobileNet_Block, self).__init__()
        assert stride in [1, 2]
 
        self.identity = stride == 1 and inp == oup
        if inp == hidden_dim:
            self.conv = nn.Sequential(
                # dw
                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),
                # Squeeze-and-Excite
                SELayer(hidden_dim) if use_se else nn.Sequential(),
                # pw-linear
                nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
            )
        else:
            self.conv = nn.Sequential(
                # pw
                nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
                nn.BatchNorm2d(hidden_dim),
                h_swish() if use_hs else nn.ReLU(inplace=True),
                # dw
                nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
                          bias=False),
                nn.BatchNorm2d(hidden_dim),
                # Squeeze-and-Excite
                SELayer(hidden_dim) if use_se else nn.Sequential(),
                h_swish() if use_hs else nn.ReLU(inplace=True),
                # pw-linear
                nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
            )
 
    def forward(self, x):
        y = self.conv(x)
        if self.identity:
            return x + y
        else:
            return y

四、添加步骤

4.1 修改common.py

此处需要修改的文件是models/common.py

common.py中定义了网络结构的通用模块,我们想要加入新的模块就只需要将模块代码放到这个文件内即可。

此时需要将上方实现的代码添加到common.py中。

在这里插入图片描述

注意❗:在4.2小节中的yolo.py文件中需要声明的模块名称为:conv_bn_hswishMobileNet_Block

4.2 修改yolo.py

此处需要修改的文件是models/yolo.py

yolo.py用于函数调用,我们只需要将common.py中定义的新的模块名添加到parse_model函数下即可。

conv_bn_hswishMobileNet_Block模块添加后如下:

在这里插入图片描述


五、yaml模型文件

5.1 模型改进⭐

在代码配置完成后,配置模型的YAML文件。

此处以models/detect/yolov9-c.yaml为例,在同目录下创建一个用于自己数据集训练的模型文件yolov9-c-mobilenetv3 .yaml

yolov9-c.yaml中的内容复制到yolov9-c-mobilenetv3 .yaml文件下,修改nc数量等于自己数据中目标的数量。

📌 模型的修改方法是将骨干网络中的所有RepNCSPELAN4模块替换成MobileNet_Block模块,搭建MobileNetV3 - Small模型,并将其替换YOLOv9的骨干网络,相比原有的骨干网络,这可以大大减少模型的计算负担,提高推理速度,并且减小后的模型,使其在部署应用方面更加方便。

结构如下:

# YOLOv9

# parameters
nc: 1  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
#activation: nn.LeakyReLU(0.1)
#activation: nn.ReLU()

# anchors
anchors: 3

# YOLOv9 backbone
backbone:
  [
   [-1, 1, Silence, []],  
   
   # conv down
  #  [-1, 1, Conv, [64, 3, 2]],  # 1-P1/2

   # conv down
   [-1, 1, conv_bn_hswish, [16, 2]],  # 1-P1/2

   # elan-1 block
   [-1, 1, MobileNet_Block, [16,  16, 3, 2, 1, 0]],  # 2-P2/4

   # avg-conv down
   [-1, 1, MobileNet_Block, [24,  72, 3, 2, 0, 0]],  # 3-P3/8

   # elan-2 block
   [-1, 1, MobileNet_Block, [24,  88, 3, 1, 0, 0]],  # 4

   # avg-conv down
   [-1, 1, MobileNet_Block, [40,  96, 5, 2, 1, 1]],  # 5-P4/16

   # elan-2 block
   [-1, 1, MobileNet_Block, [40, 240, 5, 1, 1, 1]],  # 6
   [-1, 1, MobileNet_Block, [40, 240, 5, 1, 1, 1]],  # 7
   [-1, 1, MobileNet_Block, [48, 120, 5, 1, 1, 1]],  # 8
   [-1, 1, MobileNet_Block, [48, 144, 5, 1, 1, 1]],  # 9

   # avg-conv down
   [-1, 1, MobileNet_Block, [96, 288, 5, 2, 1, 1]],  # 10-P5/32

   # elan-2 block
   [-1, 1, MobileNet_Block, [96, 576, 5, 1, 1, 1]],  # 11
   [-1, 1, MobileNet_Block, [96, 576, 5, 1, 1, 1]],  # 12
  ]

# YOLOv9 head
head:
  [
   # elan-spp block
   [-1, 1, SPPELAN, [512, 256]],  # 10

   # up-concat merge
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 9], 1, Concat, [1]],  # cat backbone P4

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 13

   # up-concat merge
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [256, 256, 128, 1]],  # 16 (P3/8-small)

   # avg-conv-down merge
   [-1, 1, ADown, [256]],
   [[-1, 16], 1, Concat, [1]],  # cat head P4

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 19 (P4/16-medium)

   # avg-conv-down merge
   [-1, 1, ADown, [512]],
   [[-1, 13], 1, Concat, [1]],  # cat head P5

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 22 (P5/32-large)
   
   
   # multi-level reversible auxiliary branch
   
   # routing
   [4, 1, CBLinear, [[256]]], # 23
   [9, 1, CBLinear, [[256, 512]]], # 24
   [12, 1, CBLinear, [[256, 512, 512]]], # 25
   
   # conv down
   [0, 1, Conv, [64, 3, 2]],  # 26-P1/2

   # conv down
   [-1, 1, Conv, [128, 3, 2]],  # 27-P2/4

   # elan-1 block
   [-1, 1, RepNCSPELAN4, [256, 128, 64, 1]],  # 28

   # avg-conv down fuse
   [-1, 1, ADown, [256]],  # 29-P3/8
   [[26, 27, 28, -1], 1, CBFuse, [[0, 0, 0]]], # 30  

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 256, 128, 1]],  # 31

   # avg-conv down fuse
   [-1, 1, ADown, [512]],  # 32-P4/16
   [[27, 28, -1], 1, CBFuse, [[1, 1]]], # 33 

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 34

   # avg-conv down fuse
   [-1, 1, ADown, [512]],  # 35-P5/32
   [[28, -1], 1, CBFuse, [[2]]], # 36

   # elan-2 block
   [-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 37
   
   
   
   # detection head

   # detect
   [[34, 37, 40, 19, 22, 25], 1, DualDDetect, [nc]],  # DualDDetect(A3, A4, A5, P3, P4, P5)
  ]


六、成功运行结果

分别打印网络模型可以看到MobileNet_Block已经加入到模型中,并可以进行训练了。

yolov9-c-mobilenetv3

                 from  n    params  module                                  arguments                     
  0                -1  1         0  models.common.Silence                   []                            
  1                -1  1       464  models.common.conv_bn_hswish            [3, 16, 2]                    
  2                -1  1       612  models.common.MobileNet_Block           [16, 16, 16, 3, 2, 1, 0]      
  3                -1  1      3864  models.common.MobileNet_Block           [16, 24, 72, 3, 2, 0, 0]      
  4                -1  1      5416  models.common.MobileNet_Block           [24, 24, 88, 3, 1, 0, 0]      
  5                -1  1     13736  models.common.MobileNet_Block           [24, 40, 96, 5, 2, 1, 1]      
  6                -1  1     55340  models.common.MobileNet_Block           [40, 40, 240, 5, 1, 1, 1]     
  7                -1  1     55340  models.common.MobileNet_Block           [40, 40, 240, 5, 1, 1, 1]     
  8                -1  1     21486  models.common.MobileNet_Block           [40, 48, 120, 5, 1, 1, 1]     
  9                -1  1     28644  models.common.MobileNet_Block           [48, 48, 144, 5, 1, 1, 1]     
 10                -1  1     91848  models.common.MobileNet_Block           [48, 96, 288, 5, 2, 1, 1]     
 11                -1  1    294096  models.common.MobileNet_Block           [96, 96, 576, 5, 1, 1, 1]     
 12                -1  1    294096  models.common.MobileNet_Block           [96, 96, 576, 5, 1, 1, 1]     
 13                -1  1    550400  models.common.SPPELAN                   [96, 512, 256]                
 14                -1  1         0  torch.nn.modules.upsampling.Upsample    [None, 2, 'nearest']          
 15           [-1, 9]  1         0  models.common.Concat                    [1]                           
 16                -1  1   2882048  models.common.RepNCSPELAN4              [560, 512, 512, 256, 1]       
 17                -1  1         0  torch.nn.modules.upsampling.Upsample    [None, 2, 'nearest']          
 18           [-1, 4]  1         0  models.common.Concat                    [1]                           
 19                -1  1    787712  models.common.RepNCSPELAN4              [536, 256, 256, 128, 1]       
 20                -1  1    164352  models.common.ADown                     [256, 256]                    
 21          [-1, 16]  1         0  models.common.Concat                    [1]                           
 22                -1  1   2988544  models.common.RepNCSPELAN4              [768, 512, 512, 256, 1]       
 23                -1  1    656384  models.common.ADown                     [512, 512]                    
 24          [-1, 13]  1         0  models.common.Concat                    [1]                           
 25                -1  1   3119616  models.common.RepNCSPELAN4              [1024, 512, 512, 256, 1]      
 26                 4  1      6400  models.common.CBLinear                  [24, [256]]                   
 27                 9  1     37632  models.common.CBLinear                  [48, [256, 512]]              
 28                12  1    124160  models.common.CBLinear                  [96, [256, 512, 512]]         
 29                 0  1      1856  models.common.Conv                      [3, 64, 3, 2]                 
 30                -1  1     73984  models.common.Conv                      [64, 128, 3, 2]               
 31                -1  1    212864  models.common.RepNCSPELAN4              [128, 256, 128, 64, 1]        
 32                -1  1    164352  models.common.ADown                     [256, 256]                    
 33  [26, 27, 28, -1]  1         0  models.common.CBFuse                    [[0, 0, 0]]                   
 34                -1  1    847616  models.common.RepNCSPELAN4              [256, 512, 256, 128, 1]       
 35                -1  1    656384  models.common.ADown                     [512, 512]                    
 36      [27, 28, -1]  1         0  models.common.CBFuse                    [[1, 1]]                      
 37                -1  1   2857472  models.common.RepNCSPELAN4              [512, 512, 512, 256, 1]       
 38                -1  1    656384  models.common.ADown                     [512, 512]                    
 39          [28, -1]  1         0  models.common.CBFuse                    [[2]]                         
 40                -1  1   2857472  models.common.RepNCSPELAN4              [512, 512, 512, 256, 1]       
 41[34, 37, 40, 19, 22, 25]  1  21542822  models.yolo.DualDDetect                 [1, [512, 512, 512, 256, 512, 512]]
yolov9-c-mobilenetv3 summary: 902 layers, 42053396 parameters, 42053364 gradients, 192.3 GFLOPs
  • 18
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Limiiiing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值