Ultralytics:简要解读YOLOv8 → YOLO11 → YOLO26网络架构

Ultralytics:简要解读YOLOv8 → YOLO11 → YOLO26网络架构

前言

相关介绍

Ultralytics 简介

Ultralytics 基于多年的计算机视觉和人工智能基础研究,创建了最先进的 (SOTA) YOLO 模型。我们的模型不断更新性能和灵活性,快速、准确且易于使用。他们擅长对象检测、跟踪、实例分割、语义分割、图像分类和姿势估计任务。

前提条件

  • 熟悉Python、Pytorch

实验环境

bash 复制代码
Package                  Version
------------------------ ------------
Python                   3.11.8
absl-py                  2.4.0
accelerate               1.13.0
annotated-doc            0.0.4
anyio                    4.13.0
calflops                 0.3.2
certifi                  2026.4.22
charset-normalizer       3.4.7
click                    8.3.3
colorama                 0.4.6
contourpy                1.3.3
cycler                   0.12.1
filelock                 3.29.0
flatbuffers              25.12.19
fonttools                4.62.1
fsspec                   2026.4.0
grpcio                   1.80.0
h11                      0.16.0
hf-xet                   1.5.0
httpcore                 1.0.9
httpx                    0.28.1
huggingface_hub          1.14.0
idna                     3.15
Jinja2                   3.1.6
kiwisolver               1.5.0
Markdown                 3.10.2
markdown-it-py           4.2.0
MarkupSafe               3.0.3
matplotlib               3.10.9
mdurl                    0.1.2
ml_dtypes                0.5.0
mpmath                   1.3.0
networkx                 3.6.1
numpy                    1.26.4
nvidia-cublas-cu12       12.8.3.14
nvidia-cuda-cupti-cu12   12.8.57
nvidia-cuda-nvrtc-cu12   12.8.61
nvidia-cuda-runtime-cu12 12.8.57
nvidia-cudnn-cu12        9.7.1.26
nvidia-cufft-cu12        11.3.3.41
nvidia-cufile-cu12       1.13.0.11
nvidia-curand-cu12       10.3.9.55
nvidia-cusolver-cu12     11.7.2.55
nvidia-cusparse-cu12     12.5.7.53
nvidia-cusparselt-cu12   0.6.3
nvidia-nccl-cu12         2.26.2
nvidia-nvjitlink-cu12    12.8.61
nvidia-nvtx-cu12         12.8.55
onnx                     1.19.0
onnxruntime-gpu          1.26.0
onnxslim                 0.1.94
opencv-python            4.6.0.66
packaging                26.2
pillow                   12.2.0
pip                      24.0
polars                   1.40.1
polars-runtime-32        1.40.1
protobuf                 7.34.1
psutil                   7.2.2
pycocotools              2.0.11
Pygments                 2.20.0
pyparsing                3.3.2
python-dateutil          2.9.0.post0
PyYAML                   6.0.3
regex                    2026.5.9
requests                 2.34.1
rich                     15.0.0
safetensors              0.7.0
scipy                    1.16.0
setuptools               65.5.0
shellingham              1.5.4
six                      1.17.0
sympy                    1.14.0
tabulate                 0.10.0
tensorboard              2.20.0
tensorboard-data-server  0.7.2
tokenizers               0.22.2
torch                    2.7.1+cu128
torchaudio               2.7.1+cu128
torchvision              0.22.1+cu128
tqdm                     4.67.3
transformers             5.8.1
triton                   3.3.1
typer                    0.25.1
typing_extensions        4.15.0
ultralytics              8.4.58
ultralytics-thop         2.0.19
urllib3                  2.7.0
Werkzeug                 3.1.8

总览:代码架构

bash 复制代码
ultralytics/
├── cfg/         # 配置层:模型YAML、数据集YAML、训练超参数
├── nn/          # 神经网络层:模块定义、模型组装、loss计算
│   ├── modules/ # 原子模块:Conv/Bottleneck/C2f/C3k2/Attention等
│   ├── tasks.py # 模型工场:parse_model()将YAML编译为PyTorch模型
│   └── autobackend.py # 多后端推理封装
├── models/      # 任务层:detect/segment/pose/classify的train/val/predict
│   └── yolo/
│       ├── model.py  # YOLO类入口(继承自engine/Model)
│       ├── detect/
│       │   ├── train.py, val.py, predict.py
│       ├── segment/
│       └── pose/
└── engine/      # 引擎层:Model基类、Trainer、Validator、Predictor、Exporter
    └── model.py # Model门面类统一调用train/val/predict/export

用户入口是 YOLO('yolo26n.pt')。关键在于 Model.init 初始化流程:

bash 复制代码
YOLO('yolo26n.yaml')
 → Model.__init__ → ._new() 解析YAML
 → DetectionModel.__init__  → _initialize_yolo_model()
   → yaml_model_load() 读取yaml
   → parse_model() 从YAML编译网络
   → 自动计算stride + bias_init
 → 暴露 .train()/.val()/.predict()/.export()

核心机制:YAML→模型的编译过程(parse_model)

ultralytics/nn/tasks.py 中的 parse_model() 是整个系统的模型编译器。它把YAML配置翻译成 nn.Sequential。

YAML中每一行格式为:from, repeats, module, args

  • from: 输入来自哪一层的输出(-1表示上一层,整数索引,列表表示多输入)
  • repeats: 重复次数(会被乘以 depth 缩放因子)
  • module: 模块名(如 Conv, C2f, C3k2, SPPF, Detect 等)
  • args: 参数列表(通道数等)

关键代码(第1849-2049行):

python 复制代码
def parse_model(d, ch, verbose=True):
    depth, width, max_channels = scales[scale]  # 从scale获取缩放因子
    # ...
    for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]):
        m = getattr(torch.nn, m[3:]) if m.startswith("nn.")   # nn.xxx
            else globals()[m]                                   # 自定义模块
        
        # 通道缩放:c2 = make_divisible(min(c2, max_channels) * width, 8)
        # 深度缩放:n = max(round(n * depth), 1) if n > 1 else n
        
        m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)
        save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1)
        # save记录需要保存中间输出的层索引(多输入连接用)

重要细节:ch 数组记录了每一层的输出通道数,后续层通过 chf 获取输入的通道数。对于 Concat 层,c2 = sum(chx for x in f) 做通道拼接。对于 Detect 头,它接收所有检测层的通道数:args.extend(reg_max, end2end, \[ch\[x for x in f]])。

YOLOv8 架构详解

YAML 配置

yaml 复制代码
# ultralytics/cfg/models/v8/yolov8.yaml
nc: 80
scales:
  n: [0.33, 0.25, 1024]  # [depth, width, max_channels]
  s: [0.33, 0.50, 1024]
  m: [0.67, 0.75, 768]
  l: [1.00, 1.00, 512]
  x: [1.00, 1.25, 512]

backbone:
  - [-1, 1, Conv, [64, 3, 2]]        # 0-P1/2   stem
  - [-1, 1, Conv, [128, 3, 2]]       # 1-P2/4
  - [-1, 3, C2f, [128, True]]        # 2
  - [-1, 1, Conv, [256, 3, 2]]       # 3-P3/8
  - [-1, 6, C2f, [256, True]]        # 4
  - [-1, 1, Conv, [512, 3, 2]]       # 5-P4/16
  - [-1, 6, C2f, [512, True]]        # 6
  - [-1, 1, Conv, [1024, 3, 2]]      # 7-P5/32
  - [-1, 3, C2f, [1024, True]]       # 8
  - [-1, 1, SPPF, [1024, 5]]         # 9

head:
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 6], 1, Concat, [1]]        # cat backbone P4
  - [-1, 3, C2f, [512]]              # 12
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 4], 1, Concat, [1]]        # cat backbone P3
  - [-1, 3, C2f, [256]]              # 15 (P3/8-small)
  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 12], 1, Concat, [1]]       # cat head P4
  - [-1, 3, C2f, [512]]              # 18 (P4/16-medium)
  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 9], 1, Concat, [1]]        # cat head P5
  - [-1, 3, C2f, [1024]]             # 21 (P5/32-large)
  - [[15, 18, 21], 1, Detect, [nc]]  # Detect(P3, P4, P5)

核心模块

Conv (conv.py:39-89): 标准卷积+BN+SiLU,是最基本的构建块:

python 复制代码
class Conv(nn.Module):
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
        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 = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
    
    def forward(self, x):
        return self.act(self.bn(self.conv(x)))
    
    def forward_fuse(self, x):  # BN融合后的前向
        return self.act(self.conv(x))

Bottleneck (block.py:457-481): 标准残差瓶颈块(1×1降维 → 3×3卷积 → shortcut):

python 复制代码
class Bottleneck(nn.Module):
    def __init__(self, c1, c2, shortcut=True, g=1, k=(3,3), e=0.5):
        c_ = int(c2 * e)  # 隐藏层通道数
        self.cv1 = Conv(c1, c_, k[0], 1)
        self.cv2 = Conv(c_, c2, k[1], 1, g=g)
        self.add = shortcut and c1 == c2
    
    def forward(self, x):
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

C2f (block.py:288-319): YOLOv8的核心构建块,CSP(Cross Stage Partial)结构的优化实现:

python 复制代码
class C2f(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        self.c = int(c2 * e)  # 隐藏通道数
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)        # 1×1卷积分出两条路径
        self.cv2 = Conv((2 + n) * self.c, c2, 1)     # 输出拼接后投影
        self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g) for _ in range(n))
    
    def forward(self, x):
        y = list(self.cv1(x).chunk(2, 1))   # 分成两半: [一半直接传递, 另一半进Bottleneck链]
        y.extend(m(y[-1]) for m in self.m)   # 每一级Bottleneck的输出都保留
        return self.cv2(torch.cat(y, 1))     # 全部拼接后投影到输出通道

C2f的设计思路:输入经1×1卷积后分成两路,一路直接作为"跨级连接",另一路经过 n 个 Bottleneck 残差块串行处理,每一级的输出都被保留,最后所有分支拼接起来再1×1投影。与传统的C3(CSP Bottleneck with 3 convs)相比,C2f用1×1代替了3×3的前置卷积,且保留了每一级Bottleneck的输出,梯度回传路径更丰富。

SPPF (block.py:208-237): 快速空间金字塔池化,用多次串行的5×5最大池化模拟不同感受野:

python 复制代码
class SPPF(nn.Module):
    def __init__(self, c1, c2, k=5, n=3, shortcut=False):
        c_ = c1 // 2
        self.cv1 = Conv(c1, c_, 1, 1, act=False)
        self.cv2 = Conv(c_ * (n + 1), c2, 1, 1)
        self.m = nn.MaxPool2d(k, 1, k // 2)  # 5×5池化,padding=2保持HW不变
    
    def forward(self, x):
        y = [self.cv1(x)]
        y.extend(self.m(y[-1]) for _ in range(3))  # 串行池化: y[0], pool(y[0]), pool(pool(y[0])), pool(pool(pool(y[0])))
        y = self.cv2(torch.cat(y, 1))
        return y + x if self.add else y

通过串行3次5×5池化,等效于感受野分别为5、9、13的并行池化(原始的SPP(k=(5,9,13))),但计算量大幅降低。

YOLOv8 Detect Head

head.py:37-262 的 Detect 类实现了YOLOv8的解耦检测头:

python 复制代码
class Detect(nn.Module):
    def __init__(self, nc=80, reg_max=16, end2end=False, ch=()):
        self.nl = len(ch)       # 检测层数(3层: P3/P4/P5)
        self.reg_max = reg_max  # DFL的离散bin数
        self.no = nc + reg_max * 4  # 每个anchor的输出维度
        
        # Box分支:每个检测层 -> 3×3 Conv -> 3×3 Conv -> 1×1 Conv(4*reg_max)
        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)
        
        # Cls分支:V8使用DWConv结构节省参数量
        self.cv3 = nn.ModuleList(
            nn.Sequential(
                nn.Sequential(DWConv(x, x, 3), Conv(x, c3, 1)),   # DWConv降低参数量
                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()

核心计算流程(forward + _inference):

python 复制代码
def forward(self, x):
    preds = self.forward_head(x, **self.one2many)  # → dict(boxes, scores, feats)
    # boxes: [B, 4*reg_max, ΣHW], scores: [B, nc, ΣHW]
    
    if not self.training:   # 推理模式
        y = self._inference(preds)
        y = self.postprocess(y.permute(0, 2, 1))  # [B, N, 6] 格式
    
    # _inference内部:
    dbox = self._get_decode_boxes(x)     # DFL解码 + anchor解码 → [B, 4, ΣHW]
    return torch.cat((dbox, x["scores"].sigmoid()), 1)  # [B, 4+nc, ΣHW]

DFL(Distribution Focal Loss) (block.py:58-80) 将边框回归建模为离散分布:

python 复制代码
class DFL(nn.Module):
    def __init__(self, c1=16):
        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))
    
    def forward(self, x):
        # x shape: [B, 4*16, anchors] → view [B, 4, 16, anchors] → softmax → 加权求和
        b, _, a = x.shape
        return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)

DFL不是直接回归坐标,而是把每个边的位置离散为 reg_max 个bin,用softmax学习分布后加权求和得到连续值。YOLOv8的 reg_max=16,YOLO26的 reg_max=1(回归退化为直接预测)。

YOLO11 架构详解

yaml 复制代码
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license

# Ultralytics YOLO11 object detection model with P3/8 - P5/32 outputs
# Model docs: https://docs.ultralytics.com/models/yolo11
# Task docs: 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: 181 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
  s: [0.50, 0.50, 1024] # summary: 181 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
  m: [0.50, 1.00, 512] # summary: 231 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
  l: [1.00, 1.00, 512] # summary: 357 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
  x: [1.00, 1.50, 512] # summary: 357 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

# YOLO11n head
head:
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 6], 1, Concat, [1]] # cat backbone P4
  - [-1, 2, C3k2, [512, 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, Conv, [256, 3, 2]]
  - [[-1, 13], 1, Concat, [1]] # cat head P4
  - [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 10], 1, Concat, [1]] # cat head P5
  - [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large)

  - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

从 YOLOv8 到 YOLO11 的核心变化

YAML对比(cfg/models/11/yolo11.yaml):

C3k2:更灵活的CSP模块

block.py:1069-1106:

python 复制代码
class C3k2(C2f):  # 继承C2f
    def __init__(self, c1, c2, n=1, c3k=False, e=0.5, attn=False, g=1, shortcut=True):
        super().__init__(c1, c2, n, shortcut, g, e)  # 调用C2f.__init__
        self.m = nn.ModuleList(
            nn.Sequential(
                Bottleneck(self.c, self.c, shortcut, g),
                PSABlock(self.c, attn_ratio=0.5, num_heads=max(self.c // 64, 1)),
            ) if attn
            else C3k(self.c, self.c, 2, shortcut, g)   # c3k=True时用大kernel Bottleneck
            if c3k
            else Bottleneck(self.c, self.c, shortcut, g)  # 默认就是标准Bottleneck
            for _ in range(n)
        )

C3k2与C2f的结构完全相同(都继承C2f的 cv1 → split → Bottleneck链 → cat → cv2),唯一的区别是内部的子模块:

  • c3k=False(默认):与C2f一样用标准 Bottleneck
  • c3k=True:改用 C3k(即 kernel size=3×3 的 Bottleneck,C2f默认k=(3,3)本质一样)
  • attn=True:Bottleneck叠加
  • PSABlock(用于YOLO11的M/L/X大模型)

C2PSA:自注意力增强

block.py:1436-1488 和 PSA:

python 复制代码
class PSABlock(nn.Module):
    def __init__(self, c, attn_ratio=0.5, num_heads=4, shortcut=True):
        self.attn = Attention(c, attn_ratio=attn_ratio, num_heads=num_heads)
        self.ffn = nn.Sequential(Conv(c, c*2, 1), Conv(c*2, c, 1, act=False))
        self.add = shortcut
    
    def forward(self, x):
        x = x + self.attn(x) if self.add else self.attn(x)
        x = x + self.ffn(x) if self.add else self.ffn(x)
        return x

class C2PSA(nn.Module):  # 结构同C2f,但用PSABlock替代Bottleneck
    def __init__(self, c1, c2, n=1, e=0.5):
        assert c1 == c2
        self.c = int(c1 * e)
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        self.cv2 = Conv(2 * self.c, c1, 1)
        self.m = nn.Sequential(*(PSABlock(self.c, 0.5, self.c//64) for _ in range(n)))
    
    def forward(self, x):
        a, b = self.cv1(x).split((self.c, self.c), dim=1)
        b = self.m(b)              # 只对一半走自注意力
        return self.cv2(torch.cat((a, b), 1))

Attention (block.py:1271-1328) 是轻量化的多头自注意力,但不是标准MHSA(Multi-Head Self-Attention):

python 复制代码
class Attention(nn.Module):
    def __init__(self, dim, num_heads=8, attn_ratio=0.5):
        self.num_heads = num_heads
        self.head_dim = dim // num_heads
        self.key_dim = int(self.head_dim * attn_ratio)  # key的维度比value小(节省计算)
        self.scale = self.key_dim ** -0.5
        nh_kd = self.key_dim * num_heads
        h = dim + nh_kd * 2
        self.qkv = Conv(dim, h, 1, act=False)   # 1x1卷积同时生成QKV
        self.proj = Conv(dim, dim, 1, act=False)
        self.pe = Conv(dim, dim, 3, 1, g=dim, act=False)  # depthwise卷积做位置编码
    
    def forward(self, x):
        B, C, H, W = x.shape
        N = H * W
        qkv = self.qkv(x)
        q, k, v = qkv.view(B, self.num_heads, self.key_dim*2 + self.head_dim, N).split(...)
        attn = (q * self.scale).transpose(-2, -1) @ k
        attn = attn.softmax(dim=-1)
        x = (v @ attn.transpose(-2, -1)).view(B, C, H, W) + self.pe(v.reshape(B, C, H, W))
        return self.proj(x)

关键点:Q的维度 = num_heads × key_dim,V的维度 = num_heads × head_dim,其中 key_dim < head_dim(attention ratio 控制),这是线性注意力的一种近似。

YOLO11 整体架构图

bash 复制代码
Backbone:                              Head (FPN + Detect):
Input(640×640)                         
  ↓ Conv k3 s2                           P5(20×20) ← SPPF + C2PSA
  ↓ Conv k3 s2                           ↓ UP ×2
  ↓ C3k2×2           P3(80×80) level     Concat [P4↓]
  ↓ Conv k3 s2        ← ─ ─ ─ ─ ─ ─ ─ ┤
  ↓ C3k2×2                               ↓ C3k2
  ↓ Conv k3 s2        ← P4(40×80)        ↓ UP ×2
  ↓ C3k2×2            ← ─ ─ ─ ─ ─ ─ ─ ┤
  ↓ Conv k3 s2                           Concat [P3↓]
  ↓ C3k2×2           P5(20×20)           ↓ C3k2(P3输出)
  ↓ SPPF                                ↓ Conv k3 s2
  ↓ C2PSA                               Concat [P4←]
                                         ↓ C3k2(P4输出)
                                        ↓ Conv k3 s2
                                         Concat [P5←]
                                         ↓ C3k2(P5输出)
                                        Detect[P3,P4,P5]

YOLO26 架构详解

yaml 复制代码
# 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. 'model=yolo26n.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, Detect, [nc]] # Detect(P3, P4, P5)

从 YOLO11 到 YOLO26 的核心变化

YAML对比(cfg/models/26/yolo26.yaml),关键区别:

End-to-End(端到端)检测

YOLO26的关键创新:end2end: True + reg_max: 1

end2end 在代码中的实现:

head.py:89-125 的 Detect.__init__

python 复制代码
def __init__(self, nc=80, reg_max=16, end2end=False, ch=()):
    # ... 常规初始化 ...
    if end2end:
        # 深拷贝一份one-to-one的检测头
        self.one2one_cv2 = copy.deepcopy(self.cv2)  # box
        self.one2one_cv3 = copy.deepcopy(self.cv3)  # cls

one-to-many vs one-to-one 双头设计:

训练时同时输出两个分支:

  • one2many:使用 TaskAlignedAssigner(topk=10),每个GT匹配多个anchor(传统一对多分配),用于训练时丰富正样本
  • one2one:使用 TaskAlignedAssigner(topk=7, topk2=1),每个GT只匹配一个anchor(一对一分配),推理时直接输出而无需NMS

E2ELoss (loss.py:1174-1206) 实现了双头损失的动态衰减:

python 复制代码
class E2ELoss:
    def __init__(self, model, loss_fn=v8DetectionLoss):
        self.one2many = loss_fn(model, tal_topk=10)       # 传统一对多分支
        self.one2one = loss_fn(model, tal_topk=7, tal_topk2=1)  # 一对一(端到端)分支
        self.o2m = 0.8     # 初始一对多权重
        self.o2o = 0.2     # 初始一对一权重
        self.final_o2m = 0.1  # 最终一对多权重
    
    def update(self):
        # 训练过程中,一对多的权重从0.8线性衰减到0.1
        # 一对一的权重从0.2上升到0.9
        self.o2m = self.decay(self.updates)
        self.o2o = max(1.0 - self.o2m, 0)

这样设计的原因是:早期训练需要一对多分配来提供丰富的梯度信号;训练后期逐步切换到一对一,使得模型学会在没有NMS的情况下直接输出最终检测结果。

reg_max=1 意味着 DFL 退化为 nn.Identity()(因为 reg_max > 1 时才启用DFL),bbox回归直接用1×1 conv输出4个值。

推理模式下,Detect.forward 流程:

python 复制代码
def forward(self, x):
    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))  # postprocess会自动做topk筛选
    return y if self.export else (y, preds)

推理时使用 one2one 分支输出 → postprocess(topk挑选)→ 直接输出,不需要NMS。

SPPF 升级:加 shortcut 连接

YOLO26 的 SPPF 相比 YOLOv8/YOLO11:

yaml 复制代码
# YOLO11:  [-1, 1, SPPF, [1024, 5]]         # k=5, n=3(默认), shortcut=False
# YOLO26:  [-1, 1, SPPF, [1024, 5, 3, True]] # k=5, n=3, shortcut=True

SPPF.__init__ self.add = shortcut and c1 == c2,所以 forward:

python 复制代码
def forward(self, x):
    y = [self.cv1(x)]
    y.extend(self.m(y[-1]) for _ in range(3))
    y = self.cv2(torch.cat(y, 1))
    return y + x if self.add else y  # YOLO26加了残差连接

YOLOv8 → YOLO11 → YOLO26对比

演进过程

  • YOLOv8:确立了C2f+CSP+解耦头+DFL的现代YOLO范式
  • YOLO11:引入C2PSA自注意力+更灵活的C3k2块,计算量降低且精度提升
  • YOLO26:端到端检测(去除NMS依赖)+归一化流(关键点不确定性建模)+多尺度原型(分割)+语义分割

从单纯的目标检测器进化为统一视觉任务框架

所有这三代模型共享同一套代码基础架构(BaseModel → parse_model → modules/ → DetectionModel),YAML配置的差异就是架构差异的直接映射。

参考文献

1 https://docs.ultralytics.com/

2 https://github.com/ultralytics/ultralytics.git

相关推荐
BaoshengTT1 小时前
成都理想贴膜能否分期及汽车贴膜分期行业规则 保圣威固 7V 不凡门店
python·汽车
卷无止境1 小时前
Python调试那点事儿:从pdb到日志系统的实战指南
后端·python
j7~1 小时前
【C语言】《C语言自定义类型(结构体+联合体+枚举类型)》--详解
c语言·开发语言·深度学习·结构体·位段·联合体·枚举类型
ACP广源盛139246256731 小时前
YLB3118 存储桥接芯片完整机会点@ACP#联动曙光 8000
大数据·人工智能·分布式·单片机·嵌入式硬件
wechat_Neal1 小时前
机器学习生命周期的核心环节
人工智能·机器学习
蝎蟹居1 小时前
GBT 4706.1-2024逐句解读系列(31) 第7.21.1条款:说明书怎么写符合标准要求
人工智能·单片机·嵌入式硬件·物联网·安全
橙臣程1 小时前
深度学习11——Tokenization、embedding、位置编码
人工智能·深度学习·embedding
正和视觉教育1 小时前
深圳CCD视觉培训:快速掌握机器视觉检测核心技术
人工智能·python·计算机视觉·视觉检测
axinawang1 小时前
第16课:编码、解码、转义字符、转义序列、输出格式化
python