赞
踩
点击进入专栏:
《人工智能专栏》 Python与Python | 机器学习 | 深度学习 | 目标检测 | YOLOv5及其改进 | YOLOv8及其改进 | 关键知识点 | 各种工具教程
代码函数调用关系图(全网最详尽-重要)
因文档特殊,不能在博客正确显示,请移步以下链接!
图解YOLOv5_v7.0代码结构与调用关系(点击进入可以放大缩小等操作)
预览:
MobileNetV3,是谷歌在2019年3月21日提出的轻量化网络架构,在前两个版本的基础上,加入神经网络架构搜索(NAS)和h-swish激活函数,并引入SE通道注意力机制,性能和速度都表现优异,受到学术界和工业界的追捧。
引用大佬的描述:MobileNet V3 = MobileNet v2 + SE结构 + hard-swish activation +网络结构头尾微调
MobileNetV1&MobileNetV2&MobileNetV3总结
MobileNetV1 | MobileNetV2 | MobileNetV3 |
---|---|---|
标准卷积改为深度可分离卷积,降低计算量;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)引入MobileNetV1的深度可分离卷积
(2)引入MobileNetV2的具有线性瓶颈的倒残差结构
(3)引入基于squeeze and excitation结构的轻量级注意力模型(SE)
(4)使用了一种新的激活函数h-swish(x)
(5)网络结构搜索中,结合两种技术:资源受限的NAS(platform-aware NAS)与NetAdapt
(6)修改了MobileNetV2网络端部最后阶段
更多介绍,还是看上面的链接吧~
之前在讲添加注意力机制时我们就介绍过改进网络的顺序,替换主干网络也是大同小异的。
(1)models/common.py --> 加入新增的网络结构
(2) models/yolo.py --> 设定网络结构的传参细节,将MobileNetV3类名加入其中。(当新的自定义模块中存在输入输出维度时,要使用qw调整输出维度)
(3) models/yolov5*.yaml --> 修改现有模型结构配置文件
(4) train.py --> 修改‘–cfg’默认参数,训练时指定模型结构配置文件
将以下代码复制粘贴到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 # 非恒等映射
如下图所示:
首先找到yolo.py里面parse_model函数的这一行
加入h_sigmoid,h_swish,SELayer,conv_bn_hswish,MobileNetV3五个模块
首先在models文件夹下复制yolov5s.yaml 文件,粘贴并重命名为 yolov5s_MobileNetv3.yaml
然后根据MobileNetv3的网络结构来修改配置文件。
根据网络结构我们可以看出MobileNetV3模块包含六个参数[out_ch, hidden_ch, kernel_size, stride, use_se, use_hs]:
修改的时候,需要注意/8,/16,/32等位置特征图的变换
同样的,head部分这几个concat的层也要做修改:
yaml文件修改后代码如下:
# YOLOv5 声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/578865
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。