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.
- 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 formatxyxy, 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ête | Objectif | Sortie de détection | Post-traitement |
|---|---|---|---|
| Un-à-un (par défaut) | Inférence de bout en bout | (N, 300, 6) | Seuil de confiance uniquement |
| Un-à-plusieurs | Sortie 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âche | Sortie de bout en bout | Donné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.
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 / YOLO11 | YOLO26 (de bout en bout) | |
|---|---|---|
| Sortie de détection | (N, nc + 4, 8400) | (N, 300, 6) |
| Format des boîtes | xywh (x centre, y centre, largeur, hauteur) | xyxy (x haut-gauche, y haut-gauche, x bas-droite, y bas-droite) |
| Disposition | Coordonnées des boîtes + scores de classe par ancrage | [x1, y1, x2, y2, conf, class_id] |
| NMS requis | Oui | Non |
| Post-traitement | NMS + filtre de confiance | Filtre 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.
Où 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 :
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.
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 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étrique | De bout en bout (par défaut) | Un-à-plusieurs + NMS (end2end=False) |
|---|---|---|
| Vitesse d'inférence CPU | Jusqu'à 43 % plus rapide | Baseline |
| Impact sur mAP | ~0,5 mAP de moins | Égal ou dépasse YOLO11 |
| Post-traitement | Filtre de confiance uniquement | Pipeline NMS complet |
| Complexité de déploiement | Minimale | Né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(ouyolo26n-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 dexywhversxyxy - 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=Falseavecnms=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 scenesNote 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âche | Forme de sortie | Description |
|---|---|---|
| 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é :
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 enabledAlternativement, 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âche | Modèle | Sortie de bout en bout |
|---|---|---|
| Détection | yolo26n.pt | (N, 300, 6) |
| Segmentation | 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) |