一、本文介绍
本文给大家带来的最新改进机制是 Slim-neck 提出的 Neck部分 , Slim-neck 是一种设计用于 优化卷积神经网络中neck部分的结构 。在我们YOLOv11中, neck 是连接主干网络(backbone)和头部网络(head)的部分,负责特征融合和处理,以便提高检测的准确性和效率。 亲测在小目标检测和大尺度目标检测的数据集上都有大幅度的涨点效果(mAP直接涨了大概有0.04左右 。 同时本文对Slim-Neck的框架原理进行了详细的分析,不光让大家会添加到自己的模型在写论文的时候也能够有一定的参照,最后本文会手把手教你添加Slim-Neck模块到网络结构中 。
目录
二、Slim-neck原理
论文地址: 官方论文地址
代码地址: 官方代码地址
2.1 Slim-neck的基本原理
Slim-neck 是一种设计用于 优化卷积神经网络(CNN)中“neck”部分的结构 。在目标检测器中,"neck"是连接CNN的主干网络( backbone )和头部网络(head)的部分,负责特征融合和处理,以便提高检测的准确性和效率。
我们可以将Slim-neck的基本原理分为以下几点:
1. GSConv的引入: GSConv是为了在 卷积神经网络 (CNN)中加快图像的预测计算。在传统的CNN中,空间信息逐渐转换成通道信息,而这一过程在每一次特征图空间压缩和通道扩张时都会导致语义信息的部分丢失。GSConv旨在在保持较低时间复杂度的同时,尽可能地保留通道之间的隐藏连接。
2. 模块元素: GSConv之后,研究者继续引入GS瓶颈(GS bottleneck)和跨阶段部分网络(GSCSP)模块VoV-GSCSP,这些模块设计用于进一步提升性能。在实际应用中,更简单的结构模块由于更易于 硬件 实现,更有可能被采用。
3. 灵活性: 论文提出了需要灵活使用GSConv、GS瓶颈和VoV-GSCSP这四个模块。可以像搭乐高一样构建Slim-neck层。
下面我为大家展示 应用于YOLOv5模型的Slim-neck架构 。这种架构使用了 GSConv 和 VoV-GSCSP模块 ,以构建一个高效的 神经网络 颈部”。在这个架构中,不同尺度的特征图(P3, P4, P5)首先通过GSConv模块处理,然后通过上采样(upsample)和拼接(Concat)操作与其他尺度的特征图结合。这样处理后的特征图再次通过GSConv模块,最后使用VoV-GSCSP模块来进一步提取和融合特征,以准备最终的检测头(head-1, head-2, head-3)进行目标检测。
通过这种模块化和分层的方法,Slim-neck架构能够在保持高准确度的同时减少计算复杂性和推理时间,这对于在自动驾驶车辆等计算资源受限的环境中的应用尤其重要。
2.2 GSConv的引入
GSConv的引入 是为了 解决在卷积神经网络(CNN)中预测计算的速度问题 。在CNN的骨干网络(backbone)中,输入图像几乎总是经历一个类似的转换过程:空间信息逐步向通道传递。每一次特征图的空间(宽度和高度)压缩和通道扩张都会导致语义信息的部分丢失。 通道密集型的卷积计算(SC) 最大限度地保留了每个通道之间的隐含连接,而 通道稀疏的卷积(DSC) 则完全切断了这些连接。GSConv尽可能地保持这些连接,并且具有更低的时间复杂度。
上面提到的时间复杂度通常由 浮点运算(FLOPs) 来定义。因此,SC、DSC和GSConv的时间复杂度分别为:
- SC:
- DSC:
- GSConv:
其中W是输出特征图的宽度,H是高度,
是卷积核的大小,
是每个卷积核的通道数,也是输入特征图的通道数,
是输出特征图的通道数。
下图为大家展示了 GSConv模块的结构 。
1. 卷积层(Conv): 输入特征图首先通过一个卷积层,该层的输出通道数为C2/2。
2. 深度可分离卷积层(DWConv): 该层标记为蓝色,表示深度可分离卷积(DSC)操作。它对输入特征图的每个通道独立进行卷积。
3. 拼接(Concat): 将Conv层和DWConv层的输出进行拼接。
4. 随机排列(Shuffle): 拼接后的特征图经过一个shuffle操作,以重新排列特征通道,提高特征间的信息流动。
5. 输出: 最终输出的特征图有C2个通道。
我将通过下图为大家清晰展示 标准卷积(SC)和深度可分离卷积(DSC)的计算过程 。标准卷积是通道密集型的计算,而深度可分离卷积是通道稀疏的计算。
上图强调了在 传统的标准卷积和现代轻量级深度可分离卷积之间的差异 ,其中后者在保持足够精确度的同时,减少了计算的复杂性,这对于计算资源受限的环境尤其有益。这种方法通常用于移动和边缘设备的神经网络架构中,以提高运行效率。
2.3 模块元素
模块元素 是构成Slim-neck架构的基础部分,设计它们的目的是为了减少计算成本,同时保持或提高模型的学习能力。模块元素可以灵活使用,像搭积木一样组合成Slim-neck层,提供了构建高效 深度学习 模型的灵活性和效率
1. GSConv: 是一种减少计算复杂性的轻量级卷积,它的计算成本约为标准卷积(SC)的一半,但在模型的学习能力方面与SC相当。
2. GS bottleneck: 基于GSConv,这是一种增强模块,用于提高特征的非线性表达和信息的复用。
3. VoV-GSCSP: 利用一次性聚合方法设计的跨阶段部分网络模块,用于在不同阶段的特征图之间进行有效的信息融合。
下图显示了 GS bottleneck模块 和 VoV-GSCSP模块 的结构:
(a) GS bottleneck模块,其中包含GSConv模块的一个或多个实例。 (b), (c), (d) 分别展示了不同设计方案的VoV-GSCSP模块。
GS bottleneck模块 是为了进一步增强网络处理特征的能力,通过 GSConv模块 的堆叠来提高模型的学习能力。而 VoV-GSCSP模块 是利用不同的结构设计方案,以提高特征利用效率和网络性能。这些模块设计是Slim-neck理念的体现,旨在减少计算复杂性和推理时间,同时保持准确性。通过这样的模块化设计,可以根据需要灵活地构建出适合特定任务的网络架构。
三、 Slim-neck的完整代码
使用方式看章节四。
- import torch
- import torch.nn as nn
- import math
-
- __all__ = ['VoVGSCSP', 'VoVGSCSPC', 'GSConv']
-
-
- def autopad(k, p=None): # kernel, padding
- # Pad to 'same'
- if p is None:
- p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
- return p
-
-
- class Conv(nn.Module):
- # Standard convolution
- def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
- super().__init__()
- self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
- self.bn = nn.BatchNorm2d(c2)
- self.act = nn.Mish() if act else nn.Identity()
-
- def forward(self, x):
- return self.act(self.bn(self.conv(x)))
-
- def forward_fuse(self, x):
- return self.act(self.conv(x))
-
-
- class GSConv(nn.Module):
- # GSConv https://github.com/AlanLi1997/slim-neck-by-gsconv
- def __init__(self, c1, c2, k=1, s=1, g=1, act=True):
- super().__init__()
- c_ = c2 // 2
- self.cv1 = Conv(c1, c_, k, s, None, g, act)
- self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
-
- def forward(self, x):
- x1 = self.cv1(x)
- x2 = torch.cat((x1, self.cv2(x1)), 1)
- b, n, h, w = x2.data.size()
- b_n = b * n // 2
- y = x2.reshape(b_n, 2, h * w)
- y = y.permute(1, 0, 2)
- y = y.reshape(2, -1, n // 2, h, w)
-
- return torch.cat((y[0], y[1]), 1)
-
-
- class GSConvns(GSConv):
- # GSConv with a normative-shuffle https://github.com/AlanLi1997/slim-neck-by-gsconv
- def __init__(self, c1, c2, k=1, s=1, g=1, act=True):
- super().__init__(c1, c2, k=1, s=1, g=1, act=True)
- c_ = c2 // 2
- self.shuf = nn.Conv2d(c_ * 2, c2, 1, 1, 0, bias=False)
-
- def forward(self, x):
- x1 = self.cv1(x)
- x2 = torch.cat((x1, self.cv2(x1)), 1)
- # normative-shuffle, TRT supported
- return nn.ReLU(self.shuf(x2))
-
-
- class GSBottleneck(nn.Module):
- # GS Bottleneck https://github.com/AlanLi1997/slim-neck-by-gsconv
- def __init__(self, c1, c2, k=3, s=1, e=0.5):
- super().__init__()
- c_ = int(c2*e)
- # for lighting
- self.conv_lighting = nn.Sequential(
- GSConv(c1, c_, 1, 1),
- GSConv(c_, c2, 3, 1, act=False))
- self.shortcut = Conv(c1, c2, 1, 1, act=False)
-
- def forward(self, x):
- return self.conv_lighting(x) + self.shortcut(x)
-
-
- class DWConv(Conv):
- # Depth-wise convolution class
- def __init__(self, c1, c2, k=1, s=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
- super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
-
-
- class GSBottleneckC(GSBottleneck):
- # cheap GS Bottleneck https://github.com/AlanLi1997/slim-neck-by-gsconv
- def __init__(self, c1, c2, k=3, s=1):
- super().__init__(c1, c2, k, s)
- self.shortcut = DWConv(c1, c2, k, s, act=False)
-
-
- class VoVGSCSP(nn.Module):
- # VoVGSCSP module with GSBottleneck
- def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
- super().__init__()
- c_ = int(c2 * e) # hidden channels
- self.cv1 = Conv(c1, c_, 1, 1)
- self.cv2 = Conv(c1, c_, 1, 1)
- self.gsb = nn.Sequential(*(GSBottleneck(c_, c_, e=1.0) for _ in range(n)))
- self.res = Conv(c_, c_, 3, 1, act=False)
- self.cv3 = Conv(2 * c_, c2, 1) #
-
- def forward(self, x):
- x1 = self.gsb(self.cv1(x))
- y = self.cv2(x)
- return self.cv3(torch.cat((y, x1), dim=1))
-
-
- class VoVGSCSPC(VoVGSCSP):
- # cheap VoVGSCSP module with GSBottleneck
- def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
- super().__init__(c1, c2)
- c_ = int(c2 * 0.5) # hidden channels
- self.gsb = GSBottleneckC(c_, c_, 1, 1)
四、手把手教你添加Slim-neck模块
4.1 修改一
第一还是建立文件,我们找到如下ultralytics/nn文件夹下建立一个目录名字呢就是'Addmodules'文件夹( 用群内的文件的话已经有了无需新建) !然后在其内部建立一个新的py文件将核心代码复制粘贴进去即可。
4.2 修改二
第二步我们在该目录下创建一个新的py文件名字为'__init__.py'( 用群内的文件的话已经有了无需新建) ,然后在其内部导入我们的检测头如下图所示。
4.3 修改三
第三步找到如下文件'ultralytics/nn/tasks.py'进行导入和注册我们的模块( 用群内的文件的话已经有了无需重新导入直接开始第四步即可) !
4.4 修改四
找到文件到如下文件'ultralytics/nn/tasks.py',在其中的parse_model方法中添加即可。
到此就可以了,完成了注册然后只需要修改yaml文件就可以进行训练了~
五、 Slim-neck的yaml文件
5.1 yaml文件1
此版本训练信息:YOLO11-SlimNeck-1 summary: 517 layers, 2,745,115 parameters, 2,745,099 gradients, 6.4 GFLOPs
- # Ultralytics YOLO 🚀, AGPL-3.0 license
- # YOLO11 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=yolo11n.yaml' will call yolo11.yaml with scale 'n'
- # [depth, width, max_channels]
- n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
- s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
- m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
- l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
- x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
-
- # YOLO11n 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, 2, C3k2, [256, False, 0.25]]
- - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- - [-1, 2, C3k2, [512, False, 0.25]]
- - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- - [-1, 2, C3k2, [512, True]]
- - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- - [-1, 2, C3k2, [1024, True]]
- - [-1, 1, SPPF, [1024, 5]] # 9
- - [-1, 2, C2PSA, [1024]] # 10
-
- # YOLO11n head
- head:
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 6], 1, Concat, [1]] # cat backbone P4
- - [-1, 3, VoVGSCSP, [512]] # 13
-
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 4], 1, Concat, [1]] # cat backbone P3
- - [-1, 3, VoVGSCSP, [256]] # 16 (P3/8-small)
-
- - [-1, 1, GSConv, [256, 3, 2]]
- - [[-1, 13], 1, Concat, [1]] # cat head P4
- - [-1, 3, VoVGSCSP, [512]] # 19 (P4/16-medium)
-
- - [-1, 1, GSConv, [512, 3, 2]]
- - [[-1, 10], 1, Concat, [1]] # cat head P5
- - [-1, 3, VoVGSCSP, [1024]] # 22 (P5/32-large)
-
- - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)
5.2 yaml文件2
训练信息:YOLO11-SlimNeck-2 summary: 417 layers, 2,491,187 parameters, 2,491,171 gradients, 5.9 GFLOPs
- # Ultralytics YOLO 🚀, AGPL-3.0 license
- # YOLO11 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=yolo11n.yaml' will call yolo11.yaml with scale 'n'
- # [depth, width, max_channels]
- n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
- s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
- m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
- l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
- x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
-
- # YOLO11n 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, 2, C3k2, [256, False, 0.25]]
- - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- - [-1, 2, C3k2, [512, False, 0.25]]
- - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- - [-1, 2, C3k2, [512, True]]
- - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- - [-1, 2, C3k2, [1024, True]]
- - [-1, 1, SPPF, [1024, 5]] # 9
- - [-1, 2, C2PSA, [1024]] # 10
-
- # YOLO11n head
- head:
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 6], 1, Concat, [1]] # cat backbone P4
- - [-1, 3, VoVGSCSPC, [512]] # 13
-
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 4], 1, Concat, [1]] # cat backbone P3
- - [-1, 3, VoVGSCSPC, [256]] # 16 (P3/8-small)
-
- - [-1, 1, GSConv, [256, 3, 2]]
- - [[-1, 13], 1, Concat, [1]] # cat head P4
- - [-1, 3, VoVGSCSPC, [512]] # 19 (P4/16-medium)
-
- - [-1, 1, GSConv, [512, 3, 2]]
- - [[-1, 10], 1, Concat, [1]] # cat head P5
- - [-1, 3, VoVGSCSPC, [1024]] # 22 (P5/32-large)
-
- - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)
5.3 yaml文件3
训练信息:YOLO11-GSConv summary: 331 layers, 2,504,955 parameters, 2,504,939 gradients, 6.4 GFLOPs
# 此版本仅用了两个GSConv所以不能算本文的SlimNeck,感兴趣可以尝试看看效果.
- # Ultralytics YOLO 🚀, AGPL-3.0 license
- # YOLO11 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=yolo11n.yaml' will call yolo11.yaml with scale 'n'
- # [depth, width, max_channels]
- n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
- s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
- m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
- l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
- x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
-
- # YOLO11n 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, 2, C3k2, [256, False, 0.25]]
- - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- - [-1, 2, C3k2, [512, False, 0.25]]
- - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- - [-1, 2, C3k2, [512, True]]
- - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- - [-1, 2, C3k2, [1024, True]]
- - [-1, 1, SPPF, [1024, 5]] # 9
- - [-1, 2, C2PSA, [1024]] # 10
-
- # YOLO11n head
- head:
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 6], 1, Concat, [1]] # cat backbone P4
- - [-1, 2, C3k2, [512, False]] # 13
-
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 4], 1, Concat, [1]] # cat backbone P3
- - [-1, 2, C3k2, [256, False]] # 16 (P3/8-small)
-
- - [-1, 1, GSConv, [256, 3, 2]]
- - [[-1, 13], 1, Concat, [1]] # cat head P4
- - [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium)
-
- - [-1, 1, GSConv, [512, 3, 2]]
- - [[-1, 10], 1, Concat, [1]] # cat head P5
- - [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large)
-
- - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)
六、 训练截图
下面是训练成功的截图。
五、本文总结
到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~