Ultralytics YOLO26 端到端 NMS-Free 目标检测:输出格式、导出兼容性与迁移实践指南
YOLO26 的检测类模型(检测、实例分割、姿态估计、OBB)默认采用端到端推理:模型直接输出最终检测结果,不再需要 NMS(非极大值抑制)后处理步骤。本文基于 Ultralytics 仓库中的官方指南与源码实现,完整解析 YOLO26 的双头架构、新输出格式 (N, 300, 6)、各导出格式的端到端兼容性、量化对端到端分支的影响,以及从 YOLOv8/YOLO11 迁移时的全部注意事项。读完后,你既能判断自己的推理代码是否需要改动,也能定位导出格式中 NMS 自动回退的底层原因。
为什么 YOLO26 要移除 NMS
传统 YOLO 模型(YOLOv8、YOLO11)的原始输出是数千个重叠候选框,必须经过独立的 NMS 步骤才能过滤出最终结果。这带来三个部署层面的问题:
- 额外延迟:NMS 需要遍历所有候选框做两两 IoU 计算,是推理管线中不可忽略的一环;
- 导出图复杂化:NMS 依赖动态控制流,不同硬件平台上的导出行为难以保持一致;
- 跨平台不一致:不同推理引擎对 NMS 的实现细节存在差异,可能导致结果漂移。
YOLO26 以端到端(End-to-End)检测解决了上述问题:由 one-to-one 检测头内部完成 top-k 筛选与去重,直接输出最终检测结果。官方文档给出的收益数据是:YOLO26n 在 CPU ONNX 推理(Intel Xeon CPU @ 2.00 GHz)上比 YOLO11n 最高快 43%(见 YOLO26 模型页)。该设计同时服务于 YOLOE-26 开放词汇模型,使其在类别动态变化时仍保持快速推理。
双头架构:one-to-one 与 one-to-many
YOLO26 在训练阶段使用双头架构:两个检测头共享同一 backbone 和 neck,但输出方式不同。
| 检测头 | 用途 | 检测输出 | 后处理 |
|---|---|---|---|
| One-to-One(默认) | 端到端推理 | (N, 300, 6) |
仅置信度阈值过滤 |
| One-to-Many | 传统 YOLO 输出 | (N, nc + 4, 8400) |
需要 NMS |
上表以检测任务为例:N 是批次大小,nc 是类别数(如 COCO 的 80),8400 是 imgsz=640 下的 anchor 总数。其他任务在 one-to-one 输出上追加任务专属数据:
| 任务 | 端到端输出 | 追加数据 |
|---|---|---|
| 检测 | (N, 300, 6) |
— |
| 实例分割 | (N, 300, 6 + nm) + proto (N, nm, H, W) |
nm 个掩码系数(默认 32) |
| 姿态估计 | (N, 300, 57) |
17 个关键点 × 3(x, y, 可见性) |
| OBB | (N, 300, 7) |
旋转角 |
训练时两个头同时运行——one-to-many 头提供更丰富的学习信号,one-to-one 头学习产出干净、不重叠的预测。推理与导出时默认只激活 one-to-one 头,每图最多 300 个检测,格式为 [x1, y1, x2, y2, confidence, class_id]。
源码印证:Detect 头的实现
配置文件中可以看到 end2end 是模型级参数,在 yolo26.yaml 中显式声明:
nc: 80 # number of classes
end2end: True # whether to use end-to-end mode
reg_max: 1 # DFL bins
在 Detect 头实现 中,end2end=True 时通过 copy.deepcopy 复制出一套独立的 one-to-one 卷积分支:
if end2end:
self.one2one_cv2 = copy.deepcopy(self.cv2)
self.one2one_cv3 = copy.deepcopy(self.cv3)
forward 中的关键逻辑(head.py)印证了双头行为:训练时同时计算两个头的预测;推理时只走 one-to-one 分支,并经 postprocess 完成 top-k 截断:
preds = self.forward_head(x, **self.one2many)
if self.end2end:
x_detach = [xi.detach() for xi in x] if self.training else x # detach keeps one2one out of the backbone
one2one = self.forward_head(x_detach, **self.one2one)
preds = {"one2many": preds, "one2one": one2one}
...
y = self._inference(preds["one2one"] if self.end2end else preds)
值得注意的是训练时用 detach() 把 one-to-one 分支与 backbone 解耦,从源码结构看其梯度主要由 one-to-many 头驱动。postprocess 方法(head.py)将原始预测 (batch, num_anchors, 4 + nc + extra) 转换为文档所述的 (batch, min(max_det, num_anchors), 6 + extra)——这就是"模型内部完成去重"的具体位置。而 fuse() 方法会直接移除 one-to-many 分支(self.cv2 = self.cv3 = None),这正是 model.fuse() 能"减小模型体积与 FLOPs"的源码依据;YOLO26 模型页 中也注明:预训练 checkpoint 参数/FLOPs 统计是 fuse 之后的数值。
其他任务头(Segment、Pose、OBB)同样接受 end2end 参数(head.py),与文档"四种检测类任务均默认支持端到端"的说法一致。
需要修改代码吗?
使用 Ultralytics Python API 或 CLI:无需任何改动
如果你使用标准 Python API 或 CLI,预测、验证、导出全部自动适配端到端模型,只把模型名换成 yolo26n.pt 即可:
from ultralytics import YOLO
# Load a YOLO26 model
model = YOLO("yolo26n.pt")
# Predict — no NMS step, no code changes
results = model.predict("image.jpg")
yolo predict model=yolo26n.pt source=image.jpg
使用自定义推理代码:需要适配新输出格式
如果你为 YOLOv8/YOLO11 写过自定义后处理(例如基于 ONNX Runtime 或 TensorRT 的推理管线),必须更新输出解析逻辑:
| YOLOv8 / YOLO11 | YOLO26(端到端) | |
|---|---|---|
| 检测输出 | (N, nc + 4, 8400) |
(N, 300, 6) |
| 框格式 | xywh(中心 x、中心 y、宽、高) |
xyxy(左上 x、左上 y、右下 x、右下 y) |
| 布局 | 每个 anchor 的框坐标 + 类别分数 | [x1, y1, x2, y2, conf, class_id] |
| 是否需要 NMS | 是 | 否 |
| 后处理 | NMS + 置信度过滤 | 仅置信度过滤 |
端到端模型下后处理被大幅简化,以 ONNX Runtime 为例:
import onnxruntime as ort
# Load and run the exported end-to-end model
session = ort.InferenceSession("yolo26n.onnx")
output = session.run(None, {session.get_inputs()[0].name: input_tensor})
# End-to-end output: (batch, 300, 6) → [x1, y1, x2, y2, confidence, class_id]
detections = output[0][0] # first image in batch
detections = detections[detections[:, 4] > 0.25] # confidence filter, no NMS
切换回 one-to-many 头(传统 NMS 输出)
如需传统 YOLO 输出格式(例如复用现有 NMS 后处理代码),可在 one-to-many 头可用时通过 end2end=False 切换:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
# Prediction with NMS (traditional behavior)
results = model.predict("image.jpg", end2end=False)
# Validation with NMS
metrics = model.val(data="coco.yaml", end2end=False)
# Export without end-to-end
model.export(format="onnx", end2end=False)
yolo predict model=yolo26n.pt source=image.jpg end2end=False
yolo val model=yolo26n.pt data=coco.yaml end2end=False
yolo export model=yolo26n.pt format=onnx end2end=False
在 default.yaml 中,end2end 是一个可选布尔参数(注释说明其适用于 YOLO26 与 YOLOv10 的 predict/val/export)。其默认值为 None(不强制),模型自身的 yaml 配置(end2end: True)生效;显式传入 False 才会切换到 one-to-many 路径。
导出格式兼容性
大多数导出格式原生支持端到端推理,包括 ONNX、TensorRT、CoreML、OpenVINO、LiteRT 和 MNN。
以下格式不支持端到端,会自动回退到 one-to-many 头:NCNN、RKNN、PaddlePaddle、ExecuTorch、IMX、Edge TPU 和 Qualcomm QNN。
回退意味着什么:导出到这些格式时,Ultralytics 会自动切换到 one-to-many 头并打印警告——你的推理管线需要像使用 YOLOv8/YOLO11 一样在端侧自行实现 NMS。
这一行为的源码依据在 exporter.py:导出器检查模型是否具有 end2end 属性,对不支持 top-k 操作的格式直接置位 model.end2end = False 并记录警告:
if hasattr(model, "end2end"):
if self.args.end2end is not None:
model.end2end = self.args.end2end
if fmt in {"rknn", "ncnn", "executorch", "paddle", "imx", "edgetpu", "qnn"}:
# Disable the end2end branch for formats without top-k support
model.end2end = False
LOGGER.warning("This export format does not support end2end models, disabling the end2end branch.")
注意回退集合是硬编码在导出器中的,与本文档列出的格式清单一一对应(IMX 额外要求 nms=True 并自动设置,见 exporter.py)。
Hailo 的特殊处理
对 Hailo,导出器从加载的模型头而非参数判断输出路径:显式传入 end2end 会直接抛出 ValueError(exporter.py)。因此:默认 YOLO26 检测模型保留 NMS-free 的 one-to-one 输出;而若 checkpoint 本身的头已被设为 end2end=False,则会按传统路径编译并配合 HailoRT 的 NMS。
量化与运行时版本会静默关闭端到端
TensorRT 与 LiteRT 支持端到端,但以下情况会自动禁用该分支(各情况均打印警告并改导 one-to-many 头):
| 触发条件 | 原因(源码注释) |
|---|---|
| TensorRT 版本 < 8.5.0 | 旧版 TensorRT 不支持端到端模型所需的 Mod 算子(exporter.py) |
TensorRT 10.3.0 + quantize=8(JetPack 6) |
该组合存在已知的 end2end 构建问题(exporter.py) |
LiteRT + quantize=8 或 quantize="w8a16" |
静态激活量化会破坏端到端的类别索引输出(exporter.py) |
换言之:如果你在 Jetson 上导出 TensorRT INT8 模型时看到端到端分支被禁用的警告,这不是配置错误,而是版本兼容性的保护机制——升级 TensorRT 到 8.5.0+ 或提高导出精度即可恢复端到端路径。
精度与速度的权衡
端到端检测在带来部署收益的同时,对精度的影响很小:
| 指标 | 端到端(默认) | One-to-Many + NMS(end2end=False) |
|---|---|---|
| COCO mAP | 低 0.6–0.8 | 基线 |
| 后处理 | 仅置信度过滤 | 完整 NMS 管线 |
| 部署复杂度 | 最小 | 需要实现 NMS |
具体数值上,五个检测规模中 one-to-one 头在 COCO 上损失 0.6–0.8 个 mAP——YOLO26n 为 40.9 → 40.1,YOLO26x 为 57.5 → 56.9——换来的是完全省去 NMS 这一趟。若你的场景以精度为绝对优先,用 end2end=False 回退到 one-to-many 头即可。更细的各规模基准(n/s/m/l/x)见 YOLO26 性能指标。
从 YOLOv8 / YOLO11 迁移清单
升级现有项目到 YOLO26 时,按角色对照检查:
- Ultralytics API / CLI 用户:无需任何改动,仅把模型名更新为
yolo26n.pt(或yolo26n-seg.pt、yolo26n-pose.pt、yolo26n-obb.pt)。 - 自定义后处理代码:更新输出解析——检测为
(N, 300, 6),分割/姿态/OBB 追加任务数据;同时注意框格式从xywh变为xyxy。 - 导出管线:对照上文兼容性章节确认目标格式是否原生支持端到端。
- TensorRT < 8.5.0:所有精度下端到端均被禁用——升级到 8.5.0+ 以保留端到端。
- 量化导出:TensorRT 10.3.0(JetPack 6)+
quantize=8、LiteRTquantize=8或quantize="w8a16"会自动关闭端到端——改用更高精度导出可保留。 - FP16 导出:若要求所有输出均为 FP16,请用
end2end=False导出(原因见 Export 模式文档中 output0 为何保持 FP32 的说明)。 - iOS / CoreML:端到端完全支持;若需要 Xcode Preview 功能,使用
end2end=False配合nms=True。 - 边缘设备(NCNN、RKNN):这些格式自动回退 one-to-many,端侧管线需自带 NMS。
FAQ
end2end=True 和 nms=True 可以同时使用吗?
不能,二者互斥。在端到端模型上设置 nms=True 导出时,会被强制改为 nms=False 并打印警告(源码见 exporter.py:'nms=True' is not available for end2end models)。端到端头内部已完成重复框过滤,外部 NMS 没有必要。
但 end2end=False + nms=True 是合法组合——它把传统 NMS 烘焙进导出图。这对 CoreML 导出很有用:可以直接在 Xcode 中用 Preview 功能预览检测模型。
端到端模型中 max_det 参数控制什么?
max_det(默认 300,见 default.yaml)控制每图返回的最大检测数,推理和导出时均可调整:
model.predict("image.jpg", max_det=100) # fewer detections
model.export(format="onnx", max_det=500) # more detections for dense scenes
该值会被烘焙进导出图作为检测头的 top-k 参数,因此 max_det=500 会把输出张量拓宽到最多 (1, 500, 6)(以 anchor 数为上限)。这正对应源码中 get_topk_index 的 k = min(max_det, anchors) 逻辑(head.py)。
导出的 ONNX 输出 (1, 300, 6) 是正确的吗?
是。检测任务的端到端输出即:批次 1、最多 300 个检测、每个 6 个值 [x1, y1, x2, y2, confidence, class_id]。只需按置信度阈值过滤,无需 NMS。
其他任务的输出形状不同:
| 任务 | 输出形状 | 说明 |
|---|---|---|
| 检测 | (1, 300, 6) |
[x1, y1, x2, y2, conf, class_id] |
| 分割 | (1, 300, 38) + (1, 32, 160, 160) |
6 个框值 + 32 个掩码系数,外加原型掩码张量 |
| 姿态 | (1, 300, 57) |
6 个框值 + 17 关键点 × 3(x, y, 可见性) |
| OBB | (1, 300, 7) |
6 个框值 + 1 个旋转角 |
如何确认导出的模型是否端到端?
可以通过 Ultralytics Python API 或导出 ONNX 的元数据检查:
from ultralytics import YOLO
model = YOLO("yolo26n.onnx")
model.predict(verbose=False) # run predict to setup predictor first
print(model.predictor.model.end2end) # True if end-to-end is enabled
import onnxruntime as ort
session = ort.InferenceSession("yolo26n.onnx")
metadata = session.get_modelmeta().custom_metadata_map
print(metadata.get("end2end")) # 'True' if end-to-end is enabled
导出器会把 end2end 状态写入导出模型元数据(exporter.py:"end2end": getattr(model, "end2end", False))。两种检查回答的是不同问题:ONNX 元数据记录的是导出时使用的头,而 predictor.model.end2end 报告后端输出是否已含后处理(无需外部 NMS)。用 end2end=False, nms=True 导出的模型会同时输出 (1, 300, 6)——因为 NMS 被烘焙进了图——此时单看标志或输出形状都无法唯一判定所用头。
实例分割、姿态和 OBB 支持端到端吗?
支持。YOLO26 的四种检测类任务变体——检测、实例分割、姿态估计、OBB——默认支持端到端推理,且都提供 end2end=False 回退。各任务在基础检测输出上追加任务专属数据,yolo26n.pt、yolo26n-seg.pt、yolo26n-pose.pt、yolo26n-obb.pt 的输出形状见上文"双头架构"一节的表格。
小结
- 端到端检测是 YOLO26 的默认行为:使用 Ultralytics API/CLI 时零改动即可获益;只有自定义后处理管线需要适配
(N, 300, 6)的xyxy新输出并删除 NMS 步骤。 - 绝大多数导出格式原生保留端到端输出;NCNN、RKNN、PaddlePaddle、ExecuTorch、IMX、Edge TPU、QNN 会自动回退 one-to-many 头,端侧需自备 NMS;TensorRT 与 LiteRT 在特定版本/量化条件下也会自动禁用端到端分支并打印警告。
- 端到端头相对 one-to-many 头在 COCO 上的精度代价仅 0.6–0.8 mAP,换来的是部署管线简化与推理提速(YOLO26n 的 CPU ONNX 推理最高快 43%);精度优先场景用
end2end=False回退。 end2end与nms互斥;max_det控制烘焙进导出图的 top-k 上限;Hailo 则从模型头本身判定输出路径、不接受end2end参数。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00