一、本文介绍
本文给大家带来的最新改进机制是针对性的改进, 针对于小目标检测增加P2层 , 针对于大目标检测增加P6层 利用DynamicHead(原版本一比一复现,全网独一份,不同于网上魔改版本)进行检测,其中我们增加P2层其拥有更高的 分辨率 ,这使得 模型 能够更好地捕捉到小尺寸目标的细节。我们增加P6层是一个较低分辨率但具有更大感受野的特征层。对于大尺寸目标,这意味着模型可以更有效地捕捉到整体的结构信息。在这些的基础上我们配合DynamicHead可以使模型根据不同尺寸的目标动态调整其检测策略,进一步提升模型的精度。 本文的内容是订阅专栏的读者提出来的,所以大家订阅专栏以后如果有感兴趣的机制均可指定。
欢迎大家订阅我的专栏一起学习YOLO!
二、增加P2和P6层的好处
我们增加P2和P6层是为了改进 目标检测模型 ,特别是在处理不同大小目标的能力上。
1. 增加P2层的好处:
- 改善小目标检测: P2层通常有更高的分辨率,这使得模型能够更好地捕捉到小尺寸目标的细节。较高分辨率的特征图能够提供更多的空间信息,有助于检测小物体。
- 更精细的特征: 由于P2层处于网络的较浅层,它能够捕捉到更多的细粒度特征,这对于理解小目标的形状和纹理非常重要。
2. 增加P6层的好处:
- 提升大目标检测性能: P6层是一个较低分辨率但具有更大感受野的特征层。对于大尺寸目标,这意味着模型可以更有效地捕捉到整体的结构信息。
- 降低计算复杂度: 对于大目标,使用较低分辨率的特征图可以减少计算量,因为处理每个大目标需要的像素数较少。
3. 适应性能力的提升:
- 使用DynamicHead可以使模型根据不同尺寸的目标动态调整其检测策略,进一步提升模型的泛化能力和适应性,从而进一步提高精度。
总结: 增加P2和P6层是为了让模型在处理不同尺寸的目标时更加高效和准确。这种策略特别适用于那些需要同时处理多种尺寸目标的应用场景的数据集,如街景图像分析、无人机视觉监控等。
三、DynamicHead的核心代码
代码的使用方式看章节四,看过前面本检测头的无需在重复添加!!!!!
- import copy
- import math
- from mmcv.ops import ModulatedDeformConv2d
- from ultralytics.utils.tal import dist2bbox, make_anchors
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- __all__ = ['DynamicHead']
- def _make_divisible(v, divisor, min_value=None):
- if min_value is None:
- min_value = divisor
- new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
- # Make sure that round down does not go down by more than 10%.
- if new_v < 0.9 * v:
- new_v += divisor
- return new_v
- class h_swish(nn.Module):
- def __init__(self, inplace=False):
- super(h_swish, self).__init__()
- self.inplace = inplace
- def forward(self, x):
- return x * F.relu6(x + 3.0, inplace=self.inplace) / 6.0
- class h_sigmoid(nn.Module):
- def __init__(self, inplace=True, h_max=1):
- super(h_sigmoid, self).__init__()
- self.relu = nn.ReLU6(inplace=inplace)
- self.h_max = h_max
- def forward(self, x):
- return self.relu(x + 3) * self.h_max / 6
- class DYReLU(nn.Module):
- def __init__(self, inp, oup, reduction=4, lambda_a=1.0, K2=True, use_bias=True, use_spatial=False,
- init_a=[1.0, 0.0], init_b=[0.0, 0.0]):
- super(DYReLU, self).__init__()
- self.oup = oup
- self.lambda_a = lambda_a * 2
- self.K2 = K2
- self.avg_pool = nn.AdaptiveAvgPool2d(1)
- self.use_bias = use_bias
- if K2:
- self.exp = 4 if use_bias else 2
- else:
- self.exp = 2 if use_bias else 1
- self.init_a = init_a
- self.init_b = init_b
- # determine squeeze
- if reduction == 4:
- squeeze = inp // reduction
- else:
- squeeze = _make_divisible(inp // reduction, 4)
- # print('reduction: {}, squeeze: {}/{}'.format(reduction, inp, squeeze))
- # print('init_a: {}, init_b: {}'.format(self.init_a, self.init_b))
- self.fc = nn.Sequential(
- nn.Linear(inp, squeeze),
- nn.ReLU(inplace=True),
- nn.Linear(squeeze, oup * self.exp),
- h_sigmoid()
- )
- if use_spatial:
- self.spa = nn.Sequential(
- nn.Conv2d(inp, 1, kernel_size=1),
- nn.BatchNorm2d(1),
- )
- else:
- self.spa = None
- def forward(self, x):
- if isinstance(x, list):
- x_in = x[0]
- x_out = x[1]
- else:
- x_in = x
- x_out = x
- b, c, h, w = x_in.size()
- y = self.avg_pool(x_in).view(b, c)
- y = self.fc(y).view(b, self.oup * self.exp, 1, 1)
- if self.exp == 4:
- a1, b1, a2, b2 = torch.split(y, self.oup, dim=1)
- a1 = (a1 - 0.5) * self.lambda_a + self.init_a[0] # 1.0
- a2 = (a2 - 0.5) * self.lambda_a + self.init_a[1]
- b1 = b1 - 0.5 + self.init_b[0]
- b2 = b2 - 0.5 + self.init_b[1]
- out = torch.max(x_out * a1 + b1, x_out * a2 + b2)
- elif self.exp == 2:
- if self.use_bias: # bias but not PL
- a1, b1 = torch.split(y, self.oup, dim=1)
- a1 = (a1 - 0.5) * self.lambda_a + self.init_a[0] # 1.0
- b1 = b1 - 0.5 + self.init_b[0]
- out = x_out * a1 + b1
- else:
- a1, a2 = torch.split(y, self.oup, dim=1)
- a1 = (a1 - 0.5) * self.lambda_a + self.init_a[0] # 1.0
- a2 = (a2 - 0.5) * self.lambda_a + self.init_a[1]
- out = torch.max(x_out * a1, x_out * a2)
- elif self.exp == 1:
- a1 = y
- a1 = (a1 - 0.5) * self.lambda_a + self.init_a[0] # 1.0
- out = x_out * a1
- if self.spa:
- ys = self.spa(x_in).view(b, -1)
- ys = F.softmax(ys, dim=1).view(b, 1, h, w) * h * w
- ys = F.hardtanh(ys, 0, 3, inplace=True) / 3
- out = out * ys
- return out
- class Conv3x3Norm(torch.nn.Module):
- def __init__(self, in_channels, out_channels, stride):
- super(Conv3x3Norm, self).__init__()
- self.conv = ModulatedDeformConv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
- self.bn = nn.GroupNorm(num_groups=16, num_channels=out_channels)
- def forward(self, input, **kwargs):
- x = self.conv(input.contiguous(), **kwargs)
- x = self.bn(x)
- return x
- class DyConv(nn.Module):
- def __init__(self, in_channels=256, out_channels=256, conv_func=Conv3x3Norm):
- super(DyConv, self).__init__()
- self.DyConv = nn.ModuleList()
- self.DyConv.append(conv_func(in_channels, out_channels, 1))
- self.DyConv.append(conv_func(in_channels, out_channels, 1))
- self.DyConv.append(conv_func(in_channels, out_channels, 2))
- self.AttnConv = nn.Sequential(
- nn.AdaptiveAvgPool2d(1),
- nn.Conv2d(in_channels, 1, kernel_size=1),
- nn.ReLU(inplace=True))
- self.h_sigmoid = h_sigmoid()
- self.relu = DYReLU(in_channels, out_channels)
- self.offset = nn.Conv2d(in_channels, 27, kernel_size=3, stride=1, padding=1)
- self.init_weights()
- def init_weights(self):
- for m in self.DyConv.modules():
- if isinstance(m, nn.Conv2d):
- nn.init.normal_(m.weight.data, 0, 0.01)
- if m.bias is not None:
- m.bias.data.zero_()
- for m in self.AttnConv.modules():
- if isinstance(m, nn.Conv2d):
- nn.init.normal_(m.weight.data, 0, 0.01)
- if m.bias is not None:
- m.bias.data.zero_()
- def forward(self, x):
- next_x = {}
- feature_names = list(x.keys())
- for level, name in enumerate(feature_names):
- feature = x[name]
- offset_mask = self.offset(feature)
- offset = offset_mask[:, :18, :, :]
- mask = offset_mask[:, 18:, :, :].sigmoid()
- conv_args = dict(offset=offset, mask=mask)
- temp_fea = [self.DyConv[1](feature, **conv_args)]
- if level > 0:
- temp_fea.append(self.DyConv[2](x[feature_names[level - 1]], **conv_args))
- if level < len(x) - 1:
- input = x[feature_names[level + 1]]
- temp_fea.append(F.interpolate(self.DyConv[0](input, **conv_args),
- size=[feature.size(2), feature.size(3)]))
- attn_fea = []
- res_fea = []
- for fea in temp_fea:
- res_fea.append(fea)
- attn_fea.append(self.AttnConv(fea))
- res_fea = torch.stack(res_fea)
- spa_pyr_attn = self.h_sigmoid(torch.stack(attn_fea))
- mean_fea = torch.mean(res_fea * spa_pyr_attn, dim=0, keepdim=False)
- next_x[name] = self.relu(mean_fea)
- return next_x
- def autopad(k, p=None, d=1): # kernel, padding, dilation
- """Pad to 'same' shape outputs."""
- if d > 1:
- k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
- 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 with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
- default_act = nn.SiLU() # default activation
- def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
- """Initialize Conv layer with given arguments including activation."""
- super().__init__()
- self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
- self.bn = nn.BatchNorm2d(c2)
- self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
- def forward(self, x):
- """Apply convolution, batch normalization and activation to input tensor."""
- return self.act(self.bn(self.conv(x)))
- def forward_fuse(self, x):
- """Perform transposed convolution of 2D data."""
- return self.act(self.conv(x))
- class DFL(nn.Module):
- """
- Integral module of Distribution Focal Loss (DFL).
- Proposed in Generalized Focal Loss https://ieeexplore.ieee.org/document/9792391
- """
- def __init__(self, c1=16):
- """Initialize a convolutional layer with a given number of input channels."""
- super().__init__()
- self.conv = nn.Conv2d(c1, 1, 1, bias=False).requires_grad_(False)
- x = torch.arange(c1, dtype=torch.float)
- self.conv.weight.data[:] = nn.Parameter(x.view(1, c1, 1, 1))
- self.c1 = c1
- def forward(self, x):
- """Applies a transformer layer on input tensor 'x' and returns a tensor."""
- b, c, a = x.shape # batch, channels, anchors
- return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)
- # return self.conv(x.view(b, self.c1, 4, a).softmax(1)).view(b, 4, a)
- class DWConv(Conv):
- """Depth-wise convolution."""
- def __init__(self, c1, c2, k=1, s=1, d=1, act=True): # ch_in, ch_out, kernel, stride, dilation, activation
- """Initialize Depth-wise convolution with given parameters."""
- super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), d=d, act=act)
- class DynamicHead(nn.Module):
- """YOLOv8 Detect head for detection models. CSDNSnu77"""
- dynamic = False # force grid reconstruction
- export = False # export mode
- end2end = False # end2end
- max_det = 300 # max_det
- shape = None
- anchors = torch.empty(0) # init
- strides = torch.empty(0) # init
- def __init__(self, nc=80, ch=()):
- """Initializes the YOLOv8 detection layer with specified number of classes and channels."""
- super().__init__()
- self.nc = nc # number of classes
- self.nl = len(ch) # number of detection layers
- self.reg_max = 16 # DFL channels (ch[0] // 16 to scale 4/8/12/16/20 for n/s/m/l/x)
- self.no = nc + self.reg_max * 4 # number of outputs per anchor
- self.stride = torch.zeros(self.nl) # strides computed during build
- c2, c3 = max((16, ch[0] // 4, self.reg_max * 4)), max(ch[0], min(self.nc, 100)) # channels
- self.cv2 = nn.ModuleList(
- nn.Sequential(Conv(x, c2, 3), Conv(c2, c2, 3), nn.Conv2d(c2, 4 * self.reg_max, 1)) for x in ch
- )
- self.cv3 = nn.ModuleList(
- nn.Sequential(
- nn.Sequential(DWConv(x, x, 3), Conv(x, c3, 1)),
- nn.Sequential(DWConv(c3, c3, 3), Conv(c3, c3, 1)),
- nn.Conv2d(c3, self.nc, 1),
- )
- for x in ch
- )
- self.dfl = DFL(self.reg_max) if self.reg_max > 1 else nn.Identity()
- dyhead_tower = []
- for i in range(self.nl):
- channel = ch[i]
- dyhead_tower.append(
- DyConv(
- channel,
- channel,
- conv_func=Conv3x3Norm,
- )
- )
- self.add_module('dyhead_tower', nn.Sequential(*dyhead_tower))
- if self.end2end:
- self.one2one_cv2 = copy.deepcopy(self.cv2)
- self.one2one_cv3 = copy.deepcopy(self.cv3)
- def forward(self, x):
- tensor_dict = {i: tensor for i, tensor in enumerate(x)}
- x = self.dyhead_tower(tensor_dict)
- x = list(x.values())
- """Concatenates and returns predicted bounding boxes and class probabilities."""
- if self.end2end:
- return self.forward_end2end(x)
- for i in range(self.nl):
- x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
- if self.training: # Training path
- return x
- y = self._inference(x)
- return y if self.export else (y, x)
- def forward_end2end(self, x):
- """
- Performs forward pass of the v10Detect module.
- Args:
- x (tensor): Input tensor.
- Returns:
- (dict, tensor): If not in training mode, returns a dictionary containing the outputs of both one2many and one2one detections.
- If in training mode, returns a dictionary containing the outputs of one2many and one2one detections separately.
- """
- x_detach = [xi.detach() for xi in x]
- one2one = [
- torch.cat((self.one2one_cv2[i](x_detach[i]), self.one2one_cv3[i](x_detach[i])), 1) for i in range(self.nl)
- ]
- for i in range(self.nl):
- x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
- if self.training: # Training path
- return {"one2many": x, "one2one": one2one}
- y = self._inference(one2one)
- y = self.postprocess(y.permute(0, 2, 1), self.max_det, self.nc)
- return y if self.export else (y, {"one2many": x, "one2one": one2one})
- def _inference(self, x):
- """Decode predicted bounding boxes and class probabilities based on multiple-level feature maps."""
- # Inference path
- shape = x[0].shape # BCHW
- x_cat = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2)
- if self.dynamic or self.shape != shape:
- self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
- self.shape = shape
- if self.export and self.format in {"saved_model", "pb", "tflite", "edgetpu", "tfjs"}: # avoid TF FlexSplitV ops
- box = x_cat[:, : self.reg_max * 4]
- cls = x_cat[:, self.reg_max * 4 :]
- else:
- box, cls = x_cat.split((self.reg_max * 4, self.nc), 1)
- if self.export and self.format in {"tflite", "edgetpu"}:
- # Precompute normalization factor to increase numerical stability
- # See https://github.com/ultralytics/ultralytics/issues/7371
- grid_h = shape[2]
- grid_w = shape[3]
- grid_size = torch.tensor([grid_w, grid_h, grid_w, grid_h], device=box.device).reshape(1, 4, 1)
- norm = self.strides / (self.stride[0] * grid_size)
- dbox = self.decode_bboxes(self.dfl(box) * norm, self.anchors.unsqueeze(0) * norm[:, :2])
- else:
- dbox = self.decode_bboxes(self.dfl(box), self.anchors.unsqueeze(0)) * self.strides
- return torch.cat((dbox, cls.sigmoid()), 1)
- def bias_init(self):
- """Initialize Detect() biases, WARNING: requires stride availability."""
- m = self # self.model[-1] # Detect() module
- # cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1
- # ncf = math.log(0.6 / (m.nc - 0.999999)) if cf is None else torch.log(cf / cf.sum()) # nominal class frequency
- for a, b, s in zip(m.cv2, m.cv3, m.stride): # from
- a[-1].bias.data[:] = 1.0 # box
- b[-1].bias.data[: m.nc] = math.log(5 / m.nc / (640 / s) ** 2) # cls (.01 objects, 80 classes, 640 img)
- if self.end2end:
- for a, b, s in zip(m.one2one_cv2, m.one2one_cv3, m.stride): # from
- a[-1].bias.data[:] = 1.0 # box
- b[-1].bias.data[: m.nc] = math.log(5 / m.nc / (640 / s) ** 2) # cls (.01 objects, 80 classes, 640 img)
- def decode_bboxes(self, bboxes, anchors):
- """Decode bounding boxes."""
- return dist2bbox(bboxes, anchors, xywh=not self.end2end, dim=1)
- @staticmethod
- def postprocess(preds: torch.Tensor, max_det: int, nc: int = 80):
- """
- Post-processes YOLO model predictions.
- Args:
- preds (torch.Tensor): Raw predictions with shape (batch_size, num_anchors, 4 + nc) with last dimension
- format [x, y, w, h, class_probs].
- max_det (int): Maximum detections per image.
- nc (int, optional): Number of classes. Default: 80.
- Returns:
- (torch.Tensor): Processed predictions with shape (batch_size, min(max_det, num_anchors), 6) and last
- dimension format [x, y, w, h, max_class_prob, class_index].
- """
- batch_size, anchors, _ = preds.shape # i.e. shape(16,8400,84)
- boxes, scores = preds.split([4, nc], dim=-1)
- index = scores.amax(dim=-1).topk(min(max_det, anchors))[1].unsqueeze(-1)
- boxes = boxes.gather(dim=1, index=index.repeat(1, 1, 4))
- scores = scores.gather(dim=1, index=index.repeat(1, 1, nc))
- scores, index = scores.flatten(1).topk(min(max_det, anchors))
- i = torch.arange(batch_size)[..., None] # batch indices
- return torch.cat([boxes[i, index // nc], scores[..., None], (index % nc)[..., None].float()], dim=-1)
- if __name__ == "__main__":
- # Generating Sample image CSDN Snu77
- image1 = (1, 64, 32, 32)
- image2 = (1, 64, 16, 16)
- image3 = (1, 64, 8, 8)
- image1 = torch.rand(image1)
- image2 = torch.rand(image2)
- image3 = torch.rand(image3)
- image = [image1, image2, image3]
- channel = (64, 64, 64)
- # Model
- mobilenet_v1 = DynamicHead(nc=80, ch=channel) # 尺度统一检测头.
- out = mobilenet_v1(image)
- print(out)
四、手把手教你添加DynamicHead检测头
4.1 修改一
首先我们将上面的代码复制粘贴到' ultralytics /nn' 目录下新建一个py文件复制粘贴进去,具体名字自己来定,我这里起名为DynamicHead.py。
4.2 修改二
第二步我们在该目录下创建一个新的py文件名字为'__init__.py'( 用群内的文件的话已经有了无需新建) ,然后在其内部导入我们的检测头如下图所示。
4.3 修改三
第三步我门中到如下文件'ultralytics/nn/tasks.py'进行导入和注册我们的模块( 用群内的文件的话已经有了无需重新导入直接开始第四步即可) !
4.4 修改四
第四步我门找到如下文件'ultralytics/nn/tasks.py,找到如下的代码进行将检测头添加进去,这里给大家推荐个快速搜索的方法用ctrl+f然后搜索Detect然后就能快速查找了。
4.5 修改五
同理
4.6 修改六
这里有一些不一样,我们需要加一行代码
- else:
- return 'detect'
为啥呢不一样,因为这里的m在代码执行过程中会将你的代码自动转换为小写,所以直接else方便一点,以后出现一些其它分割或者其它的教程的时候在提供其它的修改教程。
4.7 修改七
同理.
到此就修改完成了,大家可以复制下面的yaml文件运行。
五、DynamicHead检测头的yaml文件
5.1 DynamicHead和P2融合yaml文件
此版本训练信息:YOLO11-P2-DynamicHead summary: 485 layers, 2,515,396 parameters, 2,515,380 gradients, 16.0 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
- # YOLOv11.0-p2 head
- head:
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 6], 1, Concat, [1]] # cat backbone P4
- - [-1, 2, C3k2, [256, 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, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 2], 1, Concat, [1]] # cat backbone P2
- - [-1, 2, C3k2, [256, False]] # 19 (P2/4-xsmall) # 小目标可以尝试将这里的False设置为True.
- - [-1, 1, Conv, [128, 3, 2]]
- - [[-1, 16], 1, Concat, [1]] # cat head P3
- - [-1, 2, C3k2, [256, False]] # 22 (P3/8-small)
- - [-1, 1, Conv, [256, 3, 2]]
- - [[-1, 13], 1, Concat, [1]] # cat head P4
- - [-1, 2, C3k2, [256, False]] # 25 (P4/16-medium)
- - [-1, 1, Conv, [256, 3, 2]]
- - [[-1, 10], 1, Concat, [1]] # cat head P5
- - [-1, 2, C3k2, [256, True]] # 28 (P5/32-large)
- - [[19, 22, 25, 28], 1, DynamicHead, [nc]] # Detect(P2, P3, P4, P5)
5.2 DynamicHead和P6融合yaml文件
此版本训练信息:YOLO11-P6-DynamicHead summary: 491 layers, 2,725,596 parameters, 2,725,580 gradients, 5.0 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, [128, False, 0.25]]
- - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- - [-1, 2, C3k2, [256, False, 0.25]]
- - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- - [-1, 2, C3k2, [512, False, 0.25]]
- - [-1, 1, Conv, [768, 3, 2]] # 7-P5/32
- - [-1, 2, C3k2, [768, True]]
- - [-1, 1, Conv, [1024, 3, 2]] # 9-P6/64
- - [-1, 2, C3k2, [1024, True]]
- - [-1, 1, SPPF, [1024, 5]] # 11
- # YOLOv11.0x6 head
- head:
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 8], 1, Concat, [1]] # cat backbone P5
- - [-1, 2, C3k2, [256, False]] # 14
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 6], 1, Concat, [1]] # cat backbone P4
- - [-1, 2, C3k2, [256, False]] # 17
- - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- - [[-1, 4], 1, Concat, [1]] # cat backbone P3
- - [-1, 2, C3k2, [256, False]] # 20 (P3/8-small)
- - [-1, 1, Conv, [256, 3, 2]]
- - [[-1, 17], 1, Concat, [1]] # cat head P4
- - [-1, 2, C3k2, [256, False]] # 23 (P4/16-medium)
- - [-1, 1, Conv, [512, 3, 2]]
- - [[-1, 14], 1, Concat, [1]] # cat head P5
- - [-1, 2, C3k2, [256, True]] # 26 (P5/32-large)
- - [-1, 1, Conv, [256, 3, 2]]
- - [[-1, 11], 1, Concat, [1]] # cat head P6
- - [-1, 2, C3k2, [256, True]] # 29 (P6/64-xlarge) # True也可设置False尝试.
- - [[20, 23, 26, 29], 1, DynamicHead, [nc]] # Detect(P3, P4, P5, P6)
六、完美运行记录
最后提供一下完美运行的图片。
七、本文总结
到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~