💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 |
尽管Ultralytics 推出了最新版本的 YOLOv8 模型。但YOLOv5作为一个anchor base的目标检测的算法,YOLOv5可能比YOLOv8的效果更好。注意力机制是提高模型性能最热门的方法之一,本文给大家带来的教程是将YOLOv5的backbone替换为MobileNetV3结构来提取特征。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。
专栏地址:YOLOv5改进+入门——持续更新各种有效涨点方法1.原理
MobileNetV3 是 Google 提出的一种轻量级神经网络结构,旨在在移动设备上实现高效的图像识别和分类任务。与之前的 MobileNet 系列相比,MobileNetV3 在模型结构和性能上都有所改进。
MobileNetV3 的结构主要包括以下几个关键组件:
基础模块(Base Module):MobileNetV3 使用了一种称为“倒残差”(Inverted Residuals)的基础模块结构。该结构采用了深度可分离卷积和线性瓶颈,以减少参数数量和计算复杂度,并且保持了模型的有效性。
Squeeze-and-Excitation 模块:MobileNetV3 引入了 Squeeze-and-Excitation 模块,通过学习通道之间的相互关系,动态地调整通道权重,以增强模型的表征能力。这有助于提高模型对关键特征的感知能力,从而提高分类性能。
Hard-Swish 激活函数:MobileNetV3 使用了一种称为 Hard-Swish 的激活函数。与传统的 ReLU 激活函数相比,Hard-Swish 具有更快的计算速度和更好的性能。
网络架构优化:MobileNetV3 在网络结构上进行了优化,包括通过网络宽度和分辨率的动态调整,以适应不同的计算资源和任务需求。
总体而言,MobileNetV3 通过这些创新设计和优化,实现了更高的性能和更低的计算成本,使其成为移动设备上图像识别任务的理想选择之一。
精简原理参考:百面算法工程师 | 分类网络总结-CSDN博客
2.MobileNetv3代码实现
2.1 将MobileNetv3代码添加到YOLOv5中
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):
y = self.sigmoid(x)
return x * y
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 Conv3BN(nn.Module):
def __init__(self, inp, oup, stride):
super(Conv3BN, self).__init__()
self.conv = nn.Conv2d(inp, oup, 3, stride, 1, bias=False)
self.bn = nn.BatchNorm2d(oup)
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
MobileNetV3 的主要处理流程如下:
输入处理:输入图像首先经过预处理步骤,例如归一化和大小调整,以使其适应网络的输入要求。
特征提取:经过输入处理后,图像通过一系列基础模块(Base Module)进行特征提取。每个基础模块通常包含深度可分离卷积、激活函数(如 Hard-Swish)和通道注意力(如 Squeeze-and-Excitation)模块。
特征增强:在特征提取的过程中,通过 Squeeze-and-Excitation 模块对提取的特征图进行增强,以加强对重要特征的感知能力。
全局平均池化:在特征提取的最后阶段,通过全局平均池化操作将特征图的空间维度降低到一个固定大小。
分类器:全局平均池化后的特征图输入到分类器中,进行分类或其他任务的预测。分类器通常由一个或多个全连接层组成,最后输出预测结果。
整个流程通过堆叠和连接这些组件来完成。MobileNetV3 的设计旨在保持模型的轻量化和高效性能,以适应移动设备上的图像识别和分类任务。
2.2新增yaml文件
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# parameters
nc: 2 # number of classes
depth_multiple: 1.0 # dont change this otherwise InvertedResidual will be affected
width_multiple: 1.0 # dont change this otherwise InvertedResidual will be affected
# anchors
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 backbone
backbone:
# MobileNetV3-large
# [from, number, module, args]
[[-1, 1, Conv3BN, [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, 672, 5, 2, 1, 1]], # 14-p5/32
[-1, 1, MobileNetv3, [160, 960, 5, 1, 1, 1]], # 15-p5/32
]
# YOLOv5 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]], # 19
[-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]], # 23 (P3/8-small)
[-1, 1, Conv, [128, 3, 2]],
[[-1, 20], 1, Concat, [1]], # cat head P4
[-1, 1, C3, [256, False]], # 26 (P4/16-medium)
[-1, 1, Conv, [256, 3, 2]],
[[-1, 16], 1, Concat, [1]], # cat head P5
[-1, 1, C3, [512, False]], # 29 (P5/32-large)
[[23, 26, 29], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
温馨提示:因为本文只是对yolov5n基础上添加swin模块,如果要对yolov5n/l/m/x进行添加则只需要修改对应的depth_multiple 和 width_multiple。
yolov5n/l/m/x对应的depth_multiple 和 width_multiple如下:
# YOLOv5n
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.25 # layer channel multiple
# YOLOv5s
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
# YOLOv5l
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
# YOLOv5m
depth_multiple: 0.67 # model depth multiple
width_multiple: 0.75 # layer channel multiple
# YOLOv5x
depth_multiple: 1.33 # model depth multiple
width_multiple: 1.25 # layer channel multiple
2.3 注册模块
2.4 执行程序
在train.py中,将cfg的参数路径设置为yolov5_mobilemetv3.yaml的路径,如下图所示
建议大家写绝对路径,确保一定能找到
🚀运行程序,如果出现下面的内容则说明添加成功🚀
3.完整代码分享
YOLOv5改进 | 主干网络 | 将backbone替换为MobileNetV3【小白必备教程+附完整代码】
👆是我修改后的完整,提取码unah
4.总结
MobileNetV3 是一种轻量级神经网络结构,通过倒残差基础模块、Squeeze-and-Excitation 模块和Hard-Swish 激活函数等创新技术,实现了在移动设备等资源受限环境下高效的图像识别和分类任务。其处理流程包括输入处理、特征提取、特征增强、全局平均池化和分类器预测,通过优化网络结构和设计,MobileNetV3 在保持性能的同时减少了计算负载,适用于移动设备上的应用场景。
5. 进阶
在backbone中单独添加一层或者是替换一个C3模块,也是可行的策略,这里直接放上改进后的代码。提示:也可以尝试替换其他啊部位的C3模块。
YOLOv5改进 | 主干网络 | 将backbone替换为MobileNetV3【小白必备教程+附完整代码】
👆是我修改后的完整,提取码: 6wpu
可能在实验过程可能会遇到问题,如果你不能解决,欢迎评论区提问