Понимание сквозного (end-to-end) детектирования в Ultralytics YOLO26#
Модели YOLO26 в стиле детекции — детекция, сегментация, поза и OBB — по умолчанию не требуют NMS: они выдают финальные результаты детекции напрямую из модели, без этапа постпроцессинга Non-Maximum Suppression (NMS). Более ранние модели, такие как YOLOv8 и YOLO11, выдают тысячи пересекающихся предсказаний, которые отдельный этап NMS должен отфильтровать, что увеличивает задержку, усложняет графы экспорта и может работать нестабильно на разных аппаратных платформах.
Это называется сквозным обнаружением объектов, и оно включено по умолчанию. Результатом является более простой конвейер развертывания и меньшая задержка — YOLO26n работает до на 43% быстрее, чем YOLO11n при инференсе ONNX на CPU (Intel Xeon CPU @ 2.00 GHz).
Это руководство поможет тебе разобраться в изменениях, подскажет, нужно ли обновлять код, какие форматы экспорта поддерживают сквозной инференс и как плавно перейти со старых моделей YOLO.
Подробнее о мотивации этого архитектурного изменения читайте в блоге Ultralytics о том, почему в YOLO26 убрали NMS.
- Используешь API или CLI от Ultralytics? Никаких изменений не требуется — просто замени имя модели на
yolo26n.pt. - Используешь собственный код инференса (ONNX Runtime, TensorRT и т. д.)? Обнови свою постобработку: вывод детекции теперь имеет формат
(N, 300, 6)в видеxyxy, NMS не требуется. Для других задач добавляются дополнительные данные (коэффициенты масок, ключевые точки или углы). - Экспортируешь? Большинство форматов сохраняют сквозной вывод нативно; некоторые возвращаются к традиционному выводу, а квантование может отключить его — см. Совместимость форматов экспорта.
Как работает сквозное детектирование#
Во время обучения YOLO26 использует архитектуру с двумя головами (dual-head). Обе головы используют общие бэкбон и нек, но выдают результаты по-разному:
| Голова | Цель | Выход детектирования | Постобработка |
|---|---|---|---|
| One-to-One (по умолчанию) | Сквозной инференс | (N, 300, 6) | Только порог уверенности |
| One-to-Many | Традиционный выход YOLO | (N, nc + 4, 8400) | Требует NMS |
Формы выше указаны для детекции, где N — это размер батча, nc — количество классов (например, 80 для COCO), а количество якорей 8400 — это значение в imgsz=640. Другие задачи расширяют вывод «один к одному» дополнительными данными для каждого обнаружения:
| Задача | Сквозной выход | Дополнительные данные |
|---|---|---|
| Детектирование | (N, 300, 6) | — |
| Сегментация экземпляров | (N, 300, 6 + nm) + прото (N, nm, H, W) | коэффициенты масок nm (по умолчанию 32) |
| Pose | (N, 300, 57) | 17 ключевых точек × 3 (x, y, видимость) |
| OBB | (N, 300, 7) | Угол поворота |
Во время обучения обе головы работают одновременно — голова один-ко-многим предоставляет более богатый сигнал для обучения, в то время как голова один-к-одному учится выдавать чистые непересекающиеся предсказания. Во время инференса и экспорта по умолчанию активна только голова один-к-одному, выдающая до 300 обнаружений на изображение в формате [x1, y1, x2, y2, confidence, class_id].
Когда ты вызываешь model.fuse(), слои Conv + BatchNorm сворачиваются для более быстрого инференса, а в сквозных моделях также удаляется голова один-ко-многим, что уменьшает размер модели и количество FLOPs. Подробнее об архитектуре с двумя головами см. на странице модели YOLO26.
Нужно ли мне менять свой код?#
Использование Python API или CLI от Ultralytics#
Никаких изменений не требуется. Если ты используешь стандартный Python API Ultralytics или CLI, все работает автоматически — предикты, валидация и экспорт работают со сквозными моделями «из коробки».
from ultralytics import YOLO
# Load a YOLO26 model
model = YOLO("yolo26n.pt")
# Predict — no NMS step, no code changes
results = model.predict("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) |
| Расположение | Координаты бокса + оценки классов для каждого анкора | [x1, y1, x2, y2, conf, class_id] |
| Требуется NMS | Да | Нет |
| Постобработка | NMS + фильтр уверенности | Только фильтр уверенности |
Для задач сегментации, позы и OBB YOLO26 добавляет специфичные для задачи данные к каждому обнаружению — см. таблицу размерностей выхода.
Со сквозными моделями постобработка становится намного проще — например, при использовании 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#
Если тебе нужен традиционный формат вывода YOLO (например, для повторного использования существующего кода постобработки на основе NMS), ты можешь переключиться на голову один-ко-многим (если она доступна), установив 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)Совместимость форматов экспорта#
Большинство форматов экспорта поддерживают сквозной инференс «из коробки», включая ONNX, TensorRT, CoreML, OpenVINO, LiteRT и MNN.
Следующие форматы не поддерживают сквозной режим и автоматически переключаются на голову один-ко-многим: NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX, Edge TPU и Qualcomm QNN.
Для Hailo экспортер выбирает путь вывода из загруженной головы, а не из аргумента, поэтому end2end отклоняется, если передан: модель детекции YOLO26 по умолчанию сохраняет свои выходы «один к одному» без NMS, в то время как контрольная точка, голова которой уже end2end=False, компилирует традиционный путь с HailoRT NMS.
Компромиссы между точностью и скоростью#
Сквозное обнаружение дает значительные преимущества при развертывании с минимальным влиянием на точность:
| Метрика | Сквозной (по умолчанию) | Один-ко-многим + NMS (end2end=False) |
|---|---|---|
| COCO mAPval | на 0.6-0.8 ниже | Базовая линия |
| Постобработка | Только фильтр уверенности | Полный конвейер NMS |
| Сложность развертывания | Минимальная | Требует реализации NMS |
Для пяти масштабов детекции голова «один к одному» стоит 0.6-0.8 mAP на COCO — от 40.9 до 40.1 для YOLO26n и от 57.5 до 56.9 для YOLO26x — в обмен на полный отказ от прохода NMS. Если максимальная точность является твоим приоритетом, вернись к голове «один ко многим» с end2end=False.
См. показатели производительности YOLO26 для подробных бенчмарков по всем размерам моделей (n, s, m, l, x).
Миграция с YOLOv8 или YOLO11#
Если ты обновляешь существующий проект до YOLO26:
- Пользователи API / CLI Ultralytics: Никаких изменений не требуется — просто обнови имя модели на
yolo26n.pt(илиyolo26n-seg.pt,yolo26n-pose.pt,yolo26n-obb.pt) - Собственный код постобработки: Обнови логику для работы с новыми размерностями выхода:
(N, 300, 6)для детекции, а также данные для конкретных задач сегментации, позы и OBB. Также обрати внимание на изменение формата боксов сxywhнаxyxy - Конвейеры экспорта: Проверь раздел совместимости форматов для целевого формата
- TensorRT ниже 8.5.0: сквозной режим отключен при любой точности — обнови TensorRT до версии 8.5.0 или новее, чтобы сохранить его
- Квантованные экспорты: TensorRT 10.3.0 с
quantize=8на JetPack 6 и LiteRT сquantize=8илиquantize="w8a16"автоматически отключают сквозной режим — экспортируй с более высокой точностью, чтобы сохранить его - Экспорт в FP16: Если тебе нужны все выходы в FP16, экспортируй с параметром
end2end=False— см., почему output0 остается в FP32 - iOS / CoreML: Сквозной режим поддерживается полностью. Если тебе нужна поддержка Xcode Preview, используй
end2end=Falseсnms=True - Edge-устройства (NCNN, RKNN): Эти форматы автоматически возвращаются к one-to-many, поэтому включи NMS в свой конвейер на устройстве
Заключение#
Сквозное обнаружение является дефолтным в YOLO26 и не требует изменения кода, если ты используешь Python API Ultralytics или CLI. Обновление требуется только для собственных конвейеров постобработки, чтобы считывать новый выход (N, 300, 6) и пропускать шаг NMS (за исключением форматов экспорта, которые возвращаются к выводу один-ко-многим, таких как NCNN и RKNN, где NMS на устройстве все еще необходим). Подробные бенчмарки скорости и точности для всех размеров моделей см. на странице модели YOLO26, а полный набор опций и форматов экспорта описан в документации по режиму экспорта.
FAQ#
Нет. Эти опции взаимоисключающие. Если ты установишь
nms=Trueдля сквозной модели во время экспорта, она будет автоматически принудительно переведена вnms=Falseс выдачей предупреждения. Сквозная голова уже сама по себе выполняет фильтрацию дубликатов внутри себя, поэтому внешний NMS не нужен.Однако комбинация
end2end=Falseиnms=Trueявляется допустимой конфигурацией — она встраивает традиционный NMS в граф экспорта. Это может быть полезно для экспорта в CoreML, так как позволяет напрямую использовать функцию Preview в Xcode с моделью детекции.Параметр
max_det(по умолчанию: 300) задает максимальное количество обнаружений, возвращаемых для одного изображения. Ты можешь изменить его во время инференса или экспорта: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), ограниченного количеством якорей.Да, это ожидаемый формат сквозного вывода для детекции: размер батча 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 угол поворота Ты можешь проверить это из Python API Ultralytics или из метаданных экспортированной модели ONNX:
Проверка модели на end-to-endfrom 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Эти две проверки отвечают на разные вопросы: метаданные ONNX фиксируют голову, которая была экспортирована, в то время как
predictor.model.end2endсообщает, что вывод бэкенда уже обработан и не требует внешней NMS. Они расходятся для модели, экспортированной сend2end=False, nms=True, которая использует голову «один ко многим», но зашивает NMS в граф, а также выводит(1, 300, 6)— поэтому ни флаг, ни форма вывода по отдельности не идентифицируют голову. Информацию о формах других задач см. в разделе часто задаваемых вопросов о формах вывода.Да. Варианты задач в стиле детекции YOLO26 — детекция, сегментация экземпляров, оценка позы и ориентированное обнаружение объектов (OBB) — поддерживают сквозной инференс по умолчанию. Фолбэк
end2end=Falseдоступен и для этих задач.Каждая задача расширяет базовый вывод детекции данными, специфичными для задачи; формы вывода
yolo26n.pt,yolo26n-seg.pt,yolo26n-pose.ptиyolo26n-obb.ptперечислены в разделе Как работает сквозная детекция.