Comprendere il rilevamento end-to-end in Ultralytics YOLO26

Introduzione

Se stai passando a YOLO26 da un modello precedente come YOLOv8 o YOLO11, uno dei cambiamenti più significativi che noterai è la rimozione della Non-Maximum Suppression (NMS). I modelli YOLO tradizionali producono migliaia di previsioni sovrapposte che necessitano di un passaggio di post-elaborazione NMS separato per filtrare i rilevamenti finali. Questo aggiunge latenza, complica i grafici di esportazione e può comportarsi in modo incoerente su diverse piattaforme hardware.

YOLO26 adotta un approccio diverso. Esegue i rilevamenti finali direttamente dal modello: non è richiesto alcun filtraggio esterno. Questo è noto come rilevamento oggetti end-to-end ed è abilitato per impostazione predefinita in tutti i modelli YOLO26. Il risultato è una pipeline di distribuzione più semplice, una latenza inferiore e un'inferenza fino al 43% più veloce su CPU.

Questa guida ti illustra cosa è cambiato, se devi aggiornare il tuo codice, quali formati di esportazione supportano l'inferenza end-to-end e come migrare senza problemi dai vecchi modelli YOLO.

Per uno sguardo più approfondito alle motivazioni dietro questo cambiamento architettonico, consulta il post del blog di Ultralytics sul perché YOLO26 rimuove la NMS e come questo cambia la distribuzione.

Riepilogo rapido
  • Utilizzi l'API o la CLI di Ultralytics? Non sono necessarie modifiche: sostituisci semplicemente il nome del tuo modello con yolo26n.pt.
  • Utilizzi un codice di inferenza personalizzato (ONNX Runtime, TensorRT, ecc.)? Aggiorna la tua post-elaborazione: l'output di rilevamento ora è (N, 300, 6) nel formato xyxy, senza NMS richiesta. Altri task aggiungono dati extra (coefficienti di maschera, keypoint o angolo).
  • Esportazione? La maggior parte dei formati supporta l'output end-to-end in modo nativo. Tuttavia, alcuni formati (NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX e Edge TPU) tornano automaticamente all'output tradizionale a causa di vincoli degli operatori non supportati (es. torch.topk).

Come funziona il rilevamento end-to-end

YOLO26 utilizza un'architettura a doppia testa durante l'addestramento. Entrambe le teste condividono lo stesso backbone e collo, ma producono output in modi diversi:

TestaScopoOutput di rilevamentoPost-elaborazione
One-to-One (predefinito)Inferenza end-to-end(N, 300, 6)Solo soglia di confidenza
One-to-ManyOutput YOLO tradizionale(N, nc + 4, 8400)Richiede NMS

Le forme sopra indicate sono per il rilevamento. Altri task estendono l'output one-to-one con dati aggiuntivi per ogni rilevamento:

AttivitàOutput end-to-endDati extra
Rilevamento(N, 300, 6)
Segmentazione(N, 300, 6 + nm) + proto (N, nm, H, W)Coefficienti di maschera nm (predefinito 32)
Posa(N, 300, 57)17 keypoint × 3 (x, y, visibilità)
OBB(N, 300, 7)Angolo di rotazione

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 chiami model.fuse(), vengono unite i livelli Conv + BatchNorm per un'inferenza più veloce e, sui modelli end-to-end, viene rimossa anche la testa one-to-many, riducendo le dimensioni del modello e i FLOP. Per maggiori dettagli sull'architettura a doppia testa, consulta la pagina del modello YOLO26.

Devo modificare il mio codice?

Utilizzando l'API Python o la CLI di Ultralytics

Non sono necessarie modifiche. Se utilizzi l' API Python di Ultralytics o la CLI standard, tutto funziona automaticamente: previsione, validazione ed esportazione gestiscono tutti i modelli end-to-end fin da subito.

Nessuna modifica al codice richiesta con l'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")

Utilizzando codice di inferenza personalizzato

Sì, il formato di output è diverso. Se hai scritto una logica di post-elaborazione personalizzata per YOLOv8 o YOLO11 (ad esempio, quando esegui l'inferenza con ONNX Runtime o TensorRT), dovrai aggiornarla per gestire la nuova forma di output:

YOLOv8 / YOLO11YOLO26 (end-to-end)
Output di rilevamento(N, nc + 4, 8400)(N, 300, 6)
Formato bboxxywh (centro x, centro y, larghezza, altezza)xyxy (x in alto a sinistra, y in alto a sinistra, x in basso a destra, y in basso a destra)
LayoutCoordinate bbox + punteggi classe per anchor[x1, y1, x2, y2, conf, class_id]
NMS richiestaNo
Post-elaborazioneNMS + filtro di confidenzaSolo filtro di confidenza

Per i task di segmentazione, pose e OBB, YOLO26 aggiunge dati specifici del task a ogni rilevamento: vedi la tabella delle forme di output qui sopra.

Dove N è la batch size e nc è il numero di classi (es. 80 per COCO).

Con i modelli end-to-end, la post-elaborazione diventa molto più semplice; ad esempio, quando utilizzi 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!

Passaggio alla testa One-to-Many

Se hai bisogno del formato di output YOLO tradizionale (ad esempio, per riutilizzare il codice di post-elaborazione esistente basato su NMS), puoi passare alla testa one-to-many in qualsiasi momento impostando end2end=False:

Utilizzo della testa one-to-many per l'output tradizionale basato su 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)

Compatibilità del formato di esportazione

La maggior parte dei formati di esportazione supporta l'inferenza end-to-end fin da subito, inclusi ONNX, TensorRT, CoreML, OpenVINO, TFLite, TF.js e MNN.

I seguenti formati non supportano end-to-end e tornano automaticamente alla testa one-to-many: NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX e Edge TPU.

Cosa succede quando end-to-end non è supportato

Quando esporti in uno di questi formati, Ultralytics passa automaticamente alla testa one-to-many e registra un avviso: non è necessario alcun intervento manuale. Ciò significa che avrai bisogno della NMS nella tua pipeline di inferenza per questi formati, proprio come con YOLOv8 o YOLO11.

TensorRT + INT8

TensorRT supporta end-to-end, ma è disabilitato automaticamente quando si esporta con int8=True su TensorRT ≤10.3.0.

Compromessi tra accuratezza e velocità

Il rilevamento end-to-end offre significativi vantaggi di distribuzione con un impatto minimo sull'accuratezza:

MetricaEnd-to-End (predefinito)One-to-Many + NMS (end2end=False)
Velocità di inferenza CPUFino al 43% più veloceBaseline
Impatto mAP~0.5 mAP inferioreEguaglia o supera YOLO11
Post-elaborazioneSolo filtro di confidenzaPipeline NMS completa
Complessità di distribuzioneMinimaRichiede l'implementazione di NMS

Per la maggior parte delle applicazioni del mondo reale, la differenza di ~0.5 mAP è trascurabile, specialmente se si considerano i guadagni in termini di velocità e semplicità. Se la massima accuratezza è la tua priorità assoluta, puoi sempre tornare alla testa one-to-many utilizzando end2end=False.

Consulta le metriche di prestazione di YOLO26 per benchmark dettagliati su tutte le dimensioni del modello (n, s, m, l, x).

Migrazione da YOLOv8 o YOLO11

Se stai aggiornando un progetto esistente a YOLO26, ecco una rapida lista di controllo per garantire una transizione fluida:

  • Utenti API / CLI Ultralytics: Nessuna modifica necessaria: basta aggiornare il nome del modello a yolo26n.pt (o yolo26n-seg.pt, yolo26n-pose.pt, yolo26n-obb.pt)
  • Codice di post-elaborazione personalizzato: Aggiorna per gestire le nuove forme di output: (N, 300, 6) per il rilevamento, più dati specifici per il task per segmentazione, pose e OBB. Nota anche il cambio di formato bbox da xywh a xyxy
  • Pipeline di esportazione: Controlla la sezione compatibilità formato sopra per il tuo formato di destinazione
  • TensorRT + INT8: Verifica che la tua versione di TensorRT sia >10.3.0 per il supporto end-to-end
  • Esportazioni FP16: Se hai bisogno di tutti gli output in FP16, esporta con end2end=False: vedi perché output0 rimane FP32
  • iOS / CoreML: End-to-end è completamente supportato. Se hai bisogno del supporto Xcode Preview, usa end2end=False con nms=True
  • Dispositivi Edge (NCNN, RKNN): Questi formati tornano automaticamente a one-to-many, quindi includi NMS nella tua pipeline on-device

FAQ

Posso usare end2end=True e nms=True insieme?

No. Queste opzioni sono mutuamente esclusive. Se imposti nms=True su un modello end-to-end durante l'esportazione, verrà automaticamente forzato a nms=False con un avviso. La testa end-to-end gestisce già internamente il filtraggio dei duplicati, quindi una NMS esterna non è necessaria.

Tuttavia, end2end=False combinato con nms=True è una configurazione valida: integra la NMS tradizionale nel grafico di esportazione. Questo può essere utile per le esportazioni CoreML perché ti consente di utilizzare direttamente la funzione di anteprima in Xcode con il modello di rilevamento.

Cosa controlla il parametro max_det nei modelli end-to-end?

Il parametro max_det (predefinito: 300) imposta il numero massimo di rilevamenti che la testa one-to-one può produrre per immagine. Puoi regolarlo al momento dell'inferenza o dell'esportazione:

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

Nota che i checkpoint predefiniti di YOLO26 sono stati addestrati con max_det=300. Sebbene tu possa aumentare questo valore, la testa one-to-one è stata ottimizzata durante l'addestramento per produrre fino a 300 rilevamenti puliti, quindi i rilevamenti oltre tale limite potrebbero essere di qualità inferiore. Se hai bisogno di più di 300 rilevamenti per immagine, considera di riaddestrare con un valore max_det più alto.

Il mio modello ONNX esportato restituisce (1, 300, 6): è corretto?

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.

Per altri task, la forma di output differisce:

AttivitàForma di outputDescrizione
Rilevamento(1, 300, 6)[x1, y1, x2, y2, conf, class_id]
Segmentazione(1, 300, 38) + (1, 32, 160, 160)6 valori del box + 32 coefficienti di maschera, più un tensore di maschera prototipo
Pose(1, 300, 57)6 valori del box + 17 keypoint × 3 (x, y, visibilità)
OBB(1, 300, 7)6 valori del box + 1 angolo di rotazione

Come posso verificare se il mio modello esportato è end-to-end?

Puoi verificarlo utilizzando l'API Python di Ultralytics o ispezionando direttamente i metadati del modello ONNX esportato:

Verifica se un modello è 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

In alternativa, controlla la forma dell'output: i modelli di rilevamento end-to-end restituiscono (1, 300, 6), mentre i modelli tradizionali restituiscono (1, nc + 4, 8400). Per le forme di altri task, consulta le FAQ sulle forme di output.

L'end-to-end è supportato per i task di segmentazione, posa e OBB?

Sì. Tutte le varianti di task di YOLO26 — rilevamento, segmentazione, stima della posa e rilevamento di oggetti orientati (OBB) — supportano l'inferenza end-to-end per impostazione predefinita. Il fallback end2end=False è disponibile anche per tutti i task.

Ogni task estende l'output di rilevamento di base con dati specifici per il task:

AttivitàModelloOutput end-to-end
Rilevamentoyolo26n.pt(N, 300, 6)
Segmentazioneyolo26n-seg.pt(N, 300, 38) + proto (N, 32, 160, 160)
Poseyolo26n-pose.pt(N, 300, 57)
OBByolo26n-obb.pt(N, 300, 7)

Commenti