学习资源站

YOLOv11改进-主干_Backbone篇-华为移动端目标检测模型Ghostnetv2改进特征提取网络

一、本文介绍

本文给大家带来的改进机制是华为移动端模型Ghostnetv2,华为 GhostNetV2 是为 移动应用设计的轻量级卷积神经网络(CNN) ,旨在提供更快的推理速度,其引入了一种 硬件 友好的注意力机制,称为DFC注意力。这个注意力机制是基于全连接层构建的,它的设计目的是在通用硬件上快速执行,并且能够捕捉像素之间的长距离依赖关系,本文将通过首先介绍其主要原理,然后手把手教大家如何使用该网络结构改进我们的特征提取网络。 欢迎大家订阅本专栏,本专栏每周更新3-5篇最新机制,更有包含我所有改进的文件和交流群提供给大家。

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



二、Ghostnetv2原理

论文地址: 论文官方地址

代码地址: 代码官方地址


2.1  Ghostnetv2的基本原理

华为 GhostNetV2 是为 移动应用设计的轻量级卷积神经网络(CNN) ,旨在提供更快的推理速度。

GhostNetV2的 基本原理包括以下关键要点

1. 硬件友好的DFC注意力机制: GhostNetV2引入了一种硬件友好的注意力机制,称为DFC注意力。这个注意力机制是基于全连接层构建的,它的设计目的是在通用硬件上快速执行,并且能够捕捉像素之间的长距离依赖关系。DFC注意力的引入有助于GhostNetV2更好地捕获全局信息,而不会显著降低推理速度。

2. GhostNetV2架构: GhostNetV2采用了一种新的网络架构,用于移动应用。这个架构重新审视了GhostNet中的表现能力瓶颈,并提出了一种方法,通过DFC注意力来增强由廉价操作生成的扩展特征。这使得GhostNetV2块能够同时整合本地和长距离信息,从而提高了特征表示的质量和性能。

总结: GhostNetV2的基本原理包括引入硬件友好的DFC注意力机制,重新设计的GhostNetV2架构,以及通过DFC注意力增强特征表示以提高性能。这使得GhostNetV2成为适用于移动设备的高性能轻量级 CNN


2.2 DFC注意力机制

DFC(Directional Feature Calibration)注意力机制 是一种用于 改善特征表示 的注意力机制,特别适用于移动应用。

以下是总结的DFC注意力机制的 主要特点和工作原理:

1. 方向性特征校准: DFC注意力机制旨在校准 卷积神经网络 中的特征,以更好地捕捉长距离的信息。它利用了方向性的特点,通过水平和垂直两个方向上的全连接层来捕获长程信息。

2. 全连接层: DFC注意力机制包含两个全连接层,一个用于水平方向,另一个用于垂直方向。这些全连接层充当了注意力调整的 组件 ,它们负责处理像素之间的依赖关系。

3. 硬件友好: DFC注意力机制的设计考虑了硬件执行效率,因此可以在通用硬件上快速执行,而不会显著降低推理速度。

4. 捕捉长距离信息: DFC注意力机制通过全连接层捕获了图像中像素之间的长距离依赖关系。这有助于网络更好地理解图像中的全局信息,而不仅仅是局部信息。

5. 特征增强: DFC注意力机制通过调整特征映射中的权重来增强特征表示。这种调整使模型能够更好地适应任务需求,从而提高了性能。

下面为大家展示的是 DFC注意力的信息流程:

水平和垂直的全连接层分别捕捉两个方向上的远程信息 在DFC注意力中,一个块是由其垂直/水平线上的块直接聚合的,而其他块参与了垂直/水平线上块的生成,与焦点标记存在间接关系。

下面为 DFC注意力的一般公式:

分别沿 水平和垂直方向聚合像素 。通过共享部分变换权重,可以方便地使用卷积来实现,省去了影响实际推理速度的耗时张量重塑和转置操作。

为了处理具有不同分辨率的输入图像,滤波器的大小可以与特征图的大小分离,即在输入特征上依次应用两个核大小为1× K_{H}K_{W} ×1的深度卷积。当使用卷积实现时,DFC注意力的理论复杂性表示为 O(H^{2}W+K_{W}HW) 。这种策略在诸如 TFLite ONNX 等工具中得到很好的支持,用于在移动设备上进行快速推理。

总结: GhostNetV2的DFC注意力机制是一种专为 移动应用设计 的硬件友好型注意力机制,旨在通过全连接层捕获图像中的长距离依赖关系,从而提高特征表示和模型性能。它是GhostNetV2架构的重要组成部分,有助于该网络在保持计算效率的同时实现更高的准确度。


2.3 GhostNetV2架构

GhostNetV2架构 是GhostNetV2模型的网络结构,旨在提供 高性能的轻量级卷积神经网络(CNN)解决方案 ,特别适用于移动设备。

GhostNetV2架构的 主要特点 包括以下要点:

1. Ghost Block: GhostNetV2的基本构建块是Ghost Block,它由两个Ghost模块堆叠而成。这个Ghost Block采用了反向瓶颈的结构,第一个Ghost模块充当扩展层,增加输出通道数,第二个Ghost模块减少通道数以匹配快捷路径。这种结构有助于提高特征的抽象能力和表示质量。

2. 移动设备适用: GhostNetV2架构专门设计用于移动应用,旨在提供更快的推理速度和更高的计算效率。它采用了轻量级设计,可以在计算资源有限的移动设备上高效运行。

下图展示了 GhostNetV1和GhostNetV2中块的示意图 。Ghost块是一个包含 两个Ghost模块的反向残差瓶颈 ,在其中DFC注意力增强了扩展特征以提高表达能力。

图(a): GhostNet的一个块是由堆叠两个Ghost模块构建的。与MobileNetV2 类似,它也是一个反向瓶颈,即第一个Ghost模块充当扩展层以增加输出通道的数量,而第二个Ghost模块减少通道的数量以匹配快捷路径。

图(b): 展示了 GhostV2瓶颈的示意图 。DFC注意力分支与第一个Ghost模块并行,用于增强扩展特征。然后,增强的特征被送到第二个Ghost模块以产生输出特征。它捕捉了不同空间位置中像素之间的长程依赖关系,并增强了模型的表达能力。这个结构有助于提高模型的性能和特征表示。

总结: GhostNetV2架构是GhostNetV2模型的核心组成部分,它的设计目标是在移动设备上实现高性能的轻量级图像分类解决方案。通过引入DFC注意力和优化的Ghost Block,GhostNetV2架构成功地提高了模型的性能和效率。


三、GhsetNetV2的核心代码

使用方式看章节四!

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. import math
  5. __all__ = ['GhostNetV2']
  6. def _make_divisible(v, divisor, min_value=None):
  7. """
  8. This function is taken from the original tf repo.
  9. It ensures that all layers have a channel number that is divisible by 8
  10. It can be seen here:
  11. https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
  12. """
  13. if min_value is None:
  14. min_value = divisor
  15. new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
  16. # Make sure that round down does not go down by more than 10%.
  17. if new_v < 0.9 * v:
  18. new_v += divisor
  19. return new_v
  20. def hard_sigmoid(x, inplace: bool = False):
  21. if inplace:
  22. return x.add_(3.).clamp_(0., 6.).div_(6.)
  23. else:
  24. return F.relu6(x + 3.) / 6.
  25. class SqueezeExcite(nn.Module):
  26. def __init__(self, in_chs, se_ratio=0.25, reduced_base_chs=None,
  27. act_layer=nn.ReLU, gate_fn=hard_sigmoid, divisor=4, **_):
  28. super(SqueezeExcite, self).__init__()
  29. self.gate_fn = gate_fn
  30. reduced_chs = _make_divisible((reduced_base_chs or in_chs) * se_ratio, divisor)
  31. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  32. self.conv_reduce = nn.Conv2d(in_chs, reduced_chs, 1, bias=True)
  33. self.act1 = act_layer(inplace=True)
  34. self.conv_expand = nn.Conv2d(reduced_chs, in_chs, 1, bias=True)
  35. def forward(self, x):
  36. x_se = self.avg_pool(x)
  37. x_se = self.conv_reduce(x_se)
  38. x_se = self.act1(x_se)
  39. x_se = self.conv_expand(x_se)
  40. x = x * self.gate_fn(x_se)
  41. return x
  42. class ConvBnAct(nn.Module):
  43. def __init__(self, in_chs, out_chs, kernel_size,
  44. stride=1, act_layer=nn.ReLU):
  45. super(ConvBnAct, self).__init__()
  46. self.conv = nn.Conv2d(in_chs, out_chs, kernel_size, stride, kernel_size // 2, bias=False)
  47. self.bn1 = nn.BatchNorm2d(out_chs)
  48. self.act1 = act_layer(inplace=True)
  49. def forward(self, x):
  50. x = self.conv(x)
  51. x = self.bn1(x)
  52. x = self.act1(x)
  53. return x
  54. class GhostModuleV2(nn.Module):
  55. def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True, mode=None, args=None):
  56. super(GhostModuleV2, self).__init__()
  57. self.mode = mode
  58. self.gate_fn = nn.Sigmoid()
  59. if self.mode in ['original']:
  60. self.oup = oup
  61. init_channels = math.ceil(oup / ratio)
  62. new_channels = init_channels * (ratio - 1)
  63. self.primary_conv = nn.Sequential(
  64. nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size // 2, bias=False),
  65. nn.BatchNorm2d(init_channels),
  66. nn.ReLU(inplace=True) if relu else nn.Sequential(),
  67. )
  68. self.cheap_operation = nn.Sequential(
  69. nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size // 2, groups=init_channels, bias=False),
  70. nn.BatchNorm2d(new_channels),
  71. nn.ReLU(inplace=True) if relu else nn.Sequential(),
  72. )
  73. elif self.mode in ['attn']:
  74. self.oup = oup
  75. init_channels = math.ceil(oup / ratio)
  76. new_channels = init_channels * (ratio - 1)
  77. self.primary_conv = nn.Sequential(
  78. nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size // 2, bias=False),
  79. nn.BatchNorm2d(init_channels),
  80. nn.ReLU(inplace=True) if relu else nn.Sequential(),
  81. )
  82. self.cheap_operation = nn.Sequential(
  83. nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size // 2, groups=init_channels, bias=False),
  84. nn.BatchNorm2d(new_channels),
  85. nn.ReLU(inplace=True) if relu else nn.Sequential(),
  86. )
  87. self.short_conv = nn.Sequential(
  88. nn.Conv2d(inp, oup, kernel_size, stride, kernel_size // 2, bias=False),
  89. nn.BatchNorm2d(oup),
  90. nn.Conv2d(oup, oup, kernel_size=(1, 5), stride=1, padding=(0, 2), groups=oup, bias=False),
  91. nn.BatchNorm2d(oup),
  92. nn.Conv2d(oup, oup, kernel_size=(5, 1), stride=1, padding=(2, 0), groups=oup, bias=False),
  93. nn.BatchNorm2d(oup),
  94. )
  95. def forward(self, x):
  96. if self.mode in ['original']:
  97. x1 = self.primary_conv(x)
  98. x2 = self.cheap_operation(x1)
  99. out = torch.cat([x1, x2], dim=1)
  100. return out[:, :self.oup, :, :]
  101. elif self.mode in ['attn']:
  102. res = self.short_conv(F.avg_pool2d(x, kernel_size=2, stride=2))
  103. x1 = self.primary_conv(x)
  104. x2 = self.cheap_operation(x1)
  105. out = torch.cat([x1, x2], dim=1)
  106. return out[:, :self.oup, :, :] * F.interpolate(self.gate_fn(res), size=(out.shape[-2], out.shape[-1]),
  107. mode='nearest')
  108. class GhostBottleneckV2(nn.Module):
  109. def __init__(self, in_chs, mid_chs, out_chs, dw_kernel_size=3,
  110. stride=1, act_layer=nn.ReLU, se_ratio=0., layer_id=None, args=None):
  111. super(GhostBottleneckV2, self).__init__()
  112. has_se = se_ratio is not None and se_ratio > 0.
  113. self.stride = stride
  114. # Point-wise expansion
  115. if layer_id <= 1:
  116. self.ghost1 = GhostModuleV2(in_chs, mid_chs, relu=True, mode='original', args=args)
  117. else:
  118. self.ghost1 = GhostModuleV2(in_chs, mid_chs, relu=True, mode='attn', args=args)
  119. # Depth-wise convolution
  120. if self.stride > 1:
  121. self.conv_dw = nn.Conv2d(mid_chs, mid_chs, dw_kernel_size, stride=stride,
  122. padding=(dw_kernel_size - 1) // 2, groups=mid_chs, bias=False)
  123. self.bn_dw = nn.BatchNorm2d(mid_chs)
  124. # Squeeze-and-excitation
  125. if has_se:
  126. self.se = SqueezeExcite(mid_chs, se_ratio=se_ratio)
  127. else:
  128. self.se = None
  129. self.ghost2 = GhostModuleV2(mid_chs, out_chs, relu=False, mode='original', args=args)
  130. # shortcut
  131. if (in_chs == out_chs and self.stride == 1):
  132. self.shortcut = nn.Sequential()
  133. else:
  134. self.shortcut = nn.Sequential(
  135. nn.Conv2d(in_chs, in_chs, dw_kernel_size, stride=stride,
  136. padding=(dw_kernel_size - 1) // 2, groups=in_chs, bias=False),
  137. nn.BatchNorm2d(in_chs),
  138. nn.Conv2d(in_chs, out_chs, 1, stride=1, padding=0, bias=False),
  139. nn.BatchNorm2d(out_chs),
  140. )
  141. def forward(self, x):
  142. residual = x
  143. x = self.ghost1(x)
  144. if self.stride > 1:
  145. x = self.conv_dw(x)
  146. x = self.bn_dw(x)
  147. if self.se is not None:
  148. x = self.se(x)
  149. x = self.ghost2(x)
  150. x += self.shortcut(residual)
  151. return x
  152. class GhostNetV2(nn.Module):
  153. def __init__(self, cfgs, num_classes=1000, width=1.0, dropout=0.2, block=GhostBottleneckV2, args=None):
  154. super(GhostNetV2, self).__init__()
  155. self.cfgs = cfgs
  156. self.dropout = dropout
  157. self.num_classes = num_classes
  158. # building first layer
  159. output_channel = _make_divisible(16 * width, 4)
  160. self.conv_stem = nn.Conv2d(3, output_channel, 3, 2, 1, bias=False)
  161. self.bn1 = nn.BatchNorm2d(output_channel)
  162. self.act1 = nn.ReLU(inplace=True)
  163. input_channel = output_channel
  164. # building inverted residual blocks
  165. stages = []
  166. # block = block
  167. layer_id = 0
  168. for cfg in self.cfgs:
  169. layers = []
  170. for k, exp_size, c, se_ratio, s in cfg:
  171. output_channel = _make_divisible(c * width, 4)
  172. hidden_channel = _make_divisible(exp_size * width, 4)
  173. if block == GhostBottleneckV2:
  174. layers.append(block(input_channel, hidden_channel, output_channel, k, s,
  175. se_ratio=se_ratio, layer_id=layer_id, args=args))
  176. input_channel = output_channel
  177. layer_id += 1
  178. stages.append(nn.Sequential(*layers))
  179. output_channel = _make_divisible(exp_size * width, 4)
  180. stages.append(nn.Sequential(ConvBnAct(input_channel, output_channel, 1)))
  181. input_channel = output_channel
  182. self.blocks = nn.Sequential(*stages)
  183. self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]
  184. def reset_classifier(self, num_classes, global_avg=''):
  185. self.num_classes = num_classes
  186. self.classifier = nn.Linear(1280, self.num_classes) if self.num_classes > 0 else nn.Identity()
  187. def forward(self, x):
  188. unique_tensors = {}
  189. x = self.conv_stem(x)
  190. x = self.bn1(x)
  191. x = self.act1(x)
  192. for model in self.blocks:
  193. x = model(x)
  194. if self.dropout > 0.:
  195. x = F.dropout(x, p=self.dropout, training=self.training)
  196. width, height = x.shape[2], x.shape[3]
  197. unique_tensors[(width, height)] = x
  198. result_list = list(unique_tensors.values())[-4:]
  199. return result_list
  200. def Ghostnetv2(pretrained=False, pretrained_cfg=None, pretrained_cfg_overlay=None, **kwargs):
  201. cfgs = [
  202. # k, t, c, SE, s
  203. [[3, 16, 16, 0, 1]],
  204. [[3, 48, 24, 0, 2]],
  205. [[3, 72, 24, 0, 1]],
  206. [[5, 72, 40, 0.25, 2]],
  207. [[5, 120, 40, 0.25, 1]],
  208. [[3, 240, 80, 0, 2]],
  209. [[3, 200, 80, 0, 1],
  210. [3, 184, 80, 0, 1],
  211. [3, 184, 80, 0, 1],
  212. [3, 480, 112, 0.25, 1],
  213. [3, 672, 112, 0.25, 1]
  214. ],
  215. [[5, 672, 160, 0.25, 2]],
  216. [[5, 960, 160, 0, 1],
  217. [5, 960, 160, 0.25, 1],
  218. [5, 960, 160, 0, 1],
  219. [5, 960, 160, 0.25, 1]
  220. ]
  221. ]
  222. return GhostNetV2(cfgs)
  223. if __name__=='__main__':
  224. model = Ghostnetv2()
  225. model.eval()
  226. input = torch.randn(16,3,224,224)
  227. y = model(input)
  228. print(y.size())


四、手把手教你添加GhsetNetV2

4.1 修改一

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


4.2 修改二

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


4.3 修改三

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

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


4.4 修改四

添加如下两行代码!!!


4.5 修改五

找到七百多行大概把具体看图片,按照图片来修改就行,添加红框内的部分,注意没有()只是函数名。

  1. elif m in {自行添加对应的模型即可,下面都是一样的}:
  2. m = m(*args)
  3. c2 = m.width_list # 返回通道列表
  4. backbone = True


4.6 修改六

下面的两个红框内都是需要改动的。

  1. if isinstance(c2, list):
  2. m_ = m
  3. m_.backbone = True
  4. else:
  5. m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module
  6. t = str(m)[8:-2].replace('__main__.', '') # module type
  7. m.np = sum(x.numel() for x in m_.parameters()) # number params
  8. m_.i, m_.f, m_.type = i + 4 if backbone else i, f, t # attach index, 'from' index, type


4.7 修改七

如下的也需要修改,全部按照我的来。

代码如下把原先的代码替换了即可。

  1. if verbose:
  2. LOGGER.info(f'{i:>3}{str(f):>20}{n_:>3}{m.np:10.0f} {t:<45}{str(args):<30}') # print
  3. save.extend(x % (i + 4 if backbone else i) for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
  4. layers.append(m_)
  5. if i == 0:
  6. ch = []
  7. if isinstance(c2, list):
  8. ch.extend(c2)
  9. if len(c2) != 5:
  10. ch.insert(0, 0)
  11. else:
  12. ch.append(c2)


4.8 修改八

修改八和前面的都不太一样,需要修改前向传播中的一个部分, 已经离开了parse_model方法了。

可以在图片中开代码行数,没有离开task.py文件都是同一个文件。 同时这个部分有好几个前向传播都很相似,大家不要看错了, 是70多行左右的!!!,同时我后面提供了代码,大家直接复制粘贴即可,有时间我针对这里会出一个视频。

​​

代码如下->

  1. def _predict_once(self, x, profile=False, visualize=False, embed=None):
  2. """
  3. Perform a forward pass through the network.
  4. Args:
  5. x (torch.Tensor): The input tensor to the model.
  6. profile (bool): Print the computation time of each layer if True, defaults to False.
  7. visualize (bool): Save the feature maps of the model if True, defaults to False.
  8. embed (list, optional): A list of feature vectors/embeddings to return.
  9. Returns:
  10. (torch.Tensor): The last output of the model.
  11. """
  12. y, dt, embeddings = [], [], [] # outputs
  13. for m in self.model:
  14. if m.f != -1: # if not from previous layer
  15. x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
  16. if profile:
  17. self._profile_one_layer(m, x, dt)
  18. if hasattr(m, 'backbone'):
  19. x = m(x)
  20. if len(x) != 5: # 0 - 5
  21. x.insert(0, None)
  22. for index, i in enumerate(x):
  23. if index in self.save:
  24. y.append(i)
  25. else:
  26. y.append(None)
  27. x = x[-1] # 最后一个输出传给下一层
  28. else:
  29. x = m(x) # run
  30. y.append(x if m.i in self.save else None) # save output
  31. if visualize:
  32. feature_visualization(x, m.type, m.i, save_dir=visualize)
  33. if embed and m.i in embed:
  34. embeddings.append(nn.functional.adaptive_avg_pool2d(x, (1, 1)).squeeze(-1).squeeze(-1)) # flatten
  35. if m.i == max(embed):
  36. return torch.unbind(torch.cat(embeddings, 1), dim=0)
  37. return x

到这里就完成了修改部分,但是这里面细节很多,大家千万要注意不要替换多余的代码,导致报错,也不要拉下任何一部,都会导致运行失败,而且报错很难排查!!!很难排查!!!


注意!!! 额外的修改!

关注我的其实都知道,我大部分的修改都是一样的,这个网络需要额外的修改一步,就是s一个参数,将下面的s改为640!!!即可完美运行!!


打印计算量问题解决方案

我们找到如下文件'ultralytics/utils/torch_utils.py'按照如下的图片进行修改,否则容易打印不出来计算量。


注意事项!!!

如果大家在验证的时候报错形状不匹配的错误可以固定验证集的图片尺寸,方法如下 ->

找到下面这个文件ultralytics/ models /yolo/detect/train.py然后其中有一个类是DetectionTrainer class中的build_dataset函数中的一个参数rect=mode == 'val'改为rect=False


五、GhsetNetV2的yaml文件

5.1 yaml文件

此版本训练信息:YOLO11-GhostNetV2 summary: 740 layers, 6,070,911 parameters, 6,070,895 gradients, 7.2 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, Ghostnetv2, []] # 0-4 P1/2
  16. - [-1, 1, SPPF, [1024, 5]] # 5
  17. - [-1, 2, C2PSA, [1024]] # 6
  18. # YOLO11n head
  19. head:
  20. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  21. - [[-1, 3], 1, Concat, [1]] # cat backbone P4
  22. - [-1, 2, C3k2, [512, False]] # 9
  23. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  24. - [[-1, 2], 1, Concat, [1]] # cat backbone P3
  25. - [-1, 2, C3k2, [256, False]] # 12 (P3/8-small)
  26. - [-1, 1, Conv, [256, 3, 2]]
  27. - [[-1, 9], 1, Concat, [1]] # cat head P4
  28. - [-1, 2, C3k2, [512, False]] # 15 (P4/16-medium)
  29. - [-1, 1, Conv, [512, 3, 2]]
  30. - [[-1, 6], 1, Concat, [1]] # cat head P5
  31. - [-1, 2, C3k2, [1024, True]] # 18 (P5/32-large)
  32. - [[12, 15, 18], 1, Detect, [nc]] # Detect(P3, P4, P5)

5.2 训练文件的代码

可以复制我的运行文件进行运行。

  1. import warnings
  2. warnings.filterwarnings('ignore')
  3. from ultralytics import YOLO
  4. if __name__ == '__main__':
  5. model = YOLO('yolov8-MLLA.yaml')
  6. # 如何切换模型版本, 上面的ymal文件可以改为 yolov8s.yaml就是使用的v8s,
  7. # 类似某个改进的yaml文件名称为yolov8-XXX.yaml那么如果想使用其它版本就把上面的名称改为yolov8l-XXX.yaml即可(改的是上面YOLO中间的名字不是配置文件的)!
  8. # model.load('yolov8n.pt') # 是否加载预训练权重,科研不建议大家加载否则很难提升精度
  9. model.train(data=r"C:\Users\Administrator\PycharmProjects\yolov5-master\yolov5-master\Construction Site Safety.v30-raw-images_latestversion.yolov8\data.yaml",
  10. # 如果大家任务是其它的'ultralytics/cfg/default.yaml'找到这里修改task可以改成detect, segment, classify, pose
  11. cache=False,
  12. imgsz=640,
  13. epochs=150,
  14. single_cls=False, # 是否是单类别检测
  15. batch=16,
  16. close_mosaic=0,
  17. workers=0,
  18. device='0',
  19. optimizer='SGD', # using SGD
  20. # resume='runs/train/exp21/weights/last.pt', # 如过想续训就设置last.pt的地址
  21. amp=True, # 如果出现训练损失为Nan可以关闭amp
  22. project='runs/train',
  23. name='exp',
  24. )

六、成功运行记录

下面是成功运行的截图,已经完成了有1个epochs的训练,图片太大截不全第2个epochs了。


七、本文总结

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