当前位置:   article > 正文

YOLOv8改进之C2f模块融合CVPR2023 SCConv

scconv

目录

 

1. SCConv

 SCConv模块的设计

SCConv模块的性能

 2. YOLOv8 C2f融合SCConv模块


 

1. SCConv

卷积在各种计算机视觉任务中表现出色,但是由于卷积层提取冗余特征,其计算资源需求巨大。虽然过去用于改善网络效率的各种模型压缩策略和网络设计,包括网络剪枝权重量化低秩分解知识蒸馏等。然而,这些方法都被视为后处理步骤,因此它们的性能通常受到给定初始模型的上限约束。而网络设计另辟蹊径,试图减少密集模型参数中的固有冗余,进一步开发轻量级网络模型。

 SCConv模块的设计

为了解决上述问题,论文(SCConv: Spatial and Channel Reconstruction Convolution for Feature Redundancy (thecvf.com))提出了一个新的卷积模块,名为SCConv,这个模块利用了两个组件:空间重建单元SRU)和通道重建单元CRU)。

  • SRU 通过一种分离-重建的方法抑制空间冗余
  • CRU 则采用一种分割-转换-融合的策略减少通道冗余

此外,SCConv 是一个即插即用的架构单元,可以直接替换各种卷积神经网络中的标准卷积。

SCConv模块的性能

SCConv 模块旨在有效地限制特征冗余,不仅减少了模型参数和FLOPs的数量,而且增强了特征表示的能力。实际上,SCConv 模块提供了一种新的视角来看待CNNs的特征提取过程,提出了一种更有效地利用空间和通道冗余的方法,从而在减少冗余特征的同时提高模型性能。实验结果显示,嵌入了 SCConv 模块的模型能够通过显著降低复杂性和计算成本,减少冗余特征,从而达到更好的性能。

34f8c45daaa544d9a9e2065bb0bdbe21.png

 SRU

1fed51edcee74d3390a7e3e5918d87e9.png

CRU

e01e4ecd86b445948212b3cb64f4e367.png

 2. YOLOv8 C2f融合SCConv模块

加入融合ScConv的C2f模块,在ultralytics包中的nn包的modules中的block.py文件中添加改进模块。代码如下:

  1. class SRU(nn.Module):
  2. def __init__(self,
  3. oup_channels: int,
  4. group_num: int = 16,
  5. gate_treshold: float = 0.5
  6. ):
  7. super().__init__()
  8. self.gn = GroupBatchnorm2d(oup_channels, group_num=group_num)
  9. self.gate_treshold = gate_treshold
  10. self.sigomid = nn.Sigmoid()
  11. def forward(self, x):
  12. gn_x = self.gn(x)
  13. w_gamma = self.gn.gamma / sum(self.gn.gamma)
  14. reweigts = self.sigomid(gn_x * w_gamma)
  15. # Gate
  16. info_mask = reweigts >= self.gate_treshold
  17. noninfo_mask = reweigts < self.gate_treshold
  18. x_1 = info_mask * x
  19. x_2 = noninfo_mask * x
  20. x = self.reconstruct(x_1, x_2)
  21. return x
  22. def reconstruct(self, x_1, x_2):
  23. x_11, x_12 = torch.split(x_1, x_1.size(1) // 2, dim=1)
  24. x_21, x_22 = torch.split(x_2, x_2.size(1) // 2, dim=1)
  25. return torch.cat([x_11 + x_22, x_12 + x_21], dim=1)
  26. class CRU(nn.Module):
  27. '''
  28. alpha: 0<alpha<1
  29. '''
  30. def __init__(self,
  31. op_channel: int,
  32. alpha: float = 1 / 2,
  33. squeeze_radio: int = 2,
  34. group_size: int = 2,
  35. group_kernel_size: int = 3,
  36. ):
  37. super().__init__()
  38. self.up_channel = up_channel = int(alpha * op_channel)
  39. self.low_channel = low_channel = op_channel - up_channel
  40. self.squeeze1 = nn.Conv2d(up_channel, up_channel // squeeze_radio, kernel_size=1, bias=False)
  41. self.squeeze2 = nn.Conv2d(low_channel, low_channel // squeeze_radio, kernel_size=1, bias=False)
  42. # up
  43. self.GWC = nn.Conv2d(up_channel // squeeze_radio, op_channel, kernel_size=group_kernel_size, stride=1,
  44. padding=group_kernel_size // 2, groups=group_size)
  45. self.PWC1 = nn.Conv2d(up_channel // squeeze_radio, op_channel, kernel_size=1, bias=False)
  46. # low
  47. self.PWC2 = nn.Conv2d(low_channel // squeeze_radio, op_channel - low_channel // squeeze_radio, kernel_size=1,
  48. bias=False)
  49. self.advavg = nn.AdaptiveAvgPool2d(1)
  50. def forward(self, x):
  51. # Split
  52. up, low = torch.split(x, [self.up_channel, self.low_channel], dim=1)
  53. up, low = self.squeeze1(up), self.squeeze2(low)
  54. # Transform
  55. Y1 = self.GWC(up) + self.PWC1(up)
  56. Y2 = torch.cat([self.PWC2(low), low], dim=1)
  57. # Fuse
  58. out = torch.cat([Y1, Y2], dim=1)
  59. out = F.softmax(self.advavg(out), dim=1) * out
  60. out1, out2 = torch.split(out, out.size(1) // 2, dim=1)
  61. return out1 + out2
  62. class ScConv(nn.Module):
  63. # https://github.com/cheng-haha/ScConv/blob/main/ScConv.py
  64. def __init__(self,
  65. op_channel: int,
  66. group_num: int = 16,
  67. gate_treshold: float = 0.5,
  68. alpha: float = 1 / 2,
  69. squeeze_radio: int = 2,
  70. group_size: int = 2,
  71. group_kernel_size: int = 3,
  72. ):
  73. super().__init__()
  74. self.SRU = SRU(op_channel,
  75. group_num=group_num,
  76. gate_treshold=gate_treshold)
  77. self.CRU = CRU(op_channel,
  78. alpha=alpha,
  79. squeeze_radio=squeeze_radio,
  80. group_size=group_size,
  81. group_kernel_size=group_kernel_size)
  82. def forward(self, x):
  83. x = self.SRU(x)
  84. x = self.CRU(x)
  85. return x
  86. class Bottleneck_ScConv(Bottleneck):
  87. def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
  88. super().__init__(c1, c2, shortcut, g, k, e)
  89. c_ = int(c2 * e) # hidden channels
  90. self.cv1 = Conv(c1, c_, k[0], 1)
  91. self.cv2 = ScConv(c2)
  92. class C2f_ScConv(C2f):
  93. def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
  94. super().__init__(c1, c2, n, shortcut, g, e)
  95. self.m = nn.ModuleList(Bottleneck_ScConv(self.c, self.c, shortcut, g, k=(3, 3), e=1.0) for _ in range(n))

对融合ScConv的C2f模块的进行注册和引用,注册方式参考YOLOv8改进算法之添加CA注意力机制-CSDN博客

 在tasks.py中的parse_model中添加C2f_ScConv:

299f7928eeef4bc0bc3d6110fe307c59.jpeg

 新建相应的yaml文件,代码如下:

  1. # Ultralytics YOLO 本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
    推荐阅读
    相关标签