三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

yolov26改进 | 检测头篇 | 辅助特征融合检测头FASFFHead添加小目标检测头 (让小目标无所遁形、全网独家创新)

yolov26改进 | 检测头篇 | 辅助特征融合检测头FASFFHead添加小目标检测头 (让小目标无所遁形、全网独家创新)

开始正文前,先向大家推荐我的YOLO专栏系列。本人持续更新 YOLOv8、YOLO11、YOLO26 等热门模型,内容覆盖图像分类、目标检测、实例分割、多目标跟踪、姿态估计与关键点检测,重点讲解 小目标检测、注意力机制、特征融合、损失函数改进、自定义数据集训练、消融实验及论文代码复现。同时分享如何使用 OpenAI Codex辅助撰写论文、配置实验环境、调试项目、改进模型和分析实验结果。 专栏目前正在进行限时优惠,每周更新 5–7篇最新论文机制、YOLO改进方法和实战教程。订阅后可获得包含本人全部改进方案的代码与配置文件,并加入专属技术交流群。我也会定期在群内分享 YOLO论文选题、创新点设计、实验方案、论文写作与投稿发表经验,欢迎大家订阅交流!


一、本文介绍

本文给大家带来的最新改进机制是本人基于ASFFHead二次创新设计的四尺度检测头——FASFFHead,并将其完整集成到YOLOv26中。传统ASFFHead通常面向三层特征进行自适应空间融合,虽然能够根据不同位置动态分配各尺度特征的融合权重,但在增加第四个检测尺度后,直接进行跨层融合容易受到分辨率差异和语义差距影响,造成细节信息丢失或深层语义利用不足。针对这一问题,FASFFHead重新设计了四尺度特征对齐、渐进融合和权重分配方式,使不同层级特征在完成尺寸与通道统一后,再进行更充分的自适应融合,从而降低跨尺度传递过程中的特征损失。本文提供小目标版和大目标版两种设计思路:小目标版增加高分辨率检测层,进一步保留浅层纹理、边缘和位置信息,强化对微小目标、密集目标及远距离目标的二次提取;大目标版增加更深层检测分支,扩大有效感受野,增强对大型目标和完整结构信息的建模能力。该结构来源于专栏读者对四头ASFF检测头的实际需求,具有较强的任务针对性、扩展能力和论文创新空间。本文将详细介绍FASFFHead的四尺度输入、特征对齐、自适应融合及检测分支设计,并提供完整代码、配置文件、模块注册方法和Codex辅助修改思路。

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

专栏链接:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制


目录

一、本文介绍

二、原理介绍

三、FASFFHead的核心代码

四、手把手教你添加FASFFHead卷积

4.1 修改一

4.2 修改二

4.3 修改三

4.4 修改四

4.5 修改五

4.6 修改六

4.7 修改七

4.8 修改八

4.9 修改九

五、FASFFHead检测头的yaml文件

六、完美运行记录

七、本文总结


二、原理介绍

官方论文地址:官方论文地址点击即可跳转

官方代码地址:官方代码地址点击即可跳转


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能够在每个空间位置自适应地选择最有用的特征,以提高检测的准确性。这种方法允许模型根据每个特定位置和尺度的上下文,灵活地决定哪些特征层级对最终预测最为重要。


三、FASFFHead的核心代码

使用方法看章节四!

import copy import math from ultralytics.nn.modules import DFL import torch from torch import nn from ultralytics.utils.tal import dist2bbox, make_anchors import torch.nn.functional as F __all__ = ['FASFFHead'] 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 DWConv(Conv): """Depth-wise convolution module.""" def __init__(self, c1, c2, k=1, s=1, d=1, act=True): """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, g=math.gcd(c1, c2), d=d, act=act) 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 FASFF(nn.Module): def __init__(self, level, ch, multiplier=1, rfb=False, vis=False): super(FASFF, self).__init__() self.level = level self.dim = [int(ch[3] * multiplier), 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[2] * multiplier), self.inter_dim, 3, 2) self.stride_level_2 = Conv(int(ch[1] * multiplier), self.inter_dim, 3, 2) self.expand = Conv(self.inter_dim, int( ch[3] * multiplier), 3, 1) elif level == 1: self.compress_level_0 = Conv( int(ch[3] * multiplier), self.inter_dim, 1, 1) self.stride_level_2 = Conv( int(ch[1] * multiplier), self.inter_dim, 3, 2) self.expand = Conv(self.inter_dim, int(ch[2] * multiplier), 3, 1) elif level == 2: 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 == 3: 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_add = x[2] x_level_0 = x[3] # 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_add) level_2_downsampled_inter = F.max_pool2d( x_level_1, 3, stride=2, padding=1) 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_factor=2, mode='nearest') level_1_resized = x_level_add level_2_resized = self.stride_level_2(x_level_1) elif self.level == 2: level_0_compressed = self.compress_level_0(x_level_add) level_0_resized = F.interpolate( level_0_compressed, scale_factor=2, mode='nearest') level_1_resized = x_level_1 level_2_resized = self.stride_level_2(x_level_2) elif self.level == 3: level_0_compressed = self.compress_level_0(x_level_add) level_0_resized = F.interpolate( level_0_compressed, scale_factor=4, mode='nearest') x_level_1_compressed = self.compress_level_1(x_level_1) level_1_resized = F.interpolate( x_level_1_compressed, scale_factor=2, mode='nearest') 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, dim=1) 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(dim=1) else: return out class FASFFHead(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(nc=80, 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_max=16, end2end=False, ch: tuple = (), multiplier=1, rfb=False): """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 = FASFF(level=0, ch=ch, multiplier=multiplier, rfb=rfb) self.l1_fusion = FASFF(level=1, ch=ch, multiplier=multiplier, rfb=rfb) self.l2_fusion = FASFF(level=2, ch=ch, multiplier=multiplier, rfb=rfb) self.l3_fusion = FASFF(level=3, ch=ch, multiplier=multiplier, rfb=rfb) 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_head=self.cv2, cls_head=self.cv3) @property def one2one(self): """Returns the one-to-one head components.""" return dict(box_head=self.one2one_cv2, cls_head=self.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]: """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(boxes=boxes, scores=scores, feats=x) 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) x4 = self.l3_fusion(x) x = [x4, 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, xywh=xywh and not self.end2end and not self.xyxy, dim=1, ) 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(dim=1, index=idx.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, keepdim=True) scores, indices = scores.topk(k, dim=1) labels = labels.gather(1, indices) return scores, labels, indices ori_index = scores.max(dim=-1)[0].topk(k)[1].unsqueeze(-1) scores = scores.gather(dim=1, index=ori_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

四、手把手教你添加FASFFHead卷积

4.1 修改一

首先我们将上面的代码复制粘贴到'ultralytics/nn' 目录下新建一个py文件复制粘贴进去,具体名字自己来定,我这里起名为FASFFHead.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 修改六

同理

​​​


4.7 修改七

同理


4.8 修改八

这里有一些不一样,我们需要加一行代码

else: return 'detect'

为啥呢不一样,因为这里的m在代码执行过程中会将你的代码自动转换为小写,所以直接else方便一点,以后出现一些其它分割或者其它的教程的时候在提供其它的修改教程。

​​​​


4.9 修改九

同理.

​​​​


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


五、FASFFHead检测头的yaml文件

此版本训练信息:YOLO26-Head-FASFFHead summary: 260 layers, 2,506,140 parameters, 2,506,140 gradients, 5.8 GFLOPs

# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license # Ultralytics YOLO26 object detection model with P2/4 - 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. 'model=yolo26n-p2.yaml' will call yolo26-p2.yaml with scale 'n' # [depth, width, max_channels] n: [0.50, 0.25, 1024] # summary: 329 layers, 2,662,400 parameters, 2,662,400 gradients, 9.5 GFLOPs s: [0.50, 0.50, 1024] # summary: 329 layers, 9,765,856 parameters, 9,765,856 gradients, 27.8 GFLOPs m: [0.50, 1.00, 512] # summary: 349 layers, 21,144,288 parameters, 21,144,288 gradients, 91.4 GFLOPs l: [1.00, 1.00, 512] # summary: 489 layers, 25,815,520 parameters, 25,815,520 gradients, 115.3 GFLOPs x: [1.00, 1.50, 512] # summary: 489 layers, 57,935,232 parameters, 57,935,232 gradients, 256.9 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, nn.Upsample, [None, 2, "nearest"]] - [[-1, 2], 1, Concat, [1]] # cat backbone P2 - [-1, 2, C3k2, [128, True]] # 19 (P2/4-xsmall) - [-1, 1, Conv, [128, 3, 2]] - [[-1, 16], 1, Concat, [1]] # cat head P3 - [-1, 2, C3k2, [256, True]] # 22 (P3/8-small) - [-1, 1, Conv, [256, 3, 2]] - [[-1, 13], 1, Concat, [1]] # cat head P4 - [-1, 2, C3k2, [512, True]] # 25 (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]] # 28 (P5/32-large) - [[19, 22, 25, 28], 1, FASFFHead, [nc]] # Detect(P2, P3, P4, P5)

六、完美运行记录

最后提供一下完美运行的图片。

​​


七、本文总结

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

专栏链接:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制

← 返回列表