Понимание сквозного обнаружения в Ultralytics YOLO26
Введение
Если ты переходишь на YOLO26 с более ранней модели, такой как YOLOv8 или YOLO11, одно из самых значительных изменений, которое ты заметишь, — это удаление Non-Maximum Suppression (NMS). Традиционные модели YOLO создают тысячи перекрывающихся предсказаний, для фильтрации которых до финальных результатов требуется отдельный этап постобработки NMS. Это увеличивает задержку, усложняет графы экспорта и может приводить к нестабильной работе на различных аппаратных платформах.
В YOLO26 используется другой подход. Модель выдает финальные результаты обнаружения напрямую — внешняя фильтрация не требуется. Это называется сквозным object detection, и эта функция включена по умолчанию во всех моделях YOLO26. Результатом является упрощенный конвейер развертывания, меньшая задержка и до 43% более быстрый вывод на CPU.
Это руководство поможет тебе разобраться, что изменилось, нужно ли обновлять код, какие форматы экспорта поддерживают сквозной вывод, и как плавно перейти со старых моделей YOLO.
Чтобы глубже изучить мотивацию этого архитектурного сдвига, ознакомься с постом в блоге Ultralytics о том, почему YOLO26 удаляет NMS и как это меняет развертывание.
- Используешь Ultralytics API или CLI? Изменения не нужны — просто смени название модели на
yolo26n.pt. - Используешь собственный код для вывода (ONNX Runtime, TensorRT и т.д.)? Обнови постобработку — вывод обнаружения теперь имеет формат
(N, 300, 6)в видеxyxy, NMS не требуется. Другие задачи добавляют дополнительные данные (коэффициенты масок, ключевые точки или угол). - Экспортируешь? Большинство форматов поддерживают сквозной вывод нативно. Однако некоторые форматы (NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX и Edge TPU) автоматически возвращаются к традиционному выводу из-за ограничений неподдерживаемых операторов (например,
torch.topk).
Как работает сквозное обнаружение
YOLO26 использует двухголовую архитектуру во время обучения. Обе головы разделяют один и тот же бэкбон и нек, но формируют выходы по-разному:
| Голова | Цель | Вывод обнаружения | Постобработка |
|---|---|---|---|
| One-to-One (по умолчанию) | Сквозной вывод | (N, 300, 6) | Только порог уверенности |
| One-to-Many | Традиционный вывод YOLO | (N, nc + 4, 8400) | Требует NMS |
Указанные выше формы относятся к обнаружению. Другие задачи расширяют вывод 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].
Когда ты вызываешь model.fuse(), это сворачивает слои Conv + BatchNorm для более быстрого инференса, а в сквозных моделях также удаляет голову one-to-many, уменьшая размер модели и количество операций FLOP. Более подробную информацию о двухголовой архитектуре смотри на странице модели YOLO26.
Нужно ли мне менять свой код?
Использование Ultralytics Python API или CLI
Никаких изменений не требуется. Если ты используешь стандартный Ultralytics Python API или 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 добавляет специфические для задачи данные к каждому обнаружению — смотри таблицу форм вывода выше.
Где N — это размер пакета, а nc — количество классов (например, 80 для COCO).
Со сквозными моделями постобработка становится намного проще — например, при использовании 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] > conf_threshold] # confidence filter — that's it!Переключение на голову One-to-Many
Если тебе нужен традиционный формат вывода 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)Совместимость форматов экспорта
Большинство форматов экспорта поддерживают сквозной вывод «из коробки», включая ONNX, TensorRT, CoreML, OpenVINO, TFLite, TF.js и MNN.
Следующие форматы не поддерживают сквозной режим и автоматически возвращаются к голове one-to-many: NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX и Edge TPU.
TensorRT поддерживает сквозной режим, но он автоматически отключается при экспорте с int8=True в TensorRT ≤10.3.0.
Компромиссы точности и скорости
Сквозное обнаружение дает значительные преимущества при развертывании с минимальным влиянием на точность:
| Метрика | Сквозной (по умолчанию) | One-to-Many + NMS (end2end=False) |
|---|---|---|
| Скорость вывода на CPU | До 43% быстрее | Базовый уровень |
| Влияние на mAP | ~0.5 mAP ниже | Соответствует или превосходит YOLO11 |
| Постобработка | Только фильтр уверенности | Полный конвейер NMS |
| Сложность развертывания | Минимальная | Требует реализации NMS |
Для большинства реальных приложений разница в ~0.5 mAP незначительна, особенно при учете выигрыша в скорости и простоте. Если максимальная точность — твой главный приоритет, ты всегда можешь вернуться к голове one-to-many, используя end2end=False.
Смотри показатели производительности YOLO26 для подробных бенчмарков по всем размерам моделей (n, s, m, l, x).
Миграция с YOLOv8 или YOLO11
Если ты обновляешь существующий проект до YOLO26, вот краткий чек-лист для обеспечения плавного перехода:
- Пользователи Ultralytics API / CLI: Изменения не требуются — просто обнови имя модели до
yolo26n.pt(илиyolo26n-seg.pt,yolo26n-pose.pt,yolo26n-obb.pt) - Собственный код постобработки: Обнови для обработки новых форм вывода —
(N, 300, 6)для обнаружения, плюс специфические для задачи данные для сегментации, позы и OBB. Также обрати внимание на изменение формата бокса сxywhнаxyxy - Конвейеры экспорта: Проверь раздел совместимости форматов выше для твоего целевого формата
- TensorRT + INT8: Убедись, что твоя версия TensorRT >10.3.0 для поддержки сквозного режима
- Экспорт FP16: Если тебе нужны все выходы в FP16, экспортируй с
end2end=False— смотри почему 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 с предупреждением. Сквозная голова уже выполняет фильтрацию дубликатов внутри себя, поэтому внешняя NMS не нужна.
Однако end2end=False в сочетании с nms=True является допустимой конфигурацией — она встраивает традиционную NMS в граф экспорта. Это может быть полезно для экспорта CoreML, так как позволяет напрямую использовать функцию Preview в Xcode с моделью обнаружения.
Что контролирует параметр max_det в сквозных моделях?
Параметр max_det (по умолчанию: 300) устанавливает максимальное количество обнаружений, которое голова one-to-one может выдать на изображение. Ты можешь настроить его во время инференса или экспорта:
model.predict("image.jpg", max_det=100) # fewer detections, slightly faster
model.export(format="onnx", max_det=500) # more detections for dense scenesОбрати внимание, что стандартные чекпоинты YOLO26 были обучены с max_det=300. Хотя ты можешь увеличить это значение, голова one-to-one была оптимизирована во время обучения для создания до 300 чистых обнаружений, поэтому обнаружения сверх этого предела могут быть более низкого качества. Если тебе нужно более 300 обнаружений на изображение, рассмотри переобучение с более высоким значением max_det.
Моя экспортированная модель 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 коэффициента маски, плюс тензор прототипа маски |
| Pose | (1, 300, 57) | 6 значений рамки + 17 ключевых точек × 3 (x, y, видимость) |
| OBB | (1, 300, 7) | 6 значений рамки + 1 угол поворота |
Как проверить, является ли моя экспортированная модель end-to-end?
Ты можешь проверить это с помощью Python API Ultralytics или напрямую изучив метаданные экспортированной модели 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Альтернативно, проверь размерность выходных данных — модели end-to-end детектирования выдают (1, 300, 6), тогда как традиционные модели выдают (1, nc + 4, 8400). Для размеров выходных данных других задач см. часто задаваемые вопросы о размерах выходных данных.
Поддерживается ли end-to-end для задач сегментации, оценки позы и OBB?
Да. Все варианты задач YOLO26 — детектирование, сегментация, оценка позы и ориентированное детектирование объектов (OBB) — поддерживают end-to-end инференс по умолчанию. Резервный вариант end2end=False также доступен для всех задач.
Каждая задача расширяет базовый вывод детектирования данными, специфичными для задачи:
| Задача | Модель | Сквозной вывод |
|---|---|---|
| Обнаружение | yolo26n.pt | (N, 300, 6) |
| Сегментация | yolo26n-seg.pt | (N, 300, 38) + proto (N, 32, 160, 160) |
| Pose | yolo26n-pose.pt | (N, 300, 57) |
| OBB | yolo26n-obb.pt | (N, 300, 7) |