YOLOv8改进 | 主干篇 | 利用SENetV2改进网络结构 (全网首发改进)
一、本文介绍
本文给大家带来的改进机制是SENetV2,其是2023.11月的最新机制(所以大家想要发论文的可以在上面下点功夫),其是一种通过调整卷积网络中的通道关系来提升性能的网络结构。SENet并不是一个独立的网络模型,而是一个可以和现有的任何一个模型相结合的模块(可以看作是一种通道型的注意力机制但是相对于SENetV1来说V2又在全局的角度进行了考虑)。在SENet中,所谓的挤压和激励(Squeeze-and-Excitation)操作是作为一个单元添加到传统的卷积网络结构中,如残差单元中(后面我会把修改好的残差单元给大家大家直接复制粘贴即可使用)亲测大中小三中目标检测上都有一定程度的涨点效果。
推荐指数:?????
涨点效果:?????
专栏回顾:YOLOv8改进系列专栏——本专栏持续复习各种顶会内容——科研必备????
训练结果对比图->??
后面我会将这个机制SENetV1和SENetV2添加到多个网络结构中进行融合涨点,同时将其融合到检测头里将模型发布给大家使用。
(我对于SENetV1和SENetV2各提供了两个版本的yaml文件,我分别尝试了其中的两种,这个SENetV2的实验yaml文件可能不如我没实验的yaml文件二大家可以都尝试一下。)
目录
?二、SENetV2框架原理
?
论文地址:官方论文地址点击即可跳转
代码地址:官方代码地址点击即可跳转
?
SENetV2介绍了一种改进的SENet架构,该架构通过引入一种称为Squeeze aggregated excitation(SaE)的新模块来提升网络的表征能力。这个模块结合了挤压和激励(SENetV1)操作,通过多分支全连接层增强了网络的全局表示学习。在基准数据集上的实验结果证明了SENetV2模型相较于现有模型在分类精度上的显著提升。这一架构尤其强调在仅略微增加模型参数的情况下,如何有效地提高模型的性能。?
挤压和激励模块大家可以看我发的SENetV1文章里面有介绍。
图中展示了三种不同的神经网络模块对比:
a) ResNeXt模块:采用多分支CNN结构,不同分支的特征图通过卷积操作处理后合并(concatenate),再进行额外的卷积操作。
b) SENet模块:标准卷积操作后,利用全局平均池化来挤压特征,然后通过两个尺寸为1x1的全连接层(FC)和Sigmoid激活函数来获取通道权重,最后对卷积特征进行缩放(Scale)。
c) SENetV2模块:结合了ResNeXt和SENet的特点,采用多分支全连接层(FC)来挤压和激励操作,最后进行特征缩放。
其中SENetV2的设计旨在通过多分支结构进一步提升特征表达的精细度和全局信息的整合能力。
前面我们提到了SaE,就是SENetV2相对于SENetV1的主要改进机制,下面的图片介绍了其内部工作原理。
SENet V2中所提出的SaE(Squeeze-and-Excitation)模块的内部工作机制。挤压输出后,被输入到多分支的全连接(FC)层,然后进行激励过程。分割的输入在最后被传递以恢复其原始形状。这种设计能够让网络更有效地学习到输入数据的不同特征,并且在进行特征转换时考虑到不同通道之间的相互依赖性。?
三、SENetV2核心代码
下面的代码是MSDA的核心代码,我们将其复制导'ultralytics/nn/modules'目录下,在其中创建一个文件,我这里起名为Dilation然后粘贴进去,其余使用方式看章节四。
import torch
import torch.nn as nn
from .conv import Conv
# 定义SE模块
class SELayer(nn.Module):
def __init__(self, channel, reduction=16):
super(SELayer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channel, channel // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(channel // reduction, channel, bias=False),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)
# 定义SaE模块
class SELayerV2(nn.Module):
def __init__(self, in_channel, reduction=32):
super(SELayerV2, self).__init__()
assert in_channel>=reduction and in_channel%reduction==0,'invalid in_channel in SaElayer'
self.reduction = reduction
self.cardinality=4
self.avg_pool = nn.AdaptiveAvgPool2d(1)
#cardinality 1
self.fc1 = nn.Sequential(
nn.Linear(in_channel,in_channel//self.reduction, bias=False),
nn.ReLU(inplace=True)
)
# cardinality 2
self.fc2 = nn.Sequential(
nn.Linear(in_channel, in_channel // self.reduction, bias=False),
nn.ReLU(inplace=True)
)
# cardinality 3
self.fc3 = nn.Sequential(
nn.Linear(in_channel, in_channel // self.reduction, bias=False),
nn.ReLU(inplace=True)
)
# cardinality 4
self.fc4 = nn.Sequential(
nn.Linear(in_channel, in_channel // self.reduction, bias=False),
nn.ReLU(inplace=True)
)
self.fc = nn.Sequential(
nn.Linear(in_channel//self.reduction*self.cardinality, in_channel, bias=False),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y1 = self.fc1(y)
y2 = self.fc2(y)
y3 = self.fc3(y)
y4 = self.fc4(y)
y_concate = torch.cat([y1,y2,y3,y4],dim=1)
y_ex_dim = self.fc(y_concate).view(b,c,1,1)
return x * y_ex_dim.expand_as(x)
class Bottleneck(nn.Module):
"""Standard bottleneck."""
def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
"""Initializes a bottleneck module with given input/output channels, shortcut option, group, kernels, and
expansion.
"""
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, k[0], 1)
self.cv2 = Conv(c_, c2, k[1], 1, g=g)
self.SEV2 = SELayerV2(c2)
self.add = shortcut and c1 == c2
def forward(self, x):
"""'forward()' applies the YOLO FPN to input data."""
return x + self.SEV2(self.cv2(self.cv1(x))) if self.add else self.SEV2(self.cv2(self.cv1(x)))
class C2f_SENetV2(nn.Module):
"""Faster Implementation of CSP Bottleneck with 2 convolutions."""
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
"""Initialize CSP bottleneck layer with two convolutions with arguments ch_in, ch_out, number, shortcut, groups,
expansion.
"""
super().__init__()
self.c = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))
def forward(self, x):
"""Forward pass through C2f layer."""
y = list(self.cv1(x).chunk(2, 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
def forward_split(self, x):
"""Forward pass using split() instead of chunk()."""
y = list(self.cv1(x).split((self.c, self.c), 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
# import ipdb
#
# se_v2 = SaELayer(64)
# # 示例输入
# input = torch.randn(3, 64, 224, 224)
# output = se_v2(input)
#
# print(output.shape)#torch.Size([3, 64, 224, 224])
四、手把手教你添加SENetV2模块
?4.1?SENetV2添加步骤
4.1.1 步骤一
首先我们找到如下的目录'ultralytics/nn/modules',然后在这个目录下创建一个py文件,名字为你也可以根据你自己的习惯起即可,然后将核心代码复制进去。
4.1.2 步骤二
之后我们找到'ultralytics/nn/tasks.py'文件,在其中注册我们的模块。
首先我们需要在文件的开头导入我们的模块,如下图所示->
?
4.1.3 步骤三
我们找到parse_model这个方法,可以用搜索也可以自己手动找,大概在六百多行吧。?我们找到如下的地方,然后将模块按照我的方法添加进去即可,模仿我添加即可,其中另外的模块,你没有删除即可,添加红框的内容即可。
?
到此我们就注册成功了,可以修改yaml文件使用我们添加的模块了。
4.2 SENetV2的yaml文件和训练截图
下面推荐几个版本的yaml文件给大家,大家可以复制进行训练,但是组合用很多具体那种最有效果都不一定,针对不同的数据集效果也不一样,我不可每一种都做实验,所以我下面推荐了几种我自己认为可能有效果的配合方式,你也可以自己进行组合。
4.2.1 SENetV2的yaml版本一
这个是我尝试的版本,和上一篇SENetV1做了个对比反过来尝试一下。
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.33, 0.25, 1024] # YOLOv8n summary: 225 layers, 3157200 parameters, 3157184 gradients, 8.9 GFLOPs
s: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients, 28.8 GFLOPs
m: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients, 79.3 GFLOPs
l: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
x: [1.00, 1.25, 512] # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOP
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
- [-1, 3, C2f, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f, [1024, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 3, C2f_SENetV2, [256]] # 15 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 12], 1, Concat, [1]] # cat head P4
- [-1, 3, C2f_SENetV2, [512]] # 18 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 9], 1, Concat, [1]] # cat head P5
- [-1, 3, C2f_SENetV2, [1024]] # 21 (P5/32-large)
- [[11, 14, 17], 1, Detect, [nc]] # Detect(P3, P4, P5)
4.2.2 SENetV2的yaml版本二
下面的版本我在大中小三个检测层的输出部分添加了SENetV2,大家可以根据自己的需求,减少SENetV2比如你做的小目标检测,那么可以把另外两个去去掉,但是别忘了修改检测通道数,要不然会报错。?
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.33, 0.25, 1024] # YOLOv8n summary: 225 layers, 3157200 parameters, 3157184 gradients, 8.9 GFLOPs
s: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients, 28.8 GFLOPs
m: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients, 79.3 GFLOPs
l: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
x: [1.00, 1.25, 512] # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOP
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
- [-1, 3, C2f, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f, [1024, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 3, C2f, [256]] # 15 (P3/8-small)
- [-1, 1, SELayerV2, []] # 16
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 12], 1, Concat, [1]] # cat head P4
- [-1, 3, C2f, [512]] # 19 (P4/16-medium)
- [-1, 1, SELayerV2, []] # 20
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 9], 1, Concat, [1]] # cat head P5
- [-1, 3, C2f, [1024]] # 23 (P5/32-large)
- [-1, 1, SELayerV2, []] # 24
- [[16, 20, 24], 1, Detect, [nc]] # Detect(P3, P4, P5)
4.3 推荐SENetV2可添加的位置?
SENetV2是一种即插即用的可替换卷积的模块,其可以添加的位置有很多,添加的位置不同效果也不同,所以我下面推荐几个添加的位,置大家可以进行参考,当然不一定要按照我推荐的地方添加。
残差连接中:在残差网络的残差连接中加入SENetV2。
Neck部分:YOLOv8的Neck部分负责特征融合,这里添加SENetV2可以帮助模型更有效地融合不同层次的特征。
能添加的位置很多:一篇文章很难全部介绍到,后期我会发文件里面集成上百种的改进机制,然后还有许多融合模块,给大家。
4.4 SENetV2的训练过程截图?
下面是添加了SENetV2的训练截图。
大家可以看下面的运行结果和添加的位置所以不存在我发的代码不全或者运行不了的问题大家有问题也可以在评论区评论我看到都会为大家解答(我知道的)。
?
??????
五、本文总结
到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv8改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~),如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~
???
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!