学习资源站

YOLOv11改进-Conv篇-添加RFAConv重塑空间注意力助力yolov11有效涨点(深度学习的前沿突破)

一、本文介绍

本文给大家带来的改进机制是 RFAConv ,全称为 Receptive-Field Attention Convolution ,是一种全新的 空间注意力机制 。与 传统的空间注意力方法相比,RFAConv能够更有效地处理图像中的细节和复杂模式 (适用于所有的检测对象都有一定的提点) 。这不仅让YOLOv11在识别和定位目标时更加精准,还大幅提升了处理速度和效率。本文章深入会探讨RFAConv如何在YOLOv11中发挥作用,以及它是如何改进在我们的YOLOv11中的。我将通过案例的角度来带大家分析其有效性 (结果训练结果对比图)

适用检测目标: 亲测所有的目标检测均有一定的提点



二、RFAConv结构讲解

论文地址: 官方论文地址

代码地址: 官方代码地址


2.1、 RAFCAonv主要思想

RFAConv(Receptive-Field Attention Convolution) 的主要思想是将空间注意力机制与卷积操作相结合,从而提高 卷积神经网络 (CNN)的性能。这种方法的核心在于优化卷积核的工作方式,特别是在处理感受野内的空间特征时。以下是RFAConv的几个关键思想:

1. 感受野空间特征的重点关注: RFAConv特别关注于感受野内的空间特征,不仅仅局限于传统的空间维度。这种方法允许网络更有效地理解和处理图像中的局部区域,从而提高特征提取的精确性。

2. 解决参数共享问题: 在传统的CNN中,卷积核在处理不同区域的图像时共享同样的参数,这可能限制了 模型 对于复杂模式的学习能力。RFAConv通过引入注意力机制,能够更灵活地调整卷积核的参数,针对不同区域提供定制化的处理。

3. 提高大尺寸卷积核的效率: 对于大尺寸卷积核,仅使用标准的 空间注意力 可能不足以捕获所有重要的信息。RFAConv通过提供有效的注意力权重,使得大尺寸卷积核能够更有效地处理图像信息。

总结: RFAConv通过结合空间注意力和感受野特征的处理,为卷积神经网络提供了一种新的、更高效的方式来提取和处理图像特征,尤其是在处理复杂或大尺寸的输入时。

下面我来分别介绍这几点->


2.2、 感受野空间特征

感受野空间特征 是指卷积神经网络(CNN)中,卷积层能“看到”的输入数据的局部区域。在CNN中,每个卷积操作的输出是基于输入数据的一个小窗口,或者说是一个局部感受野。这个感受野定义了卷积核可以接触到的输入数据的大小和范围。

感受野的概念对于理解CNN如何从输入数据中提取特征是至关重要的。在网络的初级层,感受野通常很小,允许模型捕捉到细微的局部特征,如边缘和角点。随着数据通过更多的卷积层,通过层层叠加,感受野逐渐扩大,允许网络感知到更大的区域,捕捉到更复杂的特征,如纹理和对象的部分。

在CNN的上下文中,感受野空间特征指的是每个卷积操作能够感知的输入图像区域中的特征。这些特征可以包括颜色、形状、纹理等基本视觉元素。在传统的卷积网络中,感受野通常是固定的,并且每个位置的处理方式都是相同的。但是,如果网络能够根据每个区域的不同特点来调整感受野的处理方式,那么网络对特征的理解就会更加精细和适应性更强。

上图展示了 一个3x3的卷积操作。在这个操作中,特征是通过将卷积核与同样大小的感受野滑块相乘然后求和得到的。具体来说,输入图像X上的每一个3x3的区域(即感受野)都被一个3x3的卷积核K处理。每个感受野内的元素, X_{i,j} (其中i和j表示在感受野内的位置)都与卷积核K内对应位置的权重, K_{i,j} 相乘,然后这些乘积会被求和得到一个新的特征值F。这个过程在整个输入图像上滑动进行,以生成新的特征图。这种标准的卷积操作强调了局部连接和权重共享的概念,即卷积核的权重对整个输入图。

总结: 在RFAConv中,感受野空间特征被用来指导注意力机制,这样模型就不仅仅关注于当前层的特定区域,而是根据输入数据的复杂性和重要性动态调整感受野。通过这种方式,RFAConv能够为不同区域和不同尺寸的感受野提供不同的处理,使得网络能够更加有效地捕捉和利用图像中的信息。


2.3、 解决参数共享问题

RFAConv卷积以解决参数共享问题, RFAConv 通过引入注意力机制,允许网络为每个感受野生成特定的权重。这样,卷积核可以根据每个感受野内的不同特征动态调整其参数,而不是对所有区域一视同仁。

具体来说,RFAConv利用空间注意力来确定感受野中每个位置的重要性,并据此调整卷积核的权重。这样,每个感受野都有自己独特的卷积核,而不是所有感受野共享同一个核。这种方法使得网络能够更细致地学习图像中的局部特征,从而有助于提高整体网络性能。

通过这种方法,RFAConv提升了模型的表达能力,允许它更精确地适应和表达输入数据的特征,尤其是在处理复杂或多变的图像内容时。

上图展示了 一个卷积操作的过程,其中卷积核参数 K_{i} 通过将注意力权重 A_{i} 与卷积核参数 K 相乘得到。这意味着每个感受野滑块的卷积操作都有一个独特的卷积核参数,这些参数是通过将通用的卷积核参数与特定于该位置的注意力权重相结合来获得的。

具体地说,这个过程将注意力机制与卷积核相结合,为每个感受野位置产生一个定制化的卷积核。例如,图中的 Kernel 1、Kernel 2 和 Kernel 3 分别是通过将通用卷积核参数 K 与对应的注意力权重 A_{1} ​、 A_{2} ​ 和 A_{3} ​ 相乘得到的。这种方法允许网络在特征提取过程中对不同空间位置的特征赋予不同的重要性,从而增强了模型对关键特征的捕获能力。

总结: 这样的机制增加了卷积神经网络的表达能力,使得网络能够更加灵活地适应不同的输入特征,并有助于提高最终任务的性能。这是一种有效的方式来处理传统卷积操作中的参数共享问题,因为它允许每个位置的卷积核适应其处理的特定区域。


2.4、 提高大尺寸卷积核的效率

RFAConv 通过利用感受野注意力机制来动态调整卷积核的权重,从而为每个区域的特征提取提供了定制化的关注度。这样,即便是大尺寸卷积核,也能够更加有效地捕捉和处理重要的空间特征,而不会对不那么重要的信息分配过多的计算资源。

具体来说,RFAConv方法允许网络识别和强调输入特征图中更重要的区域,并且根据这些区域调整卷积核的权重。这意味着网络可以对关键特征进行重加权,使得大尺寸卷积核不仅能够捕捉到广泛的信息,同时也能够集中计算资源在更有信息量的特征上,从而提升了整体的处理效率和网络性能。

上图描述了 感受野滑块中特征的重叠,这是在标准卷积操作中常见的现象。特征的重叠导致了注意力权重的共享问题,意味着不同的感受野可能会对相同的输入特征使用相同的注意力权重。

在图中, F_{1} ​, F_{2} ​,... F_{N} 代表不同感受野滑块内的特征输出,它们是通过将输入特征 X 与对应的注意力权重 A 以及卷积核 K 的权重进行逐元素乘法运算后得到的。例如, F_{1} 是通过将 X_{11} 乘以对应的注意力权重 A_{11} 和卷积核权重 K_{1} 计算得到的,以此类推。

该图强调了 每个感受野滑块内的卷积操作的参数不应该完全共享,而是应该根据每个特定区域内的特征和相应的注意力权重进行调整。这种调整允许网络对每个局部区域进行更加精细的处理,能够更好地捕捉和响应输入数据的特定特征,而不是简单地对整个图像应用相同的权重。这样的方法能够提升网络对特征的理解和表示,从而改善最终的学习和预测性。

总结: 通过这种方法,RFAConv提升了模型的表达能力,允许它更精确地适应和表达输入数据的特征,尤其是在处理复杂或多变的图像内容时。这种灵活的参数调整机制为提高卷积神经网络的性能和 泛化 能力提供了新的途径。


三、RFAConv代码

RFAConv的核心代码使用方式看章节四!

  1. from torch import nn
  2. from einops import rearrange
  3. import torch
  4. __all__ = ['C3k2_RFAConv', 'RFAConv']
  5. class RFAConv(nn.Module):
  6. def __init__(self, in_channel, out_channel, kernel_size=3, stride=1):
  7. super().__init__()
  8. self.kernel_size = kernel_size
  9. self.get_weight = nn.Sequential(nn.AvgPool2d(kernel_size=kernel_size, padding=kernel_size // 2, stride=stride),
  10. nn.Conv2d(in_channel, in_channel * (kernel_size ** 2), kernel_size=1,
  11. groups=in_channel, bias=False))
  12. self.generate_feature = nn.Sequential(
  13. nn.Conv2d(in_channel, in_channel * (kernel_size ** 2), kernel_size=kernel_size, padding=kernel_size // 2,
  14. stride=stride, groups=in_channel, bias=False),
  15. nn.BatchNorm2d(in_channel * (kernel_size ** 2)),
  16. nn.ReLU())
  17. self.conv = Conv(in_channel, out_channel, k=kernel_size, s=kernel_size, p=0)
  18. def forward(self, x):
  19. b, c = x.shape[0:2]
  20. weight = self.get_weight(x)
  21. h, w = weight.shape[2:]
  22. weighted = weight.view(b, c, self.kernel_size ** 2, h, w).softmax(2) # b c*kernel**2,h,w -> b c k**2 h w
  23. feature = self.generate_feature(x).view(b, c, self.kernel_size ** 2, h,
  24. w) # b c*kernel**2,h,w -> b c k**2 h w
  25. weighted_data = feature * weighted
  26. conv_data = rearrange(weighted_data, 'b c (n1 n2) h w -> b c (h n1) (w n2)', n1=self.kernel_size,
  27. # b c k**2 h w -> b c h*k w*k
  28. n2=self.kernel_size)
  29. return self.conv(conv_data)
  30. def autopad(k, p=None, d=1): # kernel, padding, dilation
  31. """Pad to 'same' shape outputs."""
  32. if d > 1:
  33. k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
  34. if p is None:
  35. p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
  36. return p
  37. class Conv(nn.Module):
  38. """Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
  39. default_act = nn.SiLU() # default activation
  40. def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
  41. """Initialize Conv layer with given arguments including activation."""
  42. super().__init__()
  43. self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
  44. self.bn = nn.BatchNorm2d(c2)
  45. self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
  46. def forward(self, x):
  47. """Apply convolution, batch normalization and activation to input tensor."""
  48. return self.act(self.bn(self.conv(x)))
  49. def forward_fuse(self, x):
  50. """Perform transposed convolution of 2D data."""
  51. return self.act(self.conv(x))
  52. class Bottleneck(nn.Module):
  53. """Standard bottleneck."""
  54. def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
  55. """Initializes a bottleneck module with given input/output channels, shortcut option, group, kernels, and
  56. expansion.
  57. """
  58. super().__init__()
  59. c_ = int(c2 * e) # hidden channels
  60. self.cv1 = Conv(c1, c_, k[0], 1)
  61. self.cv2 = RFAConv(c_, c2, 3, 1)
  62. self.add = shortcut and c1 == c2
  63. def forward(self, x):
  64. """'forward()' applies the YOLO FPN to input data."""
  65. return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
  66. class C2f(nn.Module):
  67. """Faster Implementation of CSP Bottleneck with 2 convolutions."""
  68. def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
  69. """Initializes a CSP bottleneck with 2 convolutions and n Bottleneck blocks for faster processing."""
  70. super().__init__()
  71. self.c = int(c2 * e) # hidden channels
  72. self.cv1 = Conv(c1, 2 * self.c, 1, 1)
  73. self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
  74. self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))
  75. def forward(self, x):
  76. """Forward pass through C2f layer."""
  77. y = list(self.cv1(x).chunk(2, 1))
  78. y.extend(m(y[-1]) for m in self.m)
  79. return self.cv2(torch.cat(y, 1))
  80. def forward_split(self, x):
  81. """Forward pass using split() instead of chunk()."""
  82. y = list(self.cv1(x).split((self.c, self.c), 1))
  83. y.extend(m(y[-1]) for m in self.m)
  84. return self.cv2(torch.cat(y, 1))
  85. class C3(nn.Module):
  86. """CSP Bottleneck with 3 convolutions."""
  87. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
  88. """Initialize the CSP Bottleneck with given channels, number, shortcut, groups, and expansion values."""
  89. super().__init__()
  90. c_ = int(c2 * e) # hidden channels
  91. self.cv1 = Conv(c1, c_, 1, 1)
  92. self.cv2 = Conv(c1, c_, 1, 1)
  93. self.cv3 = Conv(2 * c_, c2, 1) # optional act=FReLU(c2)
  94. self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=((1, 1), (3, 3)), e=1.0) for _ in range(n)))
  95. def forward(self, x):
  96. """Forward pass through the CSP bottleneck with 2 convolutions."""
  97. return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))
  98. class C3k(C3):
  99. """C3k is a CSP bottleneck module with customizable kernel sizes for feature extraction in neural networks."""
  100. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, k=3):
  101. """Initializes the C3k module with specified channels, number of layers, and configurations."""
  102. super().__init__(c1, c2, n, shortcut, g, e)
  103. c_ = int(c2 * e) # hidden channels
  104. # self.m = nn.Sequential(*(RepBottleneck(c_, c_, shortcut, g, k=(k, k), e=1.0) for _ in range(n)))
  105. self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=(k, k), e=1.0) for _ in range(n)))
  106. class C3k2_RFAConv(C2f):
  107. """Faster Implementation of CSP Bottleneck with 2 convolutions."""
  108. def __init__(self, c1, c2, n=1, c3k=False, e=0.5, g=1, shortcut=True):
  109. """Initializes the C3k2 module, a faster CSP Bottleneck with 2 convolutions and optional C3k blocks."""
  110. super().__init__(c1, c2, n, shortcut, g, e)
  111. self.m = nn.ModuleList(
  112. C3k(self.c, self.c, 2, shortcut, g) if c3k else Bottleneck(self.c, self.c, shortcut, g) for _ in range(n)
  113. )
  114. if __name__ == "__main__":
  115. # Generating Sample image
  116. image_size = (1, 64, 240, 240)
  117. image = torch.rand(*image_size)
  118. # Model
  119. mobilenet_v1 = C3k2_RFAConv(64, 64)
  120. out = mobilenet_v1(image)
  121. print(out.size())

四、手把手教你添加RFAConv和C3k2-RFAConv模块

这个添加方式和之前的变了一下,以后的添加方法都按照这个来了,是为了和群内的文件适配。


4.1 修改一

第一还是建立文件,我们找到如下 ultralytics /nn文件夹下建立一个目录名字呢就是'Addmodules'文件夹( 用群内的文件的话已经有了无需新建 ) !然后在其内部建立一个新的py文件将核心代码复制粘贴进去即可。


4.2 修改二

第二步我们在该目录下创建一个新的py文件名字为'__init__.py'( 用群内的文件的话已经有了无需新建 ) ,然后在其内部导入我们的检测头如下图所示。


4.3 修改三

第三步我门中到如下文件'ultralytics/nn/tasks.py'进行导入和注册我们的模块( 用群内的文件的话已经有了无需重新导入直接开始第四步即可)

从今天开始以后的教程就都统一成这个样子了,因为我默认大家用了我群内的文件来进行修改!!


4.4 修改四

按照我的添加在parse_model里添加即可。

到此就修改完成了,大家可以复制下面的yaml文件运行。


五、RFAConv的yaml文件和运行记录

5.1 RFAConv的yaml文件一

此版本训练的信息: YOLO11-C3k2-RFAConv summary: 408 layers, 2,640,507 parameters, 2,640,491 gradients, 6.6 GFLOPs

  1. # Ultralytics YOLO 🚀, AGPL-3.0 license
  2. # YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
  3. # Parameters
  4. nc: 80 # number of classes
  5. scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
  6. # [depth, width, max_channels]
  7. n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
  8. s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
  9. m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
  10. l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
  11. x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
  12. # YOLO11n backbone
  13. backbone:
  14. # [from, repeats, module, args]
  15. - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
  16. - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
  17. - [-1, 2, C3k2_RFAConv, [256, False, 0.25]]
  18. - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  19. - [-1, 2, C3k2_RFAConv, [512, False, 0.25]]
  20. - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
  21. - [-1, 2, C3k2_RFAConv, [512, True]]
  22. - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
  23. - [-1, 2, C3k2_RFAConv, [1024, True]]
  24. - [-1, 1, SPPF, [1024, 5]] # 9
  25. - [-1, 2, C2PSA, [1024]] # 10
  26. # YOLO11n head
  27. head:
  28. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  29. - [[-1, 6], 1, Concat, [1]] # cat backbone P4
  30. - [-1, 2, C3k2_RFAConv, [512, False]] # 13
  31. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  32. - [[-1, 4], 1, Concat, [1]] # cat backbone P3
  33. - [-1, 2, C3k2_RFAConv, [256, False]] # 16 (P3/8-small)
  34. - [-1, 1, Conv, [256, 3, 2]]
  35. - [[-1, 13], 1, Concat, [1]] # cat head P4
  36. - [-1, 2, C3k2_RFAConv, [512, False]] # 19 (P4/16-medium)
  37. - [-1, 1, Conv, [512, 3, 2]]
  38. - [[-1, 10], 1, Concat, [1]] # cat head P5
  39. - [-1, 2, C3k2_RFAConv, [1024, True]] # 22 (P5/32-large)
  40. - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

5.2 RFAConv的yaml文件二

此版本的训练信息:YOLO11-RFAConv summary: 336 layers, 2,615,451 parameters, 2,615,435 gradients, 6.5 GFLOPs

我只替换了颈部中的两个Conv其实我发的这些机制是全部都替换了,但是大家观看代码其实往往替换其中部分产生效果的可能性最大.

  1. # Ultralytics YOLO 🚀, AGPL-3.0 license
  2. # YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
  3. # Parameters
  4. nc: 80 # number of classes
  5. scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
  6. # [depth, width, max_channels]
  7. n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
  8. s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
  9. m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
  10. l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
  11. x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
  12. # YOLO11n backbone
  13. backbone:
  14. # [from, repeats, module, args]
  15. - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
  16. - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
  17. - [-1, 2, C3k2, [256, False, 0.25]]
  18. - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  19. - [-1, 2, C3k2, [512, False, 0.25]]
  20. - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
  21. - [-1, 2, C3k2, [512, True]]
  22. - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
  23. - [-1, 2, C3k2, [1024, True]]
  24. - [-1, 1, SPPF, [1024, 5]] # 9
  25. - [-1, 2, C2PSA, [1024]] # 10
  26. # YOLO11n head
  27. head:
  28. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  29. - [[-1, 6], 1, Concat, [1]] # cat backbone P4
  30. - [-1, 2, C3k2, [512, False]] # 13
  31. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  32. - [[-1, 4], 1, Concat, [1]] # cat backbone P3
  33. - [-1, 2, C3k2, [256, False]] # 16 (P3/8-small)
  34. - [-1, 1, RFAConv, [256, 3, 2]]
  35. - [[-1, 13], 1, Concat, [1]] # cat head P4
  36. - [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium)
  37. - [-1, 1, RFAConv, [512, 3, 2]]
  38. - [[-1, 10], 1, Concat, [1]] # cat head P5
  39. - [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large)
  40. - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

5.3 RFAConv的训练过程截图


五、本文总结

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~