Ultralytics:简要解读YOLOv8 → YOLO11 → YOLO26网络架构
- 前言
- 相关介绍
-
- [Ultralytics 简介](#Ultralytics 简介)
- 前提条件
- 实验环境
- 总览:代码架构
- 核心机制:YAML→模型的编译过程(parse_model)
- [YOLOv8 架构详解](#YOLOv8 架构详解)
-
- [YAML 配置](#YAML 配置)
- 核心模块
- [YOLOv8 Detect Head](#YOLOv8 Detect Head)
- [YOLO11 架构详解](#YOLO11 架构详解)
-
- [从 YOLOv8 到 YOLO11 的核心变化](#从 YOLOv8 到 YOLO11 的核心变化)
- C3k2:更灵活的CSP模块
- C2PSA:自注意力增强
- [YOLO11 整体架构图](#YOLO11 整体架构图)
- [YOLO26 架构详解](#YOLO26 架构详解)
-
- [从 YOLO11 到 YOLO26 的核心变化](#从 YOLO11 到 YOLO26 的核心变化)
- End-to-End(端到端)检测
-
- [one-to-many vs one-to-one 双头设计:](#one-to-many vs one-to-one 双头设计:)
- [SPPF 升级:加 shortcut 连接](#SPPF 升级:加 shortcut 连接)
-
- [YOLO26 的 SPPF 相比 YOLOv8/YOLO11:](#YOLO26 的 SPPF 相比 YOLOv8/YOLO11:)
- [YOLOv8 → YOLO11 → YOLO26对比](#YOLOv8 → YOLO11 → YOLO26对比)
- 参考文献

前言
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏、人工智能混合编程实践专栏或我的个人主页查看
- YOLOs-CPP:一个免费开源的YOLO全系列C++推理库(以YOLO26为例)
- PaddleOCR:Win10上安装使用PPOCRLabel标注工具
- 目标检测:使用自己的数据集微调DEIMv2进行物体检测
- 图像分割:PyTorch从零开始实现SegFormer语义分割
- 图像超分:使用自己的数据集微调Real-ESRGAN-x4plus进行超分重建
- 图像生成:PyTorch从零开始实现一个简单的扩散模型
- Stable Diffusion:使用自己的数据集微调 Stable Diffusion 3.5 LoRA 文生图模型
- 图像超分:使用自己的数据集微调Real-ESRGAN-x2plus进行超分重建
- Anomalib:使用Anomalib 2.1.0训练自己的数据集进行异常检测
- Anomalib:在Linux服务器上安装使用Anomalib 2.1.0
- 人工智能混合编程实践:C++调用封装好的DLL进行异常检测推理
- 人工智能混合编程实践:C++调用封装好的DLL进行FP16图像超分重建(v3.0)
- 隔离系统Python:源码编译3.11.8到自定义目录(含PGO性能优化)
- 在线机的Python环境迁移到离线机上
- Nuitka 将 Python 脚本封装为 .pyd 或 .so 文件
- Ultralytics:使用 YOLO11 进行速度估计
- Ultralytics:使用 YOLO11 进行物体追踪
- Ultralytics:使用 YOLO11 进行物体计数
- Ultralytics:使用 YOLO11 进行目标打码
- 人工智能混合编程实践:C++调用Python ONNX进行YOLOv8推理
- 人工智能混合编程实践:C++调用封装好的DLL进行YOLOv8实例分割
- 人工智能混合编程实践:C++调用Python ONNX进行图像超分重建
- 人工智能混合编程实践:C++调用Python AgentOCR进行文本识别
- 通过计算实例简单地理解PatchCore异常检测
- Python将YOLO格式实例分割数据集转换为COCO格式实例分割数据集
- YOLOv8 Ultralytics:使用Ultralytics框架训练RT-DETR实时目标检测模型
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目
- Stable Diffusion:在服务器上部署使用Stable Diffusion WebUI进行AI绘图(v2.0)
- Stable Diffusion:使用自己的数据集微调训练LoRA模型(v2.0)
相关介绍
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
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏、人工智能混合编程实践专栏或我的个人主页查看
- YOLOs-CPP:一个免费开源的YOLO全系列C++推理库(以YOLO26为例)
- PaddleOCR:Win10上安装使用PPOCRLabel标注工具
- 目标检测:使用自己的数据集微调DEIMv2进行物体检测
- 图像分割:PyTorch从零开始实现SegFormer语义分割
- 图像超分:使用自己的数据集微调Real-ESRGAN-x4plus进行超分重建
- 图像生成:PyTorch从零开始实现一个简单的扩散模型
- Stable Diffusion:使用自己的数据集微调 Stable Diffusion 3.5 LoRA 文生图模型
- 图像超分:使用自己的数据集微调Real-ESRGAN-x2plus进行超分重建
- Anomalib:使用Anomalib 2.1.0训练自己的数据集进行异常检测
- Anomalib:在Linux服务器上安装使用Anomalib 2.1.0
- 人工智能混合编程实践:C++调用封装好的DLL进行异常检测推理
- 人工智能混合编程实践:C++调用封装好的DLL进行FP16图像超分重建(v3.0)
- 隔离系统Python:源码编译3.11.8到自定义目录(含PGO性能优化)
- 在线机的Python环境迁移到离线机上
- Nuitka 将 Python 脚本封装为 .pyd 或 .so 文件
- Ultralytics:使用 YOLO11 进行速度估计
- Ultralytics:使用 YOLO11 进行物体追踪
- Ultralytics:使用 YOLO11 进行物体计数
- Ultralytics:使用 YOLO11 进行目标打码
- 人工智能混合编程实践:C++调用Python ONNX进行YOLOv8推理
- 人工智能混合编程实践:C++调用封装好的DLL进行YOLOv8实例分割
- 人工智能混合编程实践:C++调用Python ONNX进行图像超分重建
- 人工智能混合编程实践:C++调用Python AgentOCR进行文本识别
- 通过计算实例简单地理解PatchCore异常检测
- Python将YOLO格式实例分割数据集转换为COCO格式实例分割数据集
- YOLOv8 Ultralytics:使用Ultralytics框架训练RT-DETR实时目标检测模型
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目
- Stable Diffusion:在服务器上部署使用Stable Diffusion WebUI进行AI绘图(v2.0)
- Stable Diffusion:使用自己的数据集微调训练LoRA模型(v2.0)