0


YOLOv5算法改进(8)— 替换主干网络之MobileNetV3

前言:****Hello大家好,我是小哥谈。MobileNetV3是由Google团队在2019年提出的一种卷积神经网络结构,其目标是在保持高性能的同时减少计算延时。MobileNetV3相比于前一版本(MobileNetV2)在性能上有明显的提升。根据原论文,在ImageNet分类任务中,MobileNetV3的正确率提升了3.2%,同时计算延时降低了20%。MobileNetV3通过使用NAS搜索参数重新设计了耗时层结构,这也是其与之前版本的主要区别之一。🌈

** 前期回顾:**

** ** YOLOv5算法改进(1)— 如何去改进YOLOv5算法

         YOLOv5算法改进(2)— 添加SE注意力机制

         YOLOv5算法改进(3)— 添加CBAM注意力机制

         YOLOv5算法改进(4)— 添加CA注意力机制

         YOLOv5算法改进(5)— 添加ECA注意力机制

         YOLOv5算法改进(6)— 添加SOCA注意力机制

         YOLOv5算法改进(7)— 添加SimAM注意力机制

🚀1.论文

MobileNetV3 是由 Google 团队在 2019 年提出的轻量化网络模型。传统的卷积神经网络,内容需求大、运算量大,无法在移动设备以及嵌入式设备上运行,为了解决这一问题,MobileNet网络应运而生。MobileNetV3在移动端图像分类、目标检测、语义分割等任务上均取得了优秀的表现。MobileNetV3采用了很多新的技术,包括针对通道注意力的Squeeze-and-Excitation模块、NAS搜索方法等,这些方法都有利于进一步提升网络的性能。🌱

MobileNetV3的整体架构基本沿用了MobileNetV2的设计,采用了轻量级的深度可分离卷积和残差块等结构,依然是由多个模块组成,但是每个模块得到了优化和升级,包括瓶颈结构、SE模块和NL模块。MobileNetV3在ImageNet 分类任务中正确率上升了 3.2%,计算延时还降低了20%。🌱

整体来说MobileNetV3有两大创新点:

  • 互补搜索技术组合:由资源受限的NAS执行模块级搜索,NetAdapt执行局部搜索。
  • 网络结构改进:将最后一步的平均池化层前移并移除最后一个卷积层,引入h-swish激活函数。

MobileNetV3 有两个版本,MobileNetV3-Small 与 MobileNetV3-Large 分别对应对计算和存储要求低和高的版本。🌺

论文题目:《Searching for MobileNetV3》

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

代码实现: mirrors / xiaolai-sqlai / mobilenetv3 · GitCode


🚀2.MobileNetV3网络架构

下图为MobileNetV3的网络结构图,large和small的整体结构一致,区别就是基本单元bneck的个数以及内部参数上,主要是通道数目。🌳

MobileNetV3-Large版本参数:

MobileNetV3-Small版本参数:

说明:♨️♨️♨️

上表为具体的参数设置,其中bneck是网络的基本结构。SE代表是否使用通道注意力机制。NL代表激活函数的类型,包括HS(h-swish),RE(ReLU)。NBN 代表没有BN操作。s 是stride的意思,网络使用卷积stride操作进行降采样,没有使用pooling操作。

MobileNetV3 相较于 MobileNetV2,主要有以下改进和优化:

🍀更高的准确率:MobileNetV3在ImageNet数据集上的Top-1准确率达到了75.2%,比MobileNetV2的72.0%要高。

🍀更快的推理速度:MobileNetV3通过增加Squeeze-and-Excitation模块和hard-swish非线性激活函数,提高了模型的计算效率,加快了推理速度。

🍀更少的参数和计算量:MobileNetV3在保持准确率不变的情况下,参数数量和计算量比MobileNetV2都要少。

🍀支持多种模型结构:MobileNetV3除了提供标准版模型外,还提供了Small模型和Large模型,可以根据不同的场景和需求选择合适的模型。

🍀支持自适应网络:MobileNetV3加入了Squeeze-and-Excite模块和自适应网络结构,可以根据输入图像的尺寸和分辨率自适应地调整模型的结构和参数,从而实现更好的模型泛化能力和适应性。

🍀支持权重重要性筛选:MobileNetV3提供了权重重要性筛选工具,可以定量地筛选出对模型性能影响最大的参数,从而实现更高效的模型压缩和优化。

总的来说,MobileNetV3 相对于 MobileNetV2,在准确率、推理速度、参数数量、计算量、模型结构和适应性等方面都有了显著的提升和改进。🌴


🚀3.YOLOv5结合MobileNetV3_small

💥💥步骤1:在common.py中添加MobileNetV3模块

将下面MobileNetV3模块的代码复制粘贴到common.py文件的末尾。

# Mobilenetv3Small
# ——————MobileNetV3——————
 
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__()
        # Squeeze操作
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        # Excitation操作(FC+ReLU+FC+Sigmoid)
        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)  # 学习到的每一channel的权重
        return x * y
 
 
class conv_bn_hswish(nn.Module):
    """
    This equals to
    def conv_3x3_bn(inp, oup, stride):
        return nn.Sequential(
            nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
            nn.BatchNorm2d(oup),
            h_swish()
        )
    """
 
    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 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]
 
        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

具体如下图所示:

**​💥💥步骤2:在yolo.py文件中加入类名 **

首先在yolo.py文件中找到parse_model函数这一行,加入h_sigmoidh_swishSELayerconv_bn_hswishMobileNetV3这五个模块。

**​💥💥步骤3:创建自定义yaml文件 **

models文件夹中复制yolov5s.yaml,粘贴并重命名为yolov5s_MobileNetV3.yaml

然后根据 MobileNetV3的网络架构来修改配置文件。

根据网络结构,我们可以看到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。

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

yaml文件修改后的完整代码如下:

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
 
# Parameters
nc: 80  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32
 
   # Mobilenetv3-small backbone
   # MobileNetV3_InvertedResidual [out_ch, hid_ch, k_s, stride, SE, HardSwish]
backbone:
  # [from, number, module, args]
  [[-1, 1, conv_bn_hswish, [16, 2]],             # 0-p1/2   320*320
   [-1, 1, MobileNetV3, [16,  16, 3, 2, 1, 0]],  # 1-p2/4   160*160
   [-1, 1, MobileNetV3, [24,  72, 3, 2, 0, 0]],  # 2-p3/8   80*80
   [-1, 1, MobileNetV3, [24,  88, 3, 1, 0, 0]],  # 3        80*80
   [-1, 1, MobileNetV3, [40,  96, 5, 2, 1, 1]],  # 4-p4/16  40*40
   [-1, 1, MobileNetV3, [40, 240, 5, 1, 1, 1]],  # 5        40*40
   [-1, 1, MobileNetV3, [40, 240, 5, 1, 1, 1]],  # 6        40*40
   [-1, 1, MobileNetV3, [48, 120, 5, 1, 1, 1]],  # 7        40*40
   [-1, 1, MobileNetV3, [48, 144, 5, 1, 1, 1]],  # 8        40*40
   [-1, 1, MobileNetV3, [96, 288, 5, 2, 1, 1]],  # 9-p5/32  20*20
   [-1, 1, MobileNetV3, [96, 576, 5, 1, 1, 1]],  # 10       20*20
   [-1, 1, MobileNetV3, [96, 576, 5, 1, 1, 1]],  # 11       20*20
  ]
 
# YOLOv5 v6.0 head
head:
  [[-1, 1, Conv, [96, 1, 1]],  # 12                         20*20
   [-1, 1, nn.Upsample, [None, 2, 'nearest']], # 13         40*40
   [[-1, 8], 1, Concat, [1]],  # cat backbone P4            40*40
   [-1, 3, C3, [144, False]],  # 15                         40*40
 
   [-1, 1, Conv, [144, 1, 1]], # 16                         40*40
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],# 17          80*80
   [[-1, 3], 1, Concat, [1]],  # cat backbone P3            80*80
   [-1, 3, C3, [168, False]],  # 19 (P3/8-small)            80*80
 
   [-1, 1, Conv, [168, 3, 2]], # 20                         40*40
   [[-1, 16], 1, Concat, [1]], # cat head P4                40*40
   [-1, 3, C3, [312, False]],  # 22 (P4/16-medium)          40*40
 
   [-1, 1, Conv, [312, 3, 2]], # 23                         20*20
   [[-1, 12], 1, Concat, [1]], # cat head P5                20*20
   [-1, 3, C3, [408, False]],  # 25 (P5/32-large)           20*20
 
   [[19, 22, 25], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

💥💥步骤4:验证是否加入成功

yolo.py文件里,配置我们刚才自定义的yolov5s_MobileNetV3.yaml

然后运行yolo.py,得到结果。

我们和原始的yolov5s.yaml进行对比。

可以看到替换主干网络为MobileNetV3之后层数变多了,可以学习到更多的特征,参数量由原来的700多万减少到500多万,大幅度减少了,GFLOPS也由16.4变为12.1。💯💯💯

💥💥步骤5:修改train.py中的'--cfg'默认参数

train.py文件中找到 parse_opt函数,然后将第二行**'--cfg'**的default改为 **'models/yolov5s_MobileNetV3.yaml'**,然后就可以开始进行训练了。🎈🎈🎈


** 🚀4.YOLOv5结合MobileNetV3_large**

**MobileNetV3_large **和 MobileNetV3_small的区别在于 yaml 文件中 head 中的 concat 连接不同,即深度因子和宽度因子不同。

步骤1和步骤2与上面的相同,我们直接开始步骤3,修改yaml文件部分。

**💥💥步骤3:创建自定义yaml文件 **

models文件夹中复制yolov5s.yaml,粘贴并重命名为yolov5s_MobileNetV3_large.yaml

然后根据 MobileNetV3的网络架构来修改配置文件。

yaml文件修改后的完整代码如下:

# Parameters
nc: 20  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32
 
# YOLOv5 v6.0 backbone
backbone:
 
  [[-1, 1, conv_bn_hswish, [16, 2]],                  # 0-p1/2
   [-1, 1, MobileNetV3, [ 16,  16, 3, 1, 0, 0]],  # 1-p1/2
   [-1, 1, MobileNetV3, [ 24,  64, 3, 2, 0, 0]],  # 2-p2/4
   [-1, 1, MobileNetV3, [ 24,  72, 3, 1, 0, 0]],  # 3-p2/4
   [-1, 1, MobileNetV3, [ 40,  72, 5, 2, 1, 0]],  # 4-p3/8
   [-1, 1, MobileNetV3, [ 40, 120, 5, 1, 1, 0]],  # 5-p3/8
   [-1, 1, MobileNetV3, [ 40, 120, 5, 1, 1, 0]],  # 6-p3/8
   [-1, 1, MobileNetV3, [ 80, 240, 3, 2, 0, 1]],  # 7-p4/16
   [-1, 1, MobileNetV3, [ 80, 200, 3, 1, 0, 1]],  # 8-p4/16
   [-1, 1, MobileNetV3, [ 80, 184, 3, 1, 0, 1]],  # 9-p4/16
   [-1, 1, MobileNetV3, [ 80, 184, 3, 1, 0, 1]],  # 10-p4/16
   [-1, 1, MobileNetV3, [112, 480, 3, 1, 1, 1]],  # 11-p4/16
   [-1, 1, MobileNetV3, [112, 672, 3, 1, 1, 1]],  # 12-p4/16
   [-1, 1, MobileNetV3, [160, 672, 5, 1, 1, 1]],  # 13-p4/16
   [-1, 1, MobileNetV3, [160, 960, 5, 2, 1, 1]],  # 14-p5/32   原672改为原算法960
   [-1, 1, MobileNetV3, [160, 960, 5, 1, 1, 1]],  # 15-p5/32
  ]
# YOLOv5 v6.0 head
head:
  [ [ -1, 1, Conv, [ 256, 1, 1 ] ],
    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 13], 1, Concat, [ 1 ] ],  # cat backbone P4
    [ -1, 1, C3, [ 256, False ] ],  # 13
 
    [ -1, 1, Conv, [ 128, 1, 1 ] ],
    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 6 ], 1, Concat, [ 1 ] ],  # cat backbone P3
    [ -1, 1, C3, [ 128, False ] ],  # 17 (P3/8-small)
 
    [ -1, 1, Conv, [ 128, 3, 2 ] ],
    [ [ -1, 20 ], 1, Concat, [ 1 ] ],  # cat head P4
    [ -1, 1, C3, [ 256, False ] ],  # 20 (P4/16-medium)
 
    [ -1, 1, Conv, [ 256, 3, 2 ] ],
    [ [ -1, 16 ], 1, Concat, [ 1 ] ],  # cat head P5
    [ -1, 1, C3, [ 512, False ] ],  # 23 (P5/32-large)
 
    [ [ 23, 26, 29 ], 1, Detect, [ nc, anchors ] ],  # Detect(P3, P4, P5)
  ]

网络运行结果:

由结果可以看到,MobileNetV3_large模型比MobileNetV3_small多了更多的MobileNet_Block结构,参差倒置结构中通道数维度也增大了许多,速度比YOLOv5s慢近一半,但是参数更少,效果介乎MobileNetV3_smallYOLOv5s之间,可以作为模型对比,凸显自己模型的优势。🍃🍃🍃

说明:♨️♨️♨️

如果训练之后发现掉点纯属正常现象,因为轻量化网络在提速减少计算量的同时,也会降低精度。


本文转载自: https://blog.csdn.net/weixin_61961691/article/details/132548918
版权归原作者 小哥谈 所有, 如有侵权,请联系我们删除。

“YOLOv5算法改进(8)— 替换主干网络之MobileNetV3”的评论:

还没有评论