yolov26改进 | 检测头篇 | 辅助特征融合模块ASFF改进yolov26检测头(适配YOLOv26版本,全网独家创新)
开始正文前先向大家推荐我的YOLO专栏系列。本人持续更新 YOLOv8、YOLO11、YOLO26 等热门模型内容覆盖图像分类、目标检测、实例分割、多目标跟踪、姿态估计与关键点检测重点讲解 小目标检测、注意力机制、特征融合、损失函数改进、自定义数据集训练、消融实验及论文代码复现。同时分享如何使用 OpenAI Codex辅助撰写论文、配置实验环境、调试项目、改进模型和分析实验结果。 专栏目前正在进行限时优惠每周更新 5–7篇最新论文机制、YOLO改进方法和实战教程。订阅后可获得包含本人全部改进方案的代码与配置文件并加入专属技术交流群。我也会定期在群内分享 YOLO论文选题、创新点设计、实验方案、论文写作与投稿发表经验欢迎大家订阅交流一、本文介绍本文给大家带来的最新改进机制是基于自适应空间特征融合模块ASFF设计的新型检测头——ASFFHead本文将其集成到YOLOv26中用于改进原有的多尺度检测头。ASFF的核心思想是对不同尺度的特征图进行尺寸和通道对齐并根据各个空间位置自适应学习不同层级特征的融合权重从而减少浅层细节特征与深层语义特征之间的冲突使网络能够针对不同尺寸和不同位置的目标选择更加合适的特征信息进一步增强模型的尺度不变性和多尺度目标检测能力。经过本人实验验证修改后的Detect_ASFF检测头在大、中、小不同尺度目标上均取得了较为明显的精度提升尤其适合目标尺度变化较大、复杂背景以及小目标较多的检测任务。本文提供的是标准三检测头版本后续还会在该结构基础上继续进行二次创新增加更高分辨率的小目标检测层构建四检测头版本的Detect_ASFF进一步强化模型对微小目标和密集目标的感知能力。本文将详细介绍ASFF的特征对齐、自适应权重学习和多尺度融合原理并结合完整代码与配置文件手把手讲解如何完成模块注册、检测头替换和模型训练。本文代码及其在YOLOv26中的结构适配方案为本人独立修改与创新非常推荐大家在自己的数据集上进行实验和验证。专栏链接YOLOv26有效涨点专栏包含Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制目录一、本文介绍二、ASFF的基本框架原理三、ASFFHead的核心代码四、手把手教你添加ASFFHead检测头4.1 修改一4.2 修改二4.3 修改三4.4 修改四4.5 修改五4.6 修改六4.7 修改七4.8 修改八4.9 修改九五、ASFFHead检测头的yaml文件5.1 ASFFHead的yaml文件六、完美运行记录七、本文总结二、ASFF的基本框架原理​官方论文地址官方论文地址点击即可跳转官方代码地址官方代码地址点击即可跳转​ASFF自适应空间特征融合方法针对单次对象检测任务提出解决了不同特征尺度间的一致性问题。其主要创新是引入了一种自适应的空间特征融合方式有效地过滤掉冲突信息从而增强了尺度不变性。研究表明将ASFF应用于YOLOv3可以显著提高在MS COCO数据集上的检测性能实现了速度与准确性的平衡。ASFF方法可以通过反向传播进行训练与模型无关并且引入的计算开销很小使其成为现有对象检测框架的一种实用增强。ASFF的创新点主要包括1. 自适应空间特征融合提出了一种新的金字塔特征融合策略能够空间过滤冲突信息压制不同尺度特征间的不一致性。2. 改善尺度不变性通过ASFF策略显著提升了特征的尺度不变性有助于提高对象检测的准确性。3. 低推理开销在提升检测性能的同时几乎不增加额外的推理开销。这些创新使ASFF成为单次对象检测领域的一个重要进展特别是对处理不同尺度对象的能力的提升所以将其对于一些单一尺度检测的Neck适合是不适用的大家需要注意这一点。​这张图片展示了自适应空间特征融合ASFF机制的工作原理它是用于单次对象检测的。在这种结构中不同层级的特征表示为不同颜色的层首先通过各自的步幅stride进行下采样或上采样以便所有特征具有相同的空间维度。- Level 1、Level 2和Level 3指的是特征金字塔中不同层级的特征每个层级都有不同的空间分辨率。- ASFF-1、ASFF-2和ASFF-3表示应用了ASFF机制的不同层级的特征融合。- 在ASFF-3的放大部分我们可以看到来自其他层级的特征x1→3、x2→3被调整到与第三层x3→3相同的尺寸然后它们通过学习到的权重图进行加权融合生成最终用于预测的融合特征​。通过这种方式ASFF能够在每个空间位置自适应地选择最有用的特征以提高检测的准确性。这种方法允许模型根据每个特定位置和尺度的上下文灵活地决定哪些特征层级对最终预测最为重要。三、ASFFHead的核心代码现在是三头的检测版本后期我会出四头的增加小目标检测层的版本给大家其使用方式看章节四。import torch.nn as nn import torch import math import copy from ultralytics.utils.torch_utils import TORCH_1_11 import torch.nn.functional as F class ASFFV5(nn.Module): def __init__(self, level, ch, multiplier1, rfbFalse, visFalse, act_cfgTrue): CSDN:Snu77 super(ASFFV5, self).__init__() self.level level self.dim [int(ch[2] * multiplier), int(ch[1] * multiplier), int(ch[0] * multiplier)] # print(self.dim) self.inter_dim self.dim[self.level] if level 0: self.stride_level_1 Conv(int(ch[1] * multiplier), self.inter_dim, 3, 2) self.stride_level_2 Conv(int(ch[0] * multiplier), self.inter_dim, 3, 2) self.expand Conv(self.inter_dim, int( ch[2] * multiplier), 3, 1) elif level 1: self.compress_level_0 Conv( int(ch[2] * multiplier), self.inter_dim, 1, 1) self.stride_level_2 Conv( int(ch[0] * multiplier), self.inter_dim, 3, 2) self.expand Conv(self.inter_dim, int(ch[1] * multiplier), 3, 1) elif level 2: self.compress_level_0 Conv( int(ch[2] * multiplier), self.inter_dim, 1, 1) self.compress_level_1 Conv( int(ch[1] * multiplier), self.inter_dim, 1, 1) self.expand Conv(self.inter_dim, int( ch[0] * multiplier), 3, 1) # when adding rfb, we use half number of channels to save memory compress_c 8 if rfb else 16 self.weight_level_0 Conv( self.inter_dim, compress_c, 1, 1) self.weight_level_1 Conv( self.inter_dim, compress_c, 1, 1) self.weight_level_2 Conv( self.inter_dim, compress_c, 1, 1) self.weight_levels Conv( compress_c * 3, 3, 1, 1) self.vis vis def forward(self, x): # l,m,s # 128, 256, 512 512, 256, 128 from small - large x_level_0 x[2] # l x_level_1 x[1] # m x_level_2 x[0] # s # print(x_level_0: , x_level_0.shape) # print(x_level_1: , x_level_1.shape) # print(x_level_2: , x_level_2.shape) if self.level 0: level_0_resized x_level_0 level_1_resized self.stride_level_1(x_level_1) level_2_downsampled_inter F.max_pool2d( x_level_2, 3, stride2, padding1) level_2_resized self.stride_level_2(level_2_downsampled_inter) elif self.level 1: level_0_compressed self.compress_level_0(x_level_0) level_0_resized F.interpolate( level_0_compressed, scale_factor2, modenearest) level_1_resized x_level_1 level_2_resized self.stride_level_2(x_level_2) elif self.level 2: level_0_compressed self.compress_level_0(x_level_0) level_0_resized F.interpolate( level_0_compressed, scale_factor4, modenearest) x_level_1_compressed self.compress_level_1(x_level_1) level_1_resized F.interpolate( x_level_1_compressed, scale_factor2, modenearest) level_2_resized x_level_2 # print(level: {}, l1_resized: {}, l2_resized: {}.format(self.level, # level_1_resized.shape, level_2_resized.shape)) level_0_weight_v self.weight_level_0(level_0_resized) level_1_weight_v self.weight_level_1(level_1_resized) level_2_weight_v self.weight_level_2(level_2_resized) # print(level_0_weight_v: , level_0_weight_v.shape) # print(level_1_weight_v: , level_1_weight_v.shape) # print(level_2_weight_v: , level_2_weight_v.shape) levels_weight_v torch.cat( (level_0_weight_v, level_1_weight_v, level_2_weight_v), 1) levels_weight self.weight_levels(levels_weight_v) levels_weight F.softmax(levels_weight, dim1) fused_out_reduced level_0_resized * levels_weight[:, 0:1, :, :] \ level_1_resized * levels_weight[:, 1:2, :, :] \ level_2_resized * levels_weight[:, 2:, :, :] out self.expand(fused_out_reduced) if self.vis: return out, levels_weight, fused_out_reduced.sum(dim1) else: return out def _make_divisible(v, divisor, min_valueNone): 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, inplaceFalse): super(h_swish, self).__init__() self.inplace inplace def forward(self, x): return x * F.relu6(x 3.0, inplaceself.inplace) / 6.0 class h_sigmoid(nn.Module): def __init__(self, inplaceTrue, h_max1): super(h_sigmoid, self).__init__() self.relu nn.ReLU6(inplaceinplace) self.h_max h_max def forward(self, x): return self.relu(x 3) * self.h_max / 6 def make_anchors(feats, strides, grid_cell_offset0.5): Generate anchors from features. anchor_points, stride_tensor [], [] assert feats is not None dtype, device feats[0].dtype, feats[0].device for i in range(len(feats)): # use len(feats) to avoid TracerWarning from iterating over strides tensor stride strides[i] h, w feats[i].shape[2:] if isinstance(feats, list) else (int(feats[i][0]), int(feats[i][1])) sx torch.arange(endw, devicedevice, dtypedtype) grid_cell_offset # shift x sy torch.arange(endh, devicedevice, dtypedtype) grid_cell_offset # shift y sy, sx torch.meshgrid(sy, sx, indexingij) if TORCH_1_11 else torch.meshgrid(sy, sx) anchor_points.append(torch.stack((sx, sy), -1).view(-1, 2)) stride_tensor.append(torch.full((h * w, 1), stride, dtypedtype, devicedevice)) return torch.cat(anchor_points), torch.cat(stride_tensor) def dist2bbox(distance, anchor_points, xywhTrue, dim-1): Transform distance(ltrb) to box(xywh or xyxy). lt, rb distance.chunk(2, dim) x1y1 anchor_points - lt x2y2 anchor_points rb if xywh: c_xy (x1y1 x2y2) / 2 wh x2y2 - x1y1 return torch.cat([c_xy, wh], dim) # xywh bbox return torch.cat((x1y1, x2y2), dim) # xyxy bbox def autopad(k, pNone, d1): # 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 module with batch normalization and activation. Attributes: conv (nn.Conv2d): Convolutional layer. bn (nn.BatchNorm2d): Batch normalization layer. act (nn.Module): Activation function layer. default_act (nn.Module): Default activation function (SiLU). default_act nn.SiLU() # default activation def __init__(self, c1, c2, k1, s1, pNone, g1, d1, actTrue): Initialize Conv layer with given parameters. Args: c1 (int): Number of input channels. c2 (int): Number of output channels. k (int): Kernel size. s (int): Stride. p (int, optional): Padding. g (int): Groups. d (int): Dilation. act (bool | nn.Module): Activation function. super().__init__() self.conv nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groupsg, dilationd, biasFalse) 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. Args: x (torch.Tensor): Input tensor. Returns: (torch.Tensor): Output tensor. return self.act(self.bn(self.conv(x))) def forward_fuse(self, x): Apply convolution and activation without batch normalization. Args: x (torch.Tensor): Input tensor. Returns: (torch.Tensor): Output tensor. return self.act(self.conv(x)) class DWConv(Conv): Depth-wise convolution module. def __init__(self, c1, c2, k1, s1, d1, actTrue): Initialize depth-wise convolution with given parameters. Args: c1 (int): Number of input channels. c2 (int): Number of output channels. k (int): Kernel size. s (int): Stride. d (int): Dilation. act (bool | nn.Module): Activation function. super().__init__(c1, c2, k, s, gmath.gcd(c1, c2), dd, actact) 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: int 16): Initialize a convolutional layer with a given number of input channels. Args: c1 (int): Number of input channels. super().__init__() self.conv nn.Conv2d(c1, 1, 1, biasFalse).requires_grad_(False) x torch.arange(c1, dtypetorch.float) self.conv.weight.data[:] nn.Parameter(x.view(1, c1, 1, 1)) self.c1 c1 def forward(self, x: torch.Tensor) - torch.Tensor: Apply the DFL module to input tensor and return transformed output. b, _, 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 ASFFHead(nn.Module): YOLO Detect head for object detection models. This class implements the detection head used in YOLO models for predicting bounding boxes and class probabilities. It supports both training and inference modes, with optional end-to-end detection capabilities. Attributes: dynamic (bool): Force grid reconstruction. export (bool): Export mode flag. format (str): Export format. end2end (bool): End-to-end detection mode. max_det (int): Maximum detections per image. shape (tuple): Input shape. anchors (torch.Tensor): Anchor points. strides (torch.Tensor): Feature map strides. legacy (bool): Backward compatibility for v3/v5/v8/v9/v11 models. xyxy (bool): Output format, xyxy or xywh. nc (int): Number of classes. nl (int): Number of detection layers. reg_max (int): DFL channels. no (int): Number of outputs per anchor. stride (torch.Tensor): Strides computed during build. cv2 (nn.ModuleList): Convolution layers for box regression. cv3 (nn.ModuleList): Convolution layers for classification. dfl (nn.Module): Distribution Focal Loss layer. one2one_cv2 (nn.ModuleList): One-to-one convolution layers for box regression. one2one_cv3 (nn.ModuleList): One-to-one convolution layers for classification. Methods: forward: Perform forward pass and return predictions. bias_init: Initialize detection head biases. decode_bboxes: Decode bounding boxes from predictions. postprocess: Post-process model predictions. Examples: Create a detection head for 80 classes detect Detect(nc80, ch(256, 512, 1024)) x [torch.randn(1, 256, 80, 80), torch.randn(1, 512, 40, 40), torch.randn(1, 1024, 20, 20)] outputs detect(x) dynamic False # force grid reconstruction export False # export mode format None # export format max_det 300 # max_det agnostic_nms False shape None anchors torch.empty(0) # init strides torch.empty(0) # init legacy False # backward compatibility for v3/v5/v8/v9 models xyxy False # xyxy or xywh output def __init__(self, nc: int 80, reg_max16, end2endFalse, ch: tuple (), multiplier1, rfbFalse,): Initialize the YOLO detection layer with specified number of classes and channels. Args: nc (int): Number of classes. reg_max (int): Maximum number of DFL channels. end2end (bool): Whether to use end-to-end NMS-free detection. ch (tuple): Tuple of channel sizes from backbone feature maps. super().__init__() self.nc nc # number of classes self.nl len(ch) # number of detection layers self.reg_max reg_max # 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(Conv(x, c3, 3), Conv(c3, c3, 3), nn.Conv2d(c3, self.nc, 1)) for x in ch) if self.legacy else 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() self.l0_fusion ASFFV5(level0, chch, multipliermultiplier, rfbrfb) self.l1_fusion ASFFV5(level1, chch, multipliermultiplier, rfbrfb) self.l2_fusion ASFFV5(level2, chch, multipliermultiplier, rfbrfb) if end2end: self.one2one_cv2 copy.deepcopy(self.cv2) self.one2one_cv3 copy.deepcopy(self.cv3) property def one2many(self): Returns the one-to-many head components, here for v3/v5/v8/v9/v11 backward compatibility. return dict(box_headself.cv2, cls_headself.cv3) property def one2one(self): Returns the one-to-one head components. return dict(box_headself.one2one_cv2, cls_headself.one2one_cv3) property def end2end(self): Checks if the model has one2one for v3/v5/v8/v9/v11 backward compatibility. return getattr(self, _end2end, True) and hasattr(self, one2one) end2end.setter def end2end(self, value): Override the end-to-end detection mode. self._end2end value def forward_head( self, x: list[torch.Tensor], box_head: torch.nn.Module None, cls_head: torch.nn.Module None ) - dict[str, torch.Tensor]: x1 self.l0_fusion(x) x2 self.l1_fusion(x) x3 self.l2_fusion(x) x [x3, x2, x1] Concatenates and returns predicted bounding boxes and class probabilities. if box_head is None or cls_head is None: # for fused inference return dict() bs x[0].shape[0] # batch size boxes torch.cat([box_head[i](x[i]).view(bs, 4 * self.reg_max, -1) for i in range(self.nl)], dim-1) scores torch.cat([cls_head[i](x[i]).view(bs, self.nc, -1) for i in range(self.nl)], dim-1) return dict(boxesboxes, scoresscores, featsx) def forward( self, x: list[torch.Tensor] ) - dict[str, torch.Tensor] | torch.Tensor | tuple[torch.Tensor, dict[str, torch.Tensor]]: Concatenates and returns predicted bounding boxes and class probabilities. x1 self.l0_fusion(x) x2 self.l1_fusion(x) x3 self.l2_fusion(x) x [x3, x2, x1] preds self.forward_head(x, **self.one2many) if self.end2end: x_detach [xi.detach() for xi in x] one2one self.forward_head(x_detach, **self.one2one) preds {one2many: preds, one2one: one2one} if self.training: return preds y self._inference(preds[one2one] if self.end2end else preds) if self.end2end: y self.postprocess(y.permute(0, 2, 1)) return y if self.export else (y, preds) def _inference(self, x: dict[str, torch.Tensor]) - torch.Tensor: Decode predicted bounding boxes and class probabilities based on multiple-level feature maps. Args: x (dict[str, torch.Tensor]): Dictionary of predictions from detection layers. Returns: (torch.Tensor): Concatenated tensor of decoded bounding boxes and class probabilities. # Inference path dbox self._get_decode_boxes(x) return torch.cat((dbox, x[scores].sigmoid()), 1) def _get_decode_boxes(self, x: dict[str, torch.Tensor]) - torch.Tensor: Get decoded boxes based on anchors and strides. shape x[feats][0].shape # BCHW if self.dynamic or self.shape ! shape: self.anchors, self.strides (a.transpose(0, 1) for a in make_anchors(x[feats], self.stride, 0.5)) self.shape shape dbox self.decode_bboxes(self.dfl(x[boxes]), self.anchors.unsqueeze(0)) * self.strides return dbox def bias_init(self): Initialize Detect() biases, WARNING: requires stride availability. for i, (a, b) in enumerate(zip(self.one2many[box_head], self.one2many[cls_head])): # from a[-1].bias.data[:] 2.0 # box b[-1].bias.data[: self.nc] math.log( 5 / self.nc / (640 / self.stride[i]) ** 2 ) # cls (.01 objects, 80 classes, 640 img) if self.end2end: for i, (a, b) in enumerate(zip(self.one2one[box_head], self.one2one[cls_head])): # from a[-1].bias.data[:] 2.0 # box b[-1].bias.data[: self.nc] math.log( 5 / self.nc / (640 / self.stride[i]) ** 2 ) # cls (.01 objects, 80 classes, 640 img) def decode_bboxes(self, bboxes: torch.Tensor, anchors: torch.Tensor, xywh: bool True) - torch.Tensor: Decode bounding boxes from predictions. return dist2bbox( bboxes, anchors, xywhxywh and not self.end2end and not self.xyxy, dim1, ) def postprocess(self, preds: torch.Tensor) - torch.Tensor: Post-processes YOLO model predictions. Args: preds (torch.Tensor): Raw predictions with shape (batch_size, num_anchors, 4 nc) with last dimension format [x1, y1, x2, y2, class_probs]. Returns: (torch.Tensor): Processed predictions with shape (batch_size, min(max_det, num_anchors), 6) and last dimension format [x1, y1, x2, y2, max_class_prob, class_index]. boxes, scores preds.split([4, self.nc], dim-1) scores, conf, idx self.get_topk_index(scores, self.max_det) boxes boxes.gather(dim1, indexidx.repeat(1, 1, 4)) return torch.cat([boxes, scores, conf], dim-1) def get_topk_index(self, scores: torch.Tensor, max_det: int) - tuple[torch.Tensor, torch.Tensor, torch.Tensor]: Get top-k indices from scores. Args: scores (torch.Tensor): Scores tensor with shape (batch_size, num_anchors, num_classes). max_det (int): Maximum detections per image. Returns: (torch.Tensor, torch.Tensor, torch.Tensor): Top scores, class indices, and filtered indices. batch_size, anchors, nc scores.shape # i.e. shape(16,8400,84) # Use max_det directly during export for TensorRT compatibility (requires k to be constant), # otherwise use min(max_det, anchors) for safety with small inputs during Python inference k max_det if self.export else min(max_det, anchors) if self.agnostic_nms: scores, labels scores.max(dim-1, keepdimTrue) scores, indices scores.topk(k, dim1) labels labels.gather(1, indices) return scores, labels, indices ori_index scores.max(dim-1)[0].topk(k)[1].unsqueeze(-1) scores scores.gather(dim1, indexori_index.repeat(1, 1, nc)) scores, index scores.flatten(1).topk(k) idx ori_index[torch.arange(batch_size)[..., None], index // nc] # original index return scores[..., None], (index % nc)[..., None].float(), idx def fuse(self) - None: Remove the one2many head for inference optimization. self.cv2 self.cv3 None四、手把手教你添加ASFFHead检测头4.1 修改一首先我们将上面的代码复制粘贴到ultralytics/nn 目录下新建一个py文件复制粘贴进去具体名字自己来定我这里起名为ASFFHead。​4.2 修改二第二步我们在该目录下创建一个新的py文件名字为__init__.py(用群内的文件的话已经有了无需新建)然后在其内部导入我们的检测头如下图所示。​​​4.3 修改三第三步我门中到如下文件ultralytics/nn/tasks.py进行导入和注册我们的模块(用群内的文件的话已经有了无需重新导入直接开始第四步即可)​​​4.4 修改四第四步我门找到如下文件ultralytics/nn/tasks.py找到如下的代码进行将检测头添加进去这里给大家推荐个快速搜索的方法用ctrlf然后搜索Detect然后就能快速查找了。​​​​4.5 修改五第四同理注意有些括号是后添加的自己判别下不会找博主获取视频教程。4.6 修改六同理​​​​4.7 修改七同理4.8 修改八这里有一些不一样我们需要加一行代码else: return detect为啥呢不一样因为这里的m在代码执行过程中会将你的代码自动转换为小写所以直接else方便一点以后出现一些其它分割或者其它的教程的时候在提供其它的修改教程。​​​​4.9 修改九同理.​​​​到此就修改完成了大家可以复制下面的yaml文件运行注意上面添加的步骤可能某一步你没修改对但是模型可以成功运行会出现模型精度为0或者无法收敛的情况。五、ASFFHead检测头的yaml文件5.1 ASFFHead的yaml文件此版本训练信息YOLO26-Head-ASFFHead summary: 303 layers, 3,878,654 parameters, 3,878,654 gradients, 12.3 GFLOPs# Ultralytics AGPL-3.0 License - https://ultralytics.com/license # Ultralytics YOLO26 object detection model with P3/8 - P5/32 outputs # Model docs: https://docs.ultralytics.com/models/yolo26 # Task docs: https://docs.ultralytics.com/tasks/detect # Parameters nc: 80 # number of classes end2end: True # whether to use end-to-end mode reg_max: 1 # DFL bins scales: # model compound scaling constants, i.e. modelyolo26n.yaml will call yolo26.yaml with scale n # [depth, width, max_channels] n: [0.50, 0.25, 1024] # summary: 260 layers, 2,572,280 parameters, 2,572,280 gradients, 6.1 GFLOPs s: [0.50, 0.50, 1024] # summary: 260 layers, 10,009,784 parameters, 10,009,784 gradients, 22.8 GFLOPs m: [0.50, 1.00, 512] # summary: 280 layers, 21,896,248 parameters, 21,896,248 gradients, 75.4 GFLOPs l: [1.00, 1.00, 512] # summary: 392 layers, 26,299,704 parameters, 26,299,704 gradients, 93.8 GFLOPs x: [1.00, 1.50, 512] # summary: 392 layers, 58,993,368 parameters, 58,993,368 gradients, 209.5 GFLOPs # YOLO26n 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, 3, True]] # 9 - [-1, 2, C2PSA, [1024]] # 10 # YOLO26n head head: - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 6], 1, Concat, [1]] # cat backbone P4 - [-1, 2, C3k2, [512, True]] # 13 - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 4], 1, Concat, [1]] # cat backbone P3 - [-1, 2, C3k2, [256, True]] # 16 (P3/8-small) - [-1, 1, Conv, [256, 3, 2]] - [[-1, 13], 1, Concat, [1]] # cat head P4 - [-1, 2, C3k2, [512, True]] # 19 (P4/16-medium) - [-1, 1, Conv, [512, 3, 2]] - [[-1, 10], 1, Concat, [1]] # cat head P5 - [-1, 1, C3k2, [1024, True, 0.5, True]] # 22 (P5/32-large) - [[16, 19, 22], 1, ASFFHead, [nc]] # Detect(P3, P4, P5)六、完美运行记录最后提供一下完美运行的图片。​​​​七、本文总结到此本文的正式分享内容就结束了在这里给大家推荐我的YOLOv26改进有效涨点专栏本专栏目前为新开的平均质量分98分后期我会根据各种最新的前沿顶会进行论文复现也会对一些老的改进机制进行补充如果大家觉得本文帮助到你了订阅本专栏关注后续更多的更新~专栏链接YOLOv26有效涨点专栏包含Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制​​