Compreendendo a detecção end-to-end no Ultralytics YOLO26

Introdução

Se você está migrando para o YOLO26 a partir de um modelo anterior como o YOLOv8 ou o YOLO11, uma das maiores mudanças que você notará é a remoção do Non-Maximum Suppression (NMS). Os modelos YOLO tradicionais produzem milhares de predições sobrepostas que precisam de uma etapa de pós-processamento de NMS separada para filtrar as detecções finais. Isso adiciona latência, complica os gráficos de exportação e pode ter um comportamento inconsistente entre diferentes plataformas de hardware.

O YOLO26 adota uma abordagem diferente. Ele gera as detecções finais diretamente do modelo — sem necessidade de filtragem externa. Isso é conhecido como detecção de objetos end-to-end, e está ativado por padrão em todos os modelos YOLO26. O resultado é um pipeline de implantação mais simples, menor latência e até 43% mais rápido na inferência em CPUs.

Este guia explica o que mudou, se você precisa atualizar seu código, quais formatos de exportação suportam inferência end-to-end e como migrar suavemente a partir de modelos YOLO mais antigos.

Para uma análise mais profunda sobre a motivação por trás dessa mudança arquitetural, consulte a postagem no blog da Ultralytics sobre por que o YOLO26 remove o NMS e como isso altera a implantação.

Resumo Rápido
  • Usando a API ou CLI da Ultralytics? Nenhuma mudança é necessária — basta trocar o nome do seu modelo para yolo26n.pt.
  • Usando código de inferência personalizado (ONNX Runtime, TensorRT, etc.)? Atualize seu pós-processamento — a saída de detecção agora é (N, 300, 6) no formato xyxy, sem necessidade de NMS. Outras tarefas anexam dados extras (coeficientes de máscara, keypoints ou ângulo).
  • Exportando? A maioria dos formatos suporta a saída end-to-end nativamente. No entanto, alguns formatos (NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX e Edge TPU) retrocedem automaticamente para a saída tradicional devido a restrições de operadores não suportados (ex: torch.topk).

Como Funciona a Detecção End-to-End

O YOLO26 usa uma arquitetura de cabeça dupla durante o treinamento. Ambas as cabeças compartilham o mesmo backbone e neck, mas produzem saídas de maneiras diferentes:

CabeçaObjetivoSaída de DetecçãoPós-Processamento
One-to-One (padrão)Inferência end-to-end(N, 300, 6)Apenas limiar de confiança
One-to-ManySaída YOLO tradicional(N, nc + 4, 8400)Requer NMS

Os formatos acima são para detecção. Outras tarefas estendem a saída one-to-one com dados adicionais por detecção:

TarefaSaída End-to-EndDados Extras
Detecção(N, 300, 6)
Segmentação(N, 300, 6 + nm) + proto (N, nm, H, W)nm coeficientes de máscara (padrão 32)
Pose(N, 300, 57)17 keypoints × 3 (x, y, visibilidade)
OBB(N, 300, 7)Ângulo de rotação

During training, both heads run simultaneously — the one-to-many head provides a richer learning signal, while the one-to-one head learns to produce clean, non-overlapping predictions. During inference and export, only the one-to-one head is active by default, producing up to 300 detections per image in the format [x1, y1, x2, y2, confidence, class_id].

Quando você chama model.fuse(), ele combina as camadas Conv + BatchNorm para uma inferência mais rápida e, em modelos end-to-end, também remove a cabeça one-to-many — reduzindo o tamanho do modelo e os FLOPs. Para mais detalhes sobre a arquitetura de cabeça dupla, veja a página do modelo YOLO26.

Preciso Alterar Meu Código?

Usando a API Python ou CLI da Ultralytics

Nenhuma alteração é necessária. Se você usa a API Python padrão da Ultralytics ou CLI, tudo funciona automaticamente — predição, validação e exportação lidam com modelos end-to-end nativamente.

Nenhuma alteração de código necessária com a API Ultralytics
from ultralytics import YOLO

# Load a YOLO26 model
model = YOLO("yolo26n.pt")

# Predict — no NMS step, no code changes
results = model.predict("image.jpg")

Usando Código de Inferência Personalizado

Sim, o formato de saída é diferente. Se você escreveu uma lógica de pós-processamento personalizada para o YOLOv8 ou YOLO11 (por exemplo, ao executar inferência com ONNX Runtime ou TensorRT), você precisará atualizá-la para lidar com o novo formato de saída:

YOLOv8 / YOLO11YOLO26 (end-to-end)
Saída de detecção(N, nc + 4, 8400)(N, 300, 6)
Formato da caixaxywh (centro x, centro y, largura, altura)xyxy (superior-esquerdo x, superior-esquerdo y, inferior-direito x, inferior-direito y)
LayoutCoordenadas da caixa + pontuações de classe por âncora[x1, y1, x2, y2, conf, class_id]
NMS necessárioSimNão
Pós-processamentoNMS + filtro de confiançaApenas filtro de confiança

Para tarefas de segmentação, pose e OBB, o YOLO26 anexa dados específicos da tarefa a cada detecção — veja a tabela de formatos de saída acima.

Onde N é o batch size e nc é o número de classes (ex: 80 para COCO).

Com modelos end-to-end, o pós-processamento torna-se muito mais simples — por exemplo, ao usar o 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!

Alternando para a Cabeça One-to-Many

Se você precisa do formato de saída YOLO tradicional (por exemplo, para reutilizar o código de pós-processamento baseado em NMS existente), você pode alternar para a cabeça one-to-many a qualquer momento configurando end2end=False:

Usando a cabeça one-to-many para saída tradicional baseada em NMS
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)

Compatibilidade de Formato de Exportação

A maioria dos formatos de exportação suporta inferência end-to-end nativamente, incluindo ONNX, TensorRT, CoreML, OpenVINO, TFLite, TF.js e MNN.

Os seguintes formatos não suportam end-to-end e retrocedem automaticamente para a cabeça one-to-many: NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX e Edge TPU.

O que acontece quando o end-to-end não é suportado

Ao exportar para um desses formatos, a Ultralytics alterna automaticamente para a cabeça one-to-many e registra um aviso — sem necessidade de intervenção manual. Isso significa que você precisará de NMS em seu pipeline de inferência para esses formatos, assim como no YOLOv8 ou YOLO11.

TensorRT + INT8

O TensorRT suporta end-to-end, mas é desativado automaticamente ao exportar com int8=True no TensorRT ≤10.3.0.

Compensações de Precisão e Velocidade

A detecção end-to-end oferece benefícios significativos de implantação com impacto mínimo na precisão:

MétricaEnd-to-End (padrão)One-to-Many + NMS (end2end=False)
Velocidade de Inferência em CPUAté 43% mais rápidoLinha de base
Impacto no mAP~0.5 mAP menorIguala ou supera o YOLO11
Pós-ProcessamentoApenas filtro de confiançaPipeline de NMS completo
Complexidade de ImplantaçãoMínimaRequer implementação de NMS

Para a maioria das aplicações do mundo real, a diferença de ~0.5 mAP é insignificante, especialmente ao considerar os ganhos de velocidade e simplicidade. Se a precisão máxima for sua prioridade, você sempre pode retroceder para a cabeça one-to-many usando end2end=False.

Veja as métricas de desempenho do YOLO26 para benchmarks detalhados em todos os tamanhos de modelo (n, s, m, l, x).

Migrando do YOLOv8 ou YOLO11

Se você está atualizando um projeto existente para o YOLO26, aqui está uma lista de verificação rápida para garantir uma transição tranquila:

  • Usuários da API / CLI Ultralytics: Nenhuma mudança necessária — apenas atualize o nome do modelo para yolo26n.pt (ou yolo26n-seg.pt, yolo26n-pose.pt, yolo26n-obb.pt)
  • Código de pós-processamento personalizado: Atualize para lidar com os novos formatos de saída — (N, 300, 6) para detecção, além de dados específicos da tarefa para segmentação, pose e OBB. Observe também a mudança no formato da caixa de xywh para xyxy
  • Pipelines de exportação: Verifique a seção de compatibilidade de formato acima para o seu formato de destino
  • TensorRT + INT8: Verifique se sua versão do TensorRT é >10.3.0 para suporte a end-to-end
  • Exportações FP16: Se você precisa de todas as saídas em FP16, exporte com end2end=False — veja por que output0 permanece FP32
  • iOS / CoreML: End-to-end é totalmente suportado. Se precisar de suporte ao Xcode Preview, use end2end=False com nms=True
  • Dispositivos de borda (NCNN, RKNN): Esses formatos retrocedem automaticamente para one-to-many, então inclua o NMS em seu pipeline no dispositivo

FAQ

Posso usar end2end=True e nms=True juntos?

Não. Essas opções são mutuamente exclusivas. Se você configurar nms=True em um modelo end-to-end durante a exportação, ele será forçado automaticamente para nms=False com um aviso. A cabeça end-to-end já lida com a filtragem de duplicatas internamente, então o NMS externo é desnecessário.

No entanto, end2end=False combinado com nms=True é uma configuração válida — isso integra o NMS tradicional no gráfico de exportação. Isso pode ser útil para exportações CoreML porque permite que você use a função Preview no Xcode diretamente com o modelo de detecção.

O que o parâmetro max_det controla em modelos end-to-end?

O parâmetro max_det (padrão: 300) define o número máximo de detecções que a cabeça one-to-one pode produzir por imagem. Você pode ajustá-lo no momento da inferência ou da exportação:

model.predict("image.jpg", max_det=100)  # fewer detections, slightly faster
model.export(format="onnx", max_det=500)  # more detections for dense scenes

Observe que os checkpoints padrão do YOLO26 foram treinados com max_det=300. Embora você possa aumentar esse valor, a cabeça one-to-one foi otimizada durante o treinamento para produzir até 300 detecções limpas, portanto, detecções além desse limite podem ter qualidade inferior. Se você precisar de mais de 300 detecções por imagem, considere treinar novamente com um valor de max_det maior.

Meu modelo ONNX exportado produz (1, 300, 6) — isso está correto?

Yes, that's the expected end-to-end output format for detection: batch size of 1, up to 300 detections, each with 6 values [x1, y1, x2, y2, confidence, class_id]. Simply filter by confidence threshold and you're done — no NMS needed.

Para outras tarefas, o formato de saída difere:

TarefaFormato de SaídaDescrição
Detecção(1, 300, 6)[x1, y1, x2, y2, conf, class_id]
Segmentação(1, 300, 38) + (1, 32, 160, 160)6 valores de BBox + 32 coeficientes de máscara, além de um tensor de máscara protótipo
Pose(1, 300, 57)6 valores de BBox + 17 keypoints × 3 (x, y, visibilidade)
OBB(1, 300, 7)6 valores de BBox + 1 ângulo de rotação

Como verifico se o meu modelo exportado é end-to-end?

Podes verificar usando a API Python do Ultralytics ou inspecionando diretamente os metadados do modelo ONNX exportado:

Verificar se um modelo é end-to-end
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

Alternativamente, verifica a forma de saída (output shape) — modelos de deteção end-to-end devolvem (1, 300, 6), enquanto modelos tradicionais devolvem (1, nc + 4, 8400). Para outras formas de tarefas, consulta as perguntas frequentes sobre formas de saída.

O modo end-to-end é suportado para tarefas de segmentação, pose e OBB?

Sim. Todas as variantes de tarefas do YOLO26 — deteção, segmentação, estimativa de pose e deteção de objetos orientados (OBB) — suportam inferência end-to-end por padrão. O fallback end2end=False também está disponível em todas as tarefas.

Cada tarefa estende a saída de deteção base com dados específicos da tarefa:

TarefaModeloSaída End-to-End
Detecçãoyolo26n.pt(N, 300, 6)
Segmentaçãoyolo26n-seg.pt(N, 300, 38) + proto (N, 32, 160, 160)
Poseyolo26n-pose.pt(N, 300, 57)
OBByolo26n-obb.pt(N, 300, 7)

Comentários