YOLOv11改进 | Conv篇 | 利用FasterBlock二次创新C3k2提出一种全新的结构(全网独家首发)

一、本文介绍

本文给大家带来的改进机制是利用FasterNet的FasterBlock改进特征提取网络,将其用来改进 ResNet网络 ,其旨在 提高计算速度而不牺牲准确性 ,特别是在视觉任务中。它通过一种称为 部分卷积(PConv) 的新技术来减少冗余计算和内存访问。这种方法使得FasterNet在多种设备上运行速度比其他网络快得多,同时在各种视觉任务中保持高准确率,同时本文的内容为我独家创新,全网仅此一份, 同时本文的改进机制参数量下降、计算量均有下降

欢迎大家订阅我的专栏一起学习YOLO!


目录

一、本文介绍

二、FasterNet原理

2.1 FasterNet的基本原理

2.2 部分卷积

2.3 加速神经网络

三、FasterBlock的核心代码

四、 手把手教你添加FasterBlock机制

4.1 修改一

4.2 修改二

4.3 修改三

4.4 修改四

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

5.1 FasterBlock的yaml文件

5.2 训练代码

5.3 FasterBlock的训练过程截图

五、本文总结


二、FasterNet原理

论文地址: 官方论文地址

代码地址: 官方代码地址


2.1 FasterNet的基本原理

FasterNet 是一种高效的 神经网络 架构,旨在 提高计算速度而不牺牲准确性 ,特别是在视觉任务中。它通过一种称为 部分卷积(PConv) 的新技术来减少冗余计算和内存访问。这种方法使得FasterNet在多种设备上运行速度比其他网络快得多,同时在各种视觉任务中保持高准确率。例如,FasterNet在ImageNet-1k数据集上的表现超过了其他模型,如 MobileViT-XXS ,展现了其在速度和准确度方面的优势。

FasterNet的基本原理可以总结为以下几点:

1. 部分卷积(PConv): FasterNet引入了部分卷积(PConv),这是一种新型的卷积方法,它通过只处理输入通道的一部分来减少计算量和内存访问。

2. 加速神经网络 : FasterNet利用PConv的优势,实现了在多种设备上比其他现有神经网络更快的运行速度,同时保持了较高的准确度。

下面为大家展示的是 FasterNet的整体架构

它包括四个层次化的阶段,每个阶段由一系列FasterNet块组成,并由嵌入或合并层开头。最后三层用于特征分类。在每个FasterNet块中,PConv层之后是两个点状卷积(PWConv)层。为了保持特征多样性并实现更低的延迟,仅在中间层之后放置了 归一化和激活层


2.2 部分卷积

部分卷积(PConv) 是一种 卷积神经网络 中的操作,旨在提高计算效率。它通过 只在输入特征图的一部分上执行卷积操作 ,而非传统卷积操作中的全面应用。这样,PConv可以减少不必要的计算和内存访问,因为它忽略了输入中认为是冗余的部分。这种方法特别适合在资源有限的设备上运行 深度学习 模型,因为它可以在不牺牲太多性能的情况下,显著降低计算需求。

下面我为大家展示了FasterNet中的 部分卷积(PConv)与传统卷积和深度卷积/分组卷积的比较

PConv通过仅对输入通道的一小部分应用滤波器,同时保持其余通道不变,实现了快速和高效的特性提取。PConv的计算复杂度 (FLOPs) 低于常规卷积,但高于深度卷积/分组卷积,这样在减少计算资源的同时提高了运算性能。


2.3 加速神经网络

加速神经网络 主要通过优化计算路径、减少模型大小和复杂性、提高操作效率,以及使用高效的 硬件 实现等方式来降低模型的推理时间。这些方法包括

简化网络层 使用更快的激活函数 采用量化技术 浮点运算转换为整数运算 ,以及使用特殊的算法来减少内存访问次数等。通过这些策略,可以在不损害模型准确性的前提下,使神经网络能够更快地处理数据和做出预测。


三、FasterBlock的核心代码

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

  1. import torch
  2. import torch.nn as nn
  3. from timm.models.layers import DropPath
  4. __all__ = ['C3k2_FasterBlock']
  5. class Partial_conv3(nn.Module):
  6. def __init__(self, dim, n_div, forward):
  7. super().__init__()
  8. self.dim_conv3 = dim // n_div
  9. self.dim_untouched = dim - self.dim_conv3
  10. self.partial_conv3 = nn.Conv2d(self.dim_conv3, self.dim_conv3, 3, 1, 1, bias=False)
  11. if forward == 'slicing':
  12. self.forward = self.forward_slicing
  13. elif forward == 'split_cat':
  14. self.forward = self.forward_split_cat
  15. else:
  16. raise NotImplementedError
  17. def forward_slicing(self, x):
  18. # only for inference
  19. x = x.clone() # !!! Keep the original input intact for the residual connection later
  20. x[:, :self.dim_conv3, :, :] = self.partial_conv3(x[:, :self.dim_conv3, :, :])
  21. return x
  22. def forward_split_cat(self, x):
  23. # for training/inference
  24. x1, x2 = torch.split(x, [self.dim_conv3, self.dim_untouched], dim=1)
  25. x1 = self.partial_conv3(x1)
  26. x = torch.cat((x1, x2), 1)
  27. return x
  28. def autopad(k, p=None, d=1): # kernel, padding, dilation
  29. """Pad to 'same' shape outputs."""
  30. if d > 1:
  31. k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
  32. if p is None:
  33. p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
  34. return p
  35. class Conv(nn.Module):
  36. """Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
  37. default_act = nn.SiLU() # default activation
  38. def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
  39. """Initialize Conv layer with given arguments including activation."""
  40. super().__init__()
  41. self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
  42. self.bn = nn.BatchNorm2d(c2)
  43. self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
  44. def forward(self, x):
  45. """Apply convolution, batch normalization and activation to input tensor."""
  46. return self.act(self.bn(self.conv(x)))
  47. def forward_fuse(self, x):
  48. """Perform transposed convolution of 2D data."""
  49. return self.act(self.conv(x))
  50. class FasterBlock(nn.Module):
  51. def __init__(self,
  52. inc,
  53. dim,
  54. n_div=4,
  55. mlp_ratio=2,
  56. drop_path=0.1,
  57. layer_scale_init_value=0.0,
  58. act_layer='RELU',
  59. norm_layer='BN',
  60. pconv_fw_type='split_cat'
  61. ):
  62. super().__init__()
  63. self.dim = dim
  64. self.inc = inc
  65. self.mlp_ratio = mlp_ratio
  66. self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
  67. self.n_div = n_div
  68. mlp_hidden_dim = int(dim * mlp_ratio)
  69. mlp_layer = [
  70. nn.Conv2d(dim, mlp_hidden_dim, 1, bias=False),
  71. nn.BatchNorm2d(mlp_hidden_dim),
  72. nn.ReLU(),
  73. nn.Conv2d(mlp_hidden_dim, dim, 1, bias=False)
  74. ]
  75. self.mlp = nn.Sequential(*mlp_layer)
  76. self.spatial_mixing = Partial_conv3(
  77. dim,
  78. n_div,
  79. pconv_fw_type
  80. )
  81. if inc != dim: # 在输入和输出不等时添加额外处理一步
  82. self.firstConv = Conv(inc, dim, 1)
  83. if layer_scale_init_value > 0:
  84. self.layer_scale = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)
  85. self.forward = self.forward_layer_scale
  86. else:
  87. self.forward = self.forward
  88. def forward(self, x):
  89. if self.inc != self.dim:
  90. x = self.firstConv(x)
  91. shortcut = x
  92. x = self.spatial_mixing(x)
  93. x = shortcut + self.drop_path(self.mlp(x))
  94. return x
  95. def forward_layer_scale(self, x):
  96. if self.inc != self.dim:
  97. x = self.firstConv(x)
  98. shortcut = x
  99. x = self.spatial_mixing(x)
  100. x = shortcut + self.drop_path(
  101. self.layer_scale.unsqueeze(-1).unsqueeze(-1) * self.mlp(x))
  102. return x
  103. def autopad(k, p=None, d=1): # kernel, padding, dilation
  104. """Pad to 'same' shape outputs."""
  105. if d > 1:
  106. k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
  107. if p is None:
  108. p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
  109. return p
  110. class Conv(nn.Module):
  111. """Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
  112. default_act = nn.SiLU() # default activation
  113. def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
  114. """Initialize Conv layer with given arguments including activation."""
  115. super().__init__()
  116. self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
  117. self.bn = nn.BatchNorm2d(c2)
  118. self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
  119. def forward(self, x):
  120. """Apply convolution, batch normalization and activation to input tensor."""
  121. return self.act(self.bn(self.conv(x)))
  122. def forward_fuse(self, x):
  123. """Perform transposed convolution of 2D data."""
  124. return self.act(self.conv(x))
  125. class Bottleneck(nn.Module):
  126. """Standard bottleneck."""
  127. def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
  128. """Initializes a standard bottleneck module with optional shortcut connection and configurable parameters."""
  129. super().__init__()
  130. c_ = int(c2 * e) # hidden channels
  131. self.cv1 = Conv(c1, c_, k[0], 1)
  132. self.cv2 = Conv(c_, c2, k[1], 1, g=g)
  133. self.add = shortcut and c1 == c2
  134. def forward(self, x):
  135. """Applies the YOLO FPN to input data."""
  136. return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
  137. class C2f(nn.Module):
  138. """Faster Implementation of CSP Bottleneck with 2 convolutions."""
  139. def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
  140. """Initializes a CSP bottleneck with 2 convolutions and n Bottleneck blocks for faster processing."""
  141. super().__init__()
  142. self.c = int(c2 * e) # hidden channels
  143. self.cv1 = Conv(c1, 2 * self.c, 1, 1)
  144. self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
  145. self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))
  146. def forward(self, x):
  147. """Forward pass through C2f layer."""
  148. y = list(self.cv1(x).chunk(2, 1))
  149. y.extend(m(y[-1]) for m in self.m)
  150. return self.cv2(torch.cat(y, 1))
  151. def forward_split(self, x):
  152. """Forward pass using split() instead of chunk()."""
  153. y = list(self.cv1(x).split((self.c, self.c), 1))
  154. y.extend(m(y[-1]) for m in self.m)
  155. return self.cv2(torch.cat(y, 1))
  156. class C3(nn.Module):
  157. """CSP Bottleneck with 3 convolutions."""
  158. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
  159. """Initialize the CSP Bottleneck with given channels, number, shortcut, groups, and expansion values."""
  160. super().__init__()
  161. c_ = int(c2 * e) # hidden channels
  162. self.cv1 = Conv(c1, c_, 1, 1)
  163. self.cv2 = Conv(c1, c_, 1, 1)
  164. self.cv3 = Conv(2 * c_, c2, 1) # optional act=FReLU(c2)
  165. self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=((1, 1), (3, 3)), e=1.0) for _ in range(n)))
  166. def forward(self, x):
  167. """Forward pass through the CSP bottleneck with 2 convolutions."""
  168. return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))
  169. class C3k(C3):
  170. """C3k is a CSP bottleneck module with customizable kernel sizes for feature extraction in neural networks."""
  171. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, k=3):
  172. """Initializes the C3k module with specified channels, number of layers, and configurations."""
  173. super().__init__(c1, c2, n, shortcut, g, e)
  174. c_ = int(c2 * e) # hidden channels
  175. # self.m = nn.Sequential(*(RepBottleneck(c_, c_, shortcut, g, k=(k, k), e=1.0) for _ in range(n)))
  176. self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=(k, k), e=1.0) for _ in range(n)))
  177. class C3k2_FasterBlock(C2f):
  178. """Faster Implementation of CSP Bottleneck with 2 convolutions."""
  179. def __init__(self, c1, c2, n=1, c3k=False, e=0.5, g=1, shortcut=True):
  180. """Initializes the C3k2 module, a faster CSP Bottleneck with 2 convolutions and optional C3k blocks."""
  181. super().__init__(c1, c2, n, shortcut, g, e)
  182. self.m = nn.ModuleList(
  183. C3k(self.c, self.c, 2, shortcut, g) if c3k else FasterBlock(self.c, self.c) for _ in range(n)
  184. )
  185. # 解析 c3k在主干和网络最后一个C3k2的时候设置True走的是C3k, 否则我们走的是MSBlock
  186. if __name__ == "__main__":
  187. # Generating Sample image
  188. image_size = (1, 64, 240, 240)
  189. image = torch.rand(*image_size)
  190. # Model
  191. model = C3k2_FasterBlock(64, 64)
  192. out = model(image)
  193. print(out.size())

四、 手把手教你添加FasterBlock机制

4.1 修改一

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


4.2 修改二

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


4.3 修改三

第三步我门中到如下文件'ultralytics/nn/tasks.py'进行导入和注册我们的模块( !


4.4 修改四

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


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


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

5.1 FasterBlock的yaml文件

此版本的训练信息:YOLO11-C3k2-FasterBlock summary: 330 layers, 2,548,347 parameters, 2,548,331 gradients, 6.2 GFLOPs

# 解析 c3k在主干和网络最后一个C3k2的时候设置True走的是C3k, 否则我们走的是 FasterBlock(也就是Neck的三个False时)

  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_FasterBlock, [256, False, 0.25]]
  18. - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  19. - [-1, 2, C3k2_FasterBlock, [512, False, 0.25]]
  20. - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
  21. - [-1, 2, C3k2_FasterBlock, [512, True]]
  22. - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
  23. - [-1, 2, C3k2_FasterBlock, [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_FasterBlock, [512, False]] # 13
  31. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  32. - [[-1, 4], 1, Concat, [1]] # cat backbone P3
  33. - [-1, 2, C3k2_FasterBlock, [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_FasterBlock, [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_FasterBlock, [1024, True]] # 22 (P5/32-large)
  40. - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

5.2 训练代码

大家可以创建一个py文件将我给的代码复制粘贴进去,配置好自己的文件路径即可运行。

  1. import warnings
  2. warnings.filterwarnings('ignore')
  3. from ultralytics import YOLO
  4. if __name__ == '__main__':
  5. model = YOLO('ultralytics/cfg/models/v8/yolov8-C2f-FasterBlock.yaml')
  6. # model.load('yolov8n.pt') # loading pretrain weights
  7. model.train(data=r'替换数据集yaml文件地址',
  8. # 如果大家任务是其它的'ultralytics/cfg/default.yaml'找到这里修改task可以改成detect, segment, classify, pose
  9. cache=False,
  10. imgsz=640,
  11. epochs=150,
  12. single_cls=False, # 是否是单类别检测
  13. batch=4,
  14. close_mosaic=10,
  15. workers=0,
  16. device='0',
  17. optimizer='SGD', # using SGD
  18. # resume='', # 如过想续训就设置last.pt的地址
  19. amp=False, # 如果出现训练损失为Nan可以关闭amp
  20. project='runs/train',
  21. name='exp',
  22. )


5.3 FasterBlock的训练过程截图


五、本文总结

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