Comprendre la détection de bout en bout dans Ultralytics YOLO26

Introduction

Si tu passes à YOLO26 depuis un modèle antérieur comme YOLOv8 ou YOLO11, l'un des changements les plus importants que tu remarqueras est la suppression du Non-Maximum Suppression (NMS). Les modèles YOLO traditionnels produisent des milliers de prédictions qui se chevauchent, nécessitant une étape de post-traitement NMS séparée pour filtrer jusqu'aux détections finales. Cela ajoute de la latence, complique les graphes d'exportation et peut se comporter de manière incohérente sur différentes plateformes matérielles.

YOLO26 adopte une approche différente. Il génère les détections finales directement depuis le modèle — aucun filtrage externe n'est requis. C'est ce qu'on appelle la détection d'objets de bout en bout, et elle est activée par défaut dans tous les modèles YOLO26. Le résultat est un pipeline de déploiement plus simple, une latence plus faible et jusqu'à 43 % d'inférence plus rapide sur CPU.

Ce guide t'explique ce qui a changé, si tu dois mettre à jour ton code, quels formats d'exportation prennent en charge l'inférence de bout en bout et comment migrer en douceur depuis les anciens modèles YOLO.

Pour un examen plus approfondi de la motivation derrière ce changement architectural, consulte le billet de blog Ultralytics sur les raisons pour lesquelles YOLO26 supprime le NMS.

Résumé rapide
  • Tu utilises l'API ou la CLI Ultralytics ? Aucun changement nécessaire — remplace simplement le nom de ton modèle par yolo26n.pt.
  • Tu utilises un code d'inférence personnalisé (ONNX Runtime, TensorRT, etc.) ? Mets à jour ton post-traitement — la sortie de détection est désormais (N, 300, 6) au format xyxy, aucun NMS requis. Les autres tâches ajoutent des données supplémentaires (coefficients de masque, points clés ou angle).
  • Tu exportes ? La plupart des formats prennent en charge la sortie de bout en bout nativement. Cependant, quelques formats (NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX et Edge TPU) reviennent automatiquement à la sortie traditionnelle en raison de contraintes d'opérateurs non pris en charge (par exemple, torch.topk).

Comment fonctionne la détection de bout en bout

YOLO26 utilise une architecture à double tête pendant l'entraînement. Les deux têtes partagent la même structure backbone et neck, mais produisent des sorties de manières différentes :

TêteObjectifSortie de détectionPost-traitement
Un-à-un (par défaut)Inférence de bout en bout(N, 300, 6)Seuil de confiance uniquement
Un-à-plusieursSortie YOLO traditionnelle(N, nc + 4, 8400)Nécessite NMS

Les formes ci-dessus concernent la détection. D'autres tâches étendent la sortie un-à-un avec des données supplémentaires par détection :

TâcheSortie de bout en boutDonnées supplémentaires
Détection(N, 300, 6)
Segmentation(N, 300, 6 + nm) + proto (N, nm, H, W)nm coefficients de masque (32 par défaut)
Pose(N, 300, 57)17 points clés × 3 (x, y, visibilité)
OBB(N, 300, 7)Angle de rotation

Pendant l'entraînement, les deux têtes fonctionnent simultanément — la tête un-à-plusieurs fournit un signal d'apprentissage plus riche, tandis que la tête un-à-un apprend à produire des prédictions propres et sans chevauchement. Pendant l'inférence et l'exportation, seule la tête un-à-un est active par défaut, produisant jusqu'à 300 détections par image au format [x1, y1, x2, y2, confidence, class_id].

Lorsque tu appelles model.fuse(), cela fusionne les couches Conv + BatchNorm pour une inférence plus rapide et, sur les modèles de bout en bout, supprime également la tête un-à-plusieurs — réduisant la taille du modèle et les FLOPs. Pour plus de détails sur l'architecture à double tête, consulte la page du modèle YOLO26.

Dois-je modifier mon code ?

Utiliser l'API Python ou la CLI Ultralytics

Aucun changement nécessaire. Si tu utilises l'API Python Ultralytics standard ou la CLI, tout fonctionne automatiquement — la prédiction, la validation et l'exportation gèrent toutes les modèles de bout en bout sans configuration supplémentaire.

Aucun changement de code requis avec 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")

Utiliser un code d'inférence personnalisé

Oui, le format de sortie est différent. Si tu as écrit une logique de post-traitement personnalisée pour YOLOv8 ou YOLO11 (par exemple, lors de l'exécution de l'inférence avec ONNX Runtime ou TensorRT), tu devras la mettre à jour pour gérer la nouvelle forme de sortie :

YOLOv8 / YOLO11YOLO26 (de bout en bout)
Sortie de détection(N, nc + 4, 8400)(N, 300, 6)
Format des boîtesxywh (x centre, y centre, largeur, hauteur)xyxy (x haut-gauche, y haut-gauche, x bas-droite, y bas-droite)
DispositionCoordonnées des boîtes + scores de classe par ancrage[x1, y1, x2, y2, conf, class_id]
NMS requisOuiNon
Post-traitementNMS + filtre de confianceFiltre de confiance uniquement

Pour les tâches de segmentation, pose et OBB, YOLO26 ajoute des données spécifiques à la tâche à chaque détection — consulte le tableau des formes de sortie ci-dessus.

N est la taille du batch et nc est le nombre de classes (par exemple, 80 pour COCO).

Avec les modèles de bout en bout, le post-traitement devient beaucoup plus simple — par exemple, lors de l'utilisation de 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!

Passer à la tête un-à-plusieurs

Si tu as besoin du format de sortie YOLO traditionnel (par exemple, pour réutiliser un code de post-traitement basé sur le NMS), tu peux passer à la tête un-à-plusieurs à tout moment en définissant end2end=False :

Utilisation de la tête un-à-plusieurs pour une sortie NMS traditionnelle
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é des formats d'exportation

La plupart des formats d'exportation prennent en charge l'inférence de bout en bout directement, notamment ONNX, TensorRT, CoreML, OpenVINO, TFLite, TF.js et MNN.

Les formats suivants ne prennent pas en charge le bout en bout et reviennent automatiquement à la tête un-à-plusieurs : NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX et Edge TPU.

Que se passe-t-il lorsque le bout en bout n'est pas pris en charge

Lorsque tu exportes vers l'un de ces formats, Ultralytics passe automatiquement à la tête un-à-plusieurs et enregistre un avertissement — aucune intervention manuelle n'est nécessaire. Cela signifie que tu auras besoin du NMS dans ton pipeline d'inférence pour ces formats, tout comme avec YOLOv8 ou YOLO11.

TensorRT + INT8

TensorRT prend en charge le bout en bout, mais il est désactivé automatiquement lors de l'exportation avec int8=True sur TensorRT ≤10.3.0.

Compromis entre précision et vitesse

La détection de bout en bout offre des avantages de déploiement significatifs avec un impact minimal sur la précision :

MétriqueDe bout en bout (par défaut)Un-à-plusieurs + NMS (end2end=False)
Vitesse d'inférence CPUJusqu'à 43 % plus rapideBaseline
Impact sur mAP~0,5 mAP de moinsÉgal ou dépasse YOLO11
Post-traitementFiltre de confiance uniquementPipeline NMS complet
Complexité de déploiementMinimaleNécessite l'implémentation du NMS

Pour la plupart des applications réelles, la différence de ~0,5 mAP est négligeable, surtout si l'on considère les gains de vitesse et de simplicité. Si une précision maximale est ta priorité absolue, tu peux toujours revenir à la tête un-à-plusieurs en utilisant end2end=False.

Consulte les métriques de performance YOLO26 pour des benchmarks détaillés sur toutes les tailles de modèles (n, s, m, l, x).

Migration depuis YOLOv8 ou YOLO11

Si tu mets à jour un projet existant vers YOLO26, voici une liste de contrôle rapide pour assurer une transition en douceur :

  • Utilisateurs de l'API / CLI Ultralytics : Aucun changement nécessaire — mets simplement à jour le nom du modèle par yolo26n.pt (ou yolo26n-seg.pt, yolo26n-pose.pt, yolo26n-obb.pt)
  • Code de post-traitement personnalisé : Mets à jour pour gérer les nouvelles formes de sortie — (N, 300, 6) pour la détection, plus les données spécifiques à la tâche pour la segmentation, la pose et l'OBB. Note également le changement de format de boîte de xywh vers xyxy
  • Pipelines d'exportation : Vérifie la section compatibilité des formats ci-dessus pour ton format cible
  • TensorRT + INT8 : Vérifie que ta version de TensorRT est >10.3.0 pour la prise en charge du bout en bout
  • Exportations FP16 : Si tu as besoin de toutes les sorties en FP16, exporte avec end2end=False — vois pourquoi output0 reste en FP32
  • iOS / CoreML : Le bout en bout est entièrement pris en charge. Si tu as besoin de la prise en charge de l'aperçu Xcode, utilise end2end=False avec nms=True
  • Appareils Edge (NCNN, RKNN) : Ces formats reviennent automatiquement à l'un-à-plusieurs, donc inclus le NMS dans ton pipeline sur appareil

FAQ

Puis-je utiliser end2end=True et nms=True ensemble ?

Non. Ces options sont mutuellement exclusives. Si tu définis nms=True sur un modèle de bout en bout pendant l'exportation, il sera automatiquement forcé à nms=False avec un avertissement. La tête de bout en bout gère déjà le filtrage des doublons en interne, le NMS externe est donc inutile.

Cependant, end2end=False combiné avec nms=True est une configuration valide — cela intègre le NMS traditionnel dans le graphe d'exportation. Cela peut être utile pour les exportations CoreML car cela te permet d'utiliser la fonction Aperçu dans Xcode avec le modèle de détection directement.

Que contrôle le paramètre max_det dans les modèles de bout en bout ?

Le paramètre max_det (par défaut : 300) définit le nombre maximum de détections que la tête un-à-un peut générer par image. Tu peux l'ajuster au moment de l'inférence ou de l'exportation :

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

Note que les points de contrôle YOLO26 par défaut ont été entraînés avec max_det=300. Bien que tu puisses augmenter cette valeur, la tête un-à-un a été optimisée pendant l'entraînement pour produire jusqu'à 300 détections propres, donc les détections au-delà de cette limite peuvent être de moindre qualité. Si tu as besoin de plus de 300 détections par image, envisage de réentraîner avec une valeur max_det plus élevée.

Mon modèle ONNX exporté sort (1, 300, 6) — est-ce correct ?

Oui, c'est le format de sortie attendu pour la détection de bout en bout : une taille de batch de 1, jusqu'à 300 détections, chacune avec 6 valeurs [x1, y1, x2, y2, confidence, class_id]. Filtre simplement par seuil de confiance et le tour est joué — aucun NMS n'est nécessaire.

Pour les autres tâches, la forme de sortie diffère :

TâcheForme de sortieDescription
Détection(1, 300, 6)[x1, y1, x2, y2, conf, class_id]
Segmentation(1, 300, 38) + (1, 32, 160, 160)6 valeurs de boîte + 32 coefficients de masque, plus un tenseur de masque prototype
Pose(1, 300, 57)6 valeurs de boîte + 17 points clés × 3 (x, y, visibilité)
OBB(1, 300, 7)6 valeurs de boîte + 1 angle de rotation

Comment vérifier si mon modèle exporté est end-to-end ?

Tu peux vérifier en utilisant l'API Python Ultralytics ou en inspectant directement les métadonnées du modèle ONNX exporté :

Vérifier si un modèle est 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

Alternativement, vérifie la forme de sortie — les modèles de détection end-to-end sortent (1, 300, 6), tandis que les modèles traditionnels sortent (1, nc + 4, 8400). Pour les formes d'autres tâches, consulte la FAQ sur les formes de sortie.

L'end-to-end est-il pris en charge pour les tâches de segmentation, de pose et OBB ?

Oui. Toutes les variantes de tâches YOLO26 — détection, segmentation, estimation de pose et détection d'objets orientés (OBB) — prennent en charge l'inférence end-to-end par défaut. L'option de secours end2end=False est également disponible pour toutes les tâches.

Chaque tâche étend la sortie de détection de base avec des données spécifiques à la tâche :

TâcheModèleSortie de bout en bout
Détectionyolo26n.pt(N, 300, 6)
Segmentationyolo26n-seg.pt(N, 300, 38) + proto (N, 32, 160, 160)
Poseyolo26n-pose.pt(N, 300, 57)
OBByolo26n-obb.pt(N, 300, 7)

Commentaires