{"notes":[{"id":"object-detection/rt-detr","category":"object-detection","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"RT-DETR","subtitle":"Le chrono, NMS comprise","author":"Z. Yuan","paperDate":"2023-04-17","publishedOn":"2025-01-29","world":"external/rt-detr","paperTitle":"DETRs Beat YOLOs on Real-time Object Detection","paperUrl":"https://arxiv.org/abs/2304.08069","bibliography":{"title":"DETRs Beat YOLOs on Real-time Object Detection","authors":["Yian Zhao","Wenyu Lv","Shangliang Xu","Jinman Wei","Guanzhong Wang","Qingqing Dang","Yi Liu","Jie Chen"],"url":"https://arxiv.org/abs/2304.08069","pdfUrl":"https://arxiv.org/pdf/2304.08069v3","codeUrl":"https://github.com/lyuwenyu/RT-DETR","preprint":{"id":"2304.08069","submittedOn":"2023-04-17","url":"https://arxiv.org/abs/2304.08069"},"publication":{"venue":"CVPR","year":2024,"kind":"conference","url":"https://openaccess.thecvf.com/content/CVPR2024/html/Zhao_DETRs_Beat_YOLOs_on_Real-time_Object_Detection_CVPR_2024_paper.html"},"citation":"@misc{objectdetectionrtdetr2023,\n  title = {{DETRs Beat YOLOs on Real-time Object Detection}},\n  author = {Yian Zhao and Wenyu Lv and Shangliang Xu and Jinman Wei and Guanzhong Wang and Qingqing Dang and Yi Liu and Jie Chen},\n  year = {2023},\n  eprint = {2304.08069},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/2304.08069}\n}\n","citationPath":"/content/papers/citations/object-detection/rt-detr.cbe42f7a5b6c.bib"},"thumbnail":{"src":"/content/papers/assets/7e2de3c38daff439f51d86ec04a7bd391c3f708b817ae539f3c4df7c5546f4d6.png","width":1360,"height":332},"locale":"fr","path":"/content/papers/fr/object-detection/rt-detr.9f9ee96eed30.md","headings":[{"id":"définir-le-problème","text":"Définir le problème","depth":2},{"id":"combien-de-temps-prend-la-nms","text":"Combien de temps prend la NMS ?","depth":3},{"id":"un-chronomètre-de-bout-en-bout","text":"Un chronomètre de bout en bout","depth":3},{"id":"où-detr-perd-il-son-temps","text":"Où DETR perd-il son temps ?","depth":3},{"id":"résoudre-le-problème","text":"Résoudre le problème","depth":2},{"id":"vue-densemble","text":"Vue d'ensemble","depth":3},{"id":"les-comptes-de-lencodeur","text":"Les comptes de l'encodeur","depth":3},{"id":"aifi-lauto-attention-sur-s5-seulement","text":"AIFI : l'auto-attention sur S5 seulement","depth":3},{"id":"ccff-la-fusion-inter-échelles-confiée-aux-convolutions","text":"CCFF : la fusion inter-échelles confiée aux convolutions","depth":3},{"id":"choisir-300-requêtes","text":"Choisir 300 requêtes","depth":3},{"id":"décodeur-retirer-les-dernières-couches","text":"Décodeur : retirer les dernières couches","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"face-à-yolo-et-dino","text":"Face à YOLO et DINO","depth":3},{"id":"comment-régler-les-seuils","text":"Comment régler les seuils ?","depth":3},{"id":"changer-de-machine","text":"Changer de machine","depth":3},{"id":"il-reste-parfois-des-doublons","text":"Il reste parfois des doublons","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"object-detection/detr","relation":"referenced","topic":""},{"id":"object-detection/deformable-detr","relation":"referenced","topic":""},{"id":"feature-fusion/panet","relation":"referenced","topic":""}]},{"id":"object-detection/detr","category":"object-detection","legacyPath":"object-detection/2005-detr","legacySlug":"2005-detr","legacyPaths":[],"title":"DETR","subtitle":"Un pionnier entre deux domaines","author":"Z. Yuan","paperDate":"2020-05-26","publishedOn":"2024-06-04","paperTitle":"End-to-End Object Detection with Transformers","paperUrl":"https://www.ecva.net/papers/eccv_2020/papers_ECCV/html/832_ECCV_2020_paper.php","bibliography":{"title":"End-to-End Object Detection with Transformers","authors":["Nicolas Carion","Francisco Massa","Gabriel Synnaeve","Nicolas Usunier","Alexander Kirillov","Sergey Zagoruyko"],"url":"https://www.ecva.net/papers/eccv_2020/papers_ECCV/html/832_ECCV_2020_paper.php","pdfUrl":"https://www.ecva.net/papers/eccv_2020/papers_ECCV/papers/123460205.pdf","codeUrl":"https://github.com/facebookresearch/detr","preprint":{"id":"2005.12872","submittedOn":"2020-05-26","url":"https://arxiv.org/abs/2005.12872"},"publication":{"venue":"ECCV","year":2020,"kind":"conference","url":"https://www.ecva.net/papers/eccv_2020/papers_ECCV/html/832_ECCV_2020_paper.php"},"citation":"@misc{objectdetectiondetr2020,\n  title = {{End-to-End Object Detection with Transformers}},\n  author = {Nicolas Carion and Francisco Massa and Gabriel Synnaeve and Nicolas Usunier and Alexander Kirillov and Sergey Zagoruyko},\n  year = {2020},\n  eprint = {2005.12872},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/2005.12872}\n}\n","citationPath":"/content/papers/citations/object-detection/detr.5258ffe0b9a0.bib"},"thumbnail":{"src":"/content/papers/assets/dd7e8441e6460ff562cf558374789924518fafaed7ecde0e3e35ee778c316330.jpg","width":1024,"height":266},"locale":"fr","path":"/content/papers/fr/object-detection/detr.5137ffe05d95.md","headings":[{"id":"définir-le-problème","text":"Définir le problème","depth":2},{"id":"résoudre-le-problème","text":"Résoudre le problème","depth":2},{"id":"architecture-du-modèle","text":"Architecture du modèle","depth":3},{"id":"conception-de-la-perte","text":"Conception de la perte","depth":3},{"id":"jeu-de-données","text":"Jeu de données","depth":3},{"id":"détails-techniques","text":"Détails techniques","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"alors-ça-marche-cette-fois-","text":"Alors, ça marche cette fois ?","depth":3},{"id":"analyse-des-composants-du-modèle","text":"Analyse des composants du modèle","depth":3},{"id":"analyse-du-comportement-du-modèle","text":"Analyse du comportement du modèle","depth":3},{"id":"généralisation-à-un-nombre-dobjets-inédit","text":"Généralisation à un nombre d'objets inédit","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"object-detection/deformable-detr","relation":"next","topic":"Prédiction d’ensembles et détection par attention"},{"id":"object-detection/smca-detr","relation":"comparison","topic":"Accélérer la convergence des détecteurs Transformer"}]},{"id":"object-detection/deformable-detr","category":"object-detection","legacyPath":"object-detection/2010-deformable-detr","legacySlug":"2010-deformable-detr","legacyPaths":[],"title":"Deformable DETR","subtitle":"Une attention déformable","author":"Z. Yuan","paperDate":"2020-10-08","publishedOn":"2024-09-20","paperTitle":"Deformable DETR: Deformable Transformers for End-to-End Object Detection","paperUrl":"https://arxiv.org/abs/2010.04159","bibliography":{"title":"Deformable DETR: Deformable Transformers for End-to-End Object Detection","authors":["Xizhou Zhu","Weijie Su","Lewei Lu","Bin Li","Xiaogang Wang","Jifeng Dai"],"url":"https://arxiv.org/abs/2010.04159","pdfUrl":"https://arxiv.org/pdf/2010.04159","codeUrl":null,"preprint":{"id":"2010.04159","submittedOn":"2020-10-08","url":"https://arxiv.org/abs/2010.04159"},"publication":{"venue":"ICLR","year":2021,"kind":"conference","url":"https://arxiv.org/abs/2010.04159"},"citation":"@misc{objectdetectiondeformabledetr2020,\n  title = {{Deformable DETR: Deformable Transformers for End-to-End Object Detection}},\n  author = {Xizhou Zhu and Weijie Su and Lewei Lu and Bin Li and Xiaogang Wang and Jifeng Dai},\n  year = {2020},\n  eprint = {2010.04159},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/2010.04159}\n}\n","citationPath":"/content/papers/citations/object-detection/deformable-detr.df721a30472e.bib"},"thumbnail":{"src":"/content/papers/assets/36edfc5595a85c73fcdc9a4bbb9e1ca512e1f100676eb7c37203d71c84905c05.jpg","width":1224,"height":752},"locale":"fr","path":"/content/papers/fr/object-detection/deformable-detr.b9cc2a2d8b0f.md","headings":[{"id":"définition-du-problème","text":"Définition du problème","depth":2},{"id":"résolution-du-problème","text":"Résolution du problème","depth":2},{"id":"attention-déformable","text":"Attention déformable","depth":3},{"id":"calcul-multi-échelle","text":"Calcul multi-échelle","depth":3},{"id":"architecture-du-modèle","text":"Architecture du modèle","depth":3},{"id":"lecture-du-code","text":"Lecture du code","depth":3},{"id":"stratégie-dentraînement","text":"Stratégie d'entraînement","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"comparaison-avec-detr","text":"Comparaison avec DETR","depth":3},{"id":"étude-dablation","text":"Étude d'ablation","depth":3},{"id":"comparaison-avec-les-meilleures-méthodes","text":"Comparaison avec les meilleures méthodes","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"object-detection/detr","relation":"background","topic":"Prédiction d’ensembles et détection par attention"},{"id":"object-detection/h-detr","relation":"next","topic":"Prédiction d’ensembles et détection par attention"},{"id":"object-detection/smca-detr","relation":"comparison","topic":"Accélérer la convergence des détecteurs Transformer"}]},{"id":"feature-fusion/panet","category":"feature-fusion","legacyPath":"feature-fusion/1803-panet","legacySlug":"1803-panet","legacyPaths":[],"title":"PANet","subtitle":"Donnez-moi un raccourci","author":"Z. Yuan","paperDate":"2018-03-05","publishedOn":"2024-04-06","paperTitle":"Path Aggregation Network for Instance Segmentation","paperUrl":"https://arxiv.org/abs/1803.01534","bibliography":{"title":"Path Aggregation Network for Instance Segmentation","authors":["Shu Liu","Lu Qi","Haifang Qin","Jianping Shi","Jiaya Jia"],"url":"https://arxiv.org/abs/1803.01534","pdfUrl":"https://arxiv.org/pdf/1803.01534","codeUrl":null,"preprint":{"id":"1803.01534","submittedOn":"2018-03-05","url":"https://arxiv.org/abs/1803.01534"},"publication":{"venue":"CVPR","year":2018,"kind":"conference","url":"https://arxiv.org/abs/1803.01534"},"citation":"@misc{featurefusionpanet2018,\n  title = {{Path Aggregation Network for Instance Segmentation}},\n  author = {Shu Liu and Lu Qi and Haifang Qin and Jianping Shi and Jiaya Jia},\n  year = {2018},\n  eprint = {1803.01534},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1803.01534}\n}\n","citationPath":"/content/papers/citations/feature-fusion/panet.31d49af45ee9.bib"},"thumbnail":{"src":"/content/papers/assets/549012fa5498c4bbe44cfad4f4c54ed4eac5e7f0cdc71ae6308bb1ee6a4942ed.jpg","width":1024,"height":348},"locale":"fr","path":"/content/papers/fr/feature-fusion/panet.fc7dbc681a75.md","headings":[{"id":"définir-le-problème","text":"Définir le problème","depth":2},{"id":"résoudre-le-problème","text":"Résoudre le problème","depth":2},{"id":"conception-de-panet","text":"Conception de PANet","depth":3},{"id":"réseau-dagrégation-des-chemins-path-aggregation-fpn","text":"Réseau d'agrégation des chemins (Path Aggregation FPN)","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"par-rapport-au-fpn-de-base-","text":"Par rapport au FPN de base ?","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"feature-fusion/fpn","relation":"background","topic":"Fusion multi-échelle pour la détection"},{"id":"feature-fusion/bifpn","relation":"next","topic":"Fusion multi-échelle pour la détection"}]}],"text":"En avril 2023, des auteurs de Baidu et de l'université de Pékin ont déposé sur arXiv un article intitulé DETRs Beat YOLOs, autrement dit DETR bat YOLO en détection d'objets en temps réel.\n\nLe modèle qu'ils proposent s'appelle RT-DETR (Real-Time DEtection TRansformer). Il lit une photo de 640×640 et produit 300 prédictions, chacune composée d'une boîte, d'une classe et d'un score ; à l'usage, il suffit d'écarter les boîtes de faible score avec un seuil, sans NMS derrière. Il vise le terrain de YOLO, comme le suivi d'objets, la vidéosurveillance et la conduite autonome cités au début de l'article, où il faut traiter des dizaines d'images par seconde. L'article a ensuite été publié à CVPR 2024.\n\nÀ l'époque, ce titre était audacieux. Dans la Table 2 de l'article, le DETR le plus précis est DINO, dont la version ResNet-50 atteint 50,9 AP sur COCO mais ne traite que 5 images par seconde sur un GPU T4 (entrée 800×1333). Les grands modèles YOLO atteignent eux aussi 52 ou 53 AP sur COCO, à plusieurs dizaines d'images par seconde au minimum.\n\nPour affirmer que DETR gagne, il faut d'abord s'entendre sur la manière de mesurer la vitesse de YOLO. La sortie de YOLO n'est utilisable qu'après la NMS ; selon qu'on compte ou non son temps, et avec quels seuils, la vitesse mesurée varie beaucoup. L'article commence donc par remettre la NMS dans le chronomètre, puis revient décortiquer ce qui ralentit DETR.\n\n## Définir le problème\n\n### Combien de temps prend la NMS ?\n\nYOLO propose une boîte à chaque position de la carte de caractéristiques. Pour une entrée de 640×640, YOLOv8 compte sur ses trois cartes de pas 8, 16 et 32 un total de 80×80 + 40×40 + 20×20 = 8 400 positions, avec une boîte par position ; YOLOv5 place 3 ancres par position, soit 25 200 boîtes. Plusieurs positions proches d'un même objet signalent chacune une boîte, d'où le recours à la NMS (non-maximum suppression, suppression des non-maxima) pour éliminer les doublons.\n\nLa NMS a deux seuils. Le seuil de confiance supprime d'abord les boîtes au score trop bas ; ensuite, dans chaque classe, on garde la boîte au score le plus élevé, on supprime celles dont l'IoU avec elle dépasse le seuil, puis on recommence avec les boîtes restantes. L'IoU est l'aire de l'intersection de deux boîtes divisée par l'aire de leur union.\n\nLa charge de travail de la NMS dépend donc du nombre de boîtes qui passent le seuil de confiance. Sur une même entrée, les auteurs ont compté combien de boîtes il reste à YOLOv5 et à YOLOv8 selon le seuil de confiance. En abscisse le seuil de confiance, en ordonnée le nombre de boîtes ; en bleu YOLOv5, à ancres (anchor-based), en orange YOLOv8, sans ancres (anchor-free) :\n\n[![Diagramme en barres. En abscisse, les seuils de confiance 0,001 ; 0,005 ; 0,01 ; 0,05 ; 0,1 et 0,25 ; en ordonnée, le nombre de boîtes restantes, de 0 à 10 000. Les barres bleues, YOLOv5, dépassent un peu 9 000 à 0,001, puis descendent progressivement jusqu'à environ 600 quand le seuil monte ; les barres orange, YOLOv8, dépassent un peu 3 000 à 0,001 et descendent jusqu'à environ 400. À chaque seuil, la barre bleue est plus haute que l'orange.](/content/papers/assets/3ce51df5cdcf3f7995e63085f9c6925d29364b00decdae24bb9a6f2b628bacb1.png)](/content/papers/assets/3ce51df5cdcf3f7995e63085f9c6925d29364b00decdae24bb9a6f2b628bacb1.png)\n\n*Figure 2 de l'article, Zhao et al., extraite de la [**page 3 du PDF v3**](https://arxiv.org/pdf/2304.08069v3#page=3). Nombre de boîtes restantes pour une même entrée selon le seuil de confiance.*\n\nAu seuil de confiance 0,001, YOLOv5 garde un peu plus de 9 000 boîtes et YOLOv8 un peu plus de 3 000 ; au seuil 0,25, il n'en reste à chacun que quelques centaines. À chaque seuil, YOLOv5 a plus de boîtes que YOLOv8.\n\nLes auteurs mesurent ensuite sur COCO val2017 l'AP de YOLOv8 et le temps d'exécution de la NMS. La NMS utilise l'efficientNMSPlugin de TensorRT et s'exécute sur GPU T4 en TensorRT FP16 :\n\n| Seuil de confiance | Seuil d'IoU | AP | Temps de NMS (ms) |\n| ---: | ---: | ---: | ---: |\n| 0,001 | 0,5 | 52,1 | 2,24 |\n| 0,001 | 0,6 | 52,6 | 2,29 |\n| 0,001 | 0,7 | 52,9 | 2,36 |\n| 0,001 | 0,8 | 52,8 | 2,46 |\n| 0,01 | 0,7 | 52,4 | 1,73 |\n| 0,05 | 0,7 | 51,2 | 1,06 |\n\n*Tableau : d'après la Table 1 de l'article. La table d'origine est divisée en deux moitiés, seuil de confiance fixe et seuil d'IoU fixe, réunies ici en une seule. La taille de YOLOv8 n'y est pas indiquée ; ses 52,9 AP sont identiques à ceux de YOLOv8-L dans la Table 2.*\n\nCommençons par les quatre premières lignes. Le seuil de confiance est fixé à 0,001 et le seuil d'IoU passe de 0,5 à 0,8 ; le temps de NMS augmente de 2,24 à 2,46 ms. Plus le seuil d'IoU est haut, moins chaque passe supprime de boîtes, et plus il faut de passes. L'AP est maximale à 0,7.\n\nVoyons ensuite les trois lignes où le seuil d'IoU est fixé à 0,7. Quand le seuil de confiance passe de 0,001 à 0,05, le temps de NMS descend de 2,36 à 1,06 ms, soit une baisse de plus de moitié ; l'AP tombe de 52,9 à 51,2.\n\nLe meilleur score, 52,9 AP, est obtenu avec une confiance de 0,001 et une IoU de 0,7, un réglage dont le temps de NMS est le deuxième plus long du tableau. Pour obtenir cette AP, YOLO doit payer ce temps ; pour gagner du temps, il doit céder un peu d'AP.\n\nEt les temps du tableau ne couvrent qu'une partie du travail. L'efficientNMSPlugin contient plusieurs kernels, dont EfficientNMSFilter, RadixSort et EfficientNMS ; la Table 1 ne donne que celui d'EfficientNMS.\n\nL'annexe montre aussi les prédictions de YOLOv8-L sous plusieurs couples de seuils. Avec un seuil de confiance trop bas, l'image se couvre de boîtes superflues ; avec un seuil d'IoU bas, des objets qui se masquent mutuellement sont supprimés comme des doublons, et avec un seuil haut, des doublons qui se chevauchent restent en place. Selon les auteurs, le seuil de confiance se règle encore assez bien, alors que le seuil d'IoU est difficile à fixer avec justesse.\n\nOr, quand on publie la vitesse d'un YOLO, on ne mesure généralement que le modèle lui-même, sans la NMS (§3.1). La NMS est absente du tableau des vitesses, alors que l'AP est calculée avec la NMS et un seuil de confiance très bas.\n\n### Un chronomètre de bout en bout\n\nLes auteurs définissent donc une méthode de mesure de bout en bout (§3.2) :\n\n- Les données sont celles de COCO val2017, mesurées image par image puis moyennées, car le temps de NMS varie selon l'entrée.\n- YOLO est suivi du plugin NMS de TensorRT, avec les seuils que chaque modèle utilise pour obtenir son AP publiée. Le script officiel de mesure de vitesse règle par défaut YOLOv8 sur une confiance de 0,001, une IoU de 0,7 et 300 boîtes au plus en sortie.\n- Les E/S et les copies mémoire ne sont pas comptées ; la mesure se fait sur GPU T4 en TensorRT FP16.\n\nAvec ce chronomètre, les auteurs constatent qu'à précision comparable, les YOLO sans ancres sont plus rapides que ceux à ancres, car ces derniers produisent plus de boîtes et la NMS leur prend plus de temps. Parmi les modèles testés, ceux à ancres produisent le triple de boîtes des modèles sans ancres, ce qui concorde avec les 25 200 contre 8 400 calculés plus haut.\n\n### Où DETR perd-il son temps ?\n\nCôté DETR, il n'y a pas de NMS, donc ce temps est économisé d'emblée. Le problème se situe dans le modèle lui-même.\n\n\n> [!TIP]\n> \n> Si vous n'avez pas lu DETR, un peu de contexte. DETR fait de la détection d'objets avec un Transformer. Il prépare un nombre fixe de requêtes (queries), et chaque requête produit une boîte et une classe. À l'entraînement, l'algorithme hongrois apparie prédictions et annotations un à un ; une seule requête est responsable de chaque objet, si bien que la NMS devient inutile à l'inférence. Deformable DETR a ensuite envoyé des caractéristiques multi-échelles dans l'encodeur et restreint l'attention à quelques points échantillonnés, ce qui accélère nettement la convergence. Vous pouvez consulter nos notes sur ces deux articles :\n> \n> - **[[20.05] DETR : un pionnier entre deux domaines](/blog/papers/object-detection/detr)**\n> - **[[20.10] Deformable DETR : une attention déformable](/blog/papers/object-detection/deformable-detr)**\n\n\nLes caractéristiques multi-échelles font converger DETR plus vite et le rendent plus précis, au prix d'une séquence soudain bien plus longue à traiter pour l'encodeur. L'attention déformable a beau réduire le calcul, l'encodeur reste le goulot d'étranglement. Les auteurs citent l'analyse de D²ETR, selon laquelle l'encodeur concentre 49 % des GFLOPs de Deformable DETR, mais n'apporte que 11 % de l'AP.\n\nComment alléger l'encodeur, alors ? Si on le retire entièrement, combien de précision perd-on ? Si on n'en garde qu'une partie, laquelle garder ?\n\n## Résoudre le problème\n\n### Vue d'ensemble\n\nRT-DETR se compose de trois parties, le backbone, l'encodeur hybride et le décodeur. Le backbone fournit les caractéristiques des trois derniers stages, $S_3$, $S_4$ et $S_5$ ; l'encodeur hybride les organise en une séquence de caractéristiques d'image ; la sélection des requêtes en retient un nombre fixe, qui servent de point de départ aux requêtes ; le décodeur affine ensuite ces requêtes couche après couche et produit classes et boîtes.\n\nPrenons une image de 640×640 et dessinons la taille des données à chaque étape :\n\n[![Schéma du flux de données de RT-DETR. Une image de 640×640 traverse le backbone ResNet, qui fournit trois niveaux de caractéristiques, S3, S4 et S5, de tailles 80×80, 40×40 et 20×20, soit 6 400, 1 600 et 400 positions. Une convolution 1×1 ramène d'abord les trois niveaux à 256 canaux. Seul S5 entre dans AIFI, où il est aplati en 400 tokens pour une couche d'auto-attention, et en ressort sous forme de F5 ; S3 et S4 descendent directement, sans auto-attention. Les trois entrent ensemble dans CCFF, qui fusionne les échelles voisines par convolution, de haut en bas puis de bas en haut, et produit trois niveaux de 80×80, 40×40 et 20×20, aplatis et concaténés en 8 400 caractéristiques de dimension 256. La sélection des requêtes garde les 300 meilleures selon le score ; la caractéristique sert de contenu et la boîte prédite de point de départ. Le décodeur a 6 couches, chacune suivie d'une tête de prédiction ; une ligne rouge indique, à titre d'exemple, une inférence arrêtée à la couche 4, sans les deux dernières. La sortie finale compte 300 boîtes, classes et scores, filtrés par un seuil de score, sans NMS.](/content/papers/assets/aa6c94e8f5f296044711fbe3b976511ca9ae47680afa1399fb4629960e7f1b01.png)](/content/papers/assets/aa6c94e8f5f296044711fbe3b976511ca9ae47680afa1399fb4629960e7f1b01.png)\n\n*Figure : réalisée pour cette note. Exemple avec un backbone ResNet-50 et une entrée de 640×640 ; l'arrêt du décodeur à la couche 4 n'est qu'un exemple.*\n\nSuivons maintenant ce schéma, de l'encodeur jusqu'au décodeur.\n\n### Les comptes de l'encodeur\n\nComptons d'abord les positions de chacun des trois niveaux. Une fois la carte aplatie, chaque position devient un token :\n\n```python\nH = W = 640\nstrides = {\"S3\": 8, \"S4\": 16, \"S5\": 32}\ntokens = {k: (H // s) * (W // s) for k, s in strides.items()}\ntotal = sum(tokens.values())\n\nfor k, n in tokens.items():\n    side = H // strides[k]\n    print(f\"{k}: {side}×{side} = {n} tokens, soit {n / total:.0%}, {n * n / 1e6:.2f}M paires deux à deux\")\nprint(\"Au total,\", total, \"tokens sur les trois niveaux\")\n\npairs_each = sum(n * n for n in tokens.values())\npairs_s5 = tokens[\"S5\"] ** 2\nprint(f\"Auto-attention sur chaque niveau : {pairs_each / 1e6:.2f}M paires ; sur S5 seul : {pairs_s5 / 1e6:.2f}M paires, facteur {pairs_each / pairs_s5:.0f}\")\n```\n\n```text\nS3: 80×80 = 6400 tokens, soit 76%, 40.96M paires deux à deux\nS4: 40×40 = 1600 tokens, soit 19%, 2.56M paires deux à deux\nS5: 20×20 = 400 tokens, soit 5%, 0.16M paires deux à deux\nAu total, 8400 tokens sur les trois niveaux\nAuto-attention sur chaque niveau : 43.68M paires ; sur S5 seul : 0.16M paires, facteur 273\n```\n\nS3 représente à lui seul 76 % des tokens. Une auto-attention ordinaire apparie chaque token avec tous les tokens du même niveau, ce qui fait plus de 40 millions de paires pour le seul S3 ; en se limitant à S5, 160 000 paires suffisent.\n\nLes auteurs vérifient ces comptes avec une série de variantes d'encodeur. De gauche à droite, la figure présente cinq encodeurs, de A à E ; les trois flèches sous chaque case représentent les trois niveaux de caractéristiques. SSE désigne un encodeur Transformer mono-échelle, MSE un encodeur Transformer multi-échelle et CSF une fusion inter-échelles :\n\n[![Cinq blocs côte à côte représentant les cinq variantes d'encodeur. A n'a qu'un Concat ; B est un SSE suivi d'un Concat ; C un Concat suivi d'un MSE ; D un SSE suivi d'un CSF ; E un AIFI suivi d'un CCFF. Les flèches du bas indiquent que l'on ajoute l'interaction intra-niveau de A à B et l'interaction inter-échelles de B à C, que l'on sépare les deux de C à D, et que E est une version renforcée de D.](/content/papers/assets/7e2de3c38daff439f51d86ec04a7bd391c3f708b817ae539f3c4df7c5546f4d6.png)](/content/papers/assets/7e2de3c38daff439f51d86ec04a7bd391c3f708b817ae539f3c4df7c5546f4d6.png)\n\n*Figure 3 de l'article, Zhao et al., extraite de la [**page 4 du PDF v3**](https://arxiv.org/pdf/2304.08069v3#page=4). Structure des cinq variantes d'encodeur.*\n\nChaque variante part de DINO-Deformable-R50, avec le chargement des données et le décodeur plus légers de RT-DETR, et s'entraîne 12 époques. Le tableau ajoute une ligne $\\mathrm{D}_{S_5}$, de même structure que D, à ceci près que le SSE ne traite que S5 :\n\n| Variante | Fonctionnement de l'encodeur | AP | Paramètres (M) | Latence (ms) |\n| --- | --- | ---: | ---: | ---: |\n| A | encodeur retiré, les trois niveaux sont concaténés directement | 43,0 | 31 | 7,2 |\n| B | les trois niveaux partagent une couche Transformer mono-échelle, chacun fait son interaction intra-niveau, puis concaténation | 44,9 | 32 | 11,1 |\n| C | les sorties de B sont concaténées et envoyées dans un encodeur Transformer multi-échelle | 45,6 | 32 | 13,3 |\n| D | interaction intra-niveau confiée à un Transformer mono-échelle, fusion inter-niveaux à une structure de type PANet | 46,4 | 35 | 12,2 |\n| $\\mathrm{D}_{S_5}$ | comme D, mais seul S5 fait l'interaction intra-niveau | 46,8 | 35 | 7,9 |\n| E | AIFI + CCFF, c'est-à-dire l'approche de RT-DETR | 47,9 | 42 | 9,3 |\n\n*Tableau : Table 3 de l'article ; la colonne « Fonctionnement de l'encodeur » est résumée d'après le §4.2.*\n\nLisons-le ligne par ligne :\n\n1. **A → B** : l'AP gagne 1,9 et la latence augmente de 54 %. L'interaction intra-niveau est utile, mais l'auto-attention sur chaque niveau coûte cher.\n2. **B → C** : encore 0,7 d'AP et 20 % de latence en plus. L'interaction inter-échelles est utile elle aussi, mais l'encodeur multi-échelle coûte encore plus cher.\n3. **C → D** : en séparant interaction intra-niveau et fusion inter-niveaux, l'AP gagne 0,8 et la latence baisse même de 8 %.\n4. **D → $\\mathrm{D}_{S_5}$** : en ne gardant que l'auto-attention de S5, la latence passe de 12,2 à 7,9 ms, soit 35 % de moins, et l'AP gagne encore 0,4.\n5. **D → E** : avec AIFI et CCFF, l'AP gagne 1,5, les paramètres augmentent de 20 % et la latence baisse de 24 %.\n\nL'étape 4 supprime l'auto-attention de S3 et S4, c'est-à-dire les plus de 40 millions de paires calculées plus haut, et la latence baisse de 4,3 ms. Comparé à A, qui n'a pas du tout d'encodeur, le modèle final E n'ajoute que 2,1 ms de latence, pour une AP supérieure de 4,9.\n\nPourquoi ne garder que S5 ? Selon les auteurs, les caractéristiques de haut niveau portent la sémantique des objets ; l'auto-attention à ce niveau capte les relations entre les différents objets et concepts de l'image, ce qui aide les modules suivants à localiser et à reconnaître. Les caractéristiques de bas niveau manquent de sémantique ; l'interaction intra-niveau y est superflue, et risque même de faire double emploi avec celle du haut niveau ou d'y semer la confusion. La ligne $\\mathrm{D}_{S_5}$ va dans ce sens, puisque l'AP y gagne encore 0,4 avec bien moins de calcul.\n\n### AIFI : l'auto-attention sur S5 seulement\n\nL'article écrit l'encodeur hybride en trois lignes :\n\n$$\n\\begin{aligned}\n\\mathcal{Q} = \\mathcal{K} = \\mathcal{V} &= \\mathrm{Flatten}(S_5) \\\\\nF_5 &= \\mathrm{Reshape}(\\mathrm{AIFI}(\\mathcal{Q}, \\mathcal{K}, \\mathcal{V})) \\\\\n\\mathcal{O} &= \\mathrm{CCFF}(\\{S_3, S_4, F_5\\})\n\\end{aligned}\n$$\n\nAIFI est une couche standard d'encodeur Transformer. S5 est aplati en séquence, et Q, K et V en proviennent tous les trois ; après l'auto-attention et le réseau feed-forward, le résultat est replié à la forme 20×20. Continuons avec la même image, en remplaçant pour l'instant la sortie du backbone par des nombres aléatoires :\n\n```python\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\ntorch.manual_seed(0)\n# Sorties des trois derniers stages de ResNet-50, remplacées ici par des nombres aléatoires\nS3 = torch.randn(1, 512, 80, 80)\nS4 = torch.randn(1, 1024, 40, 40)\nS5 = torch.randn(1, 2048, 20, 20)\n\n# Une convolution 1×1 ramène d'abord les trois niveaux à 256 canaux\nproj = nn.ModuleList(nn.Conv2d(c, 256, 1) for c in (512, 1024, 2048))\nS3, S4, S5 = (p(x) for p, x in zip(proj, (S3, S4, S5)))\n\n# AIFI : seul S5 est aplati en séquence et passe dans une couche d'encodeur Transformer\naifi = nn.TransformerEncoderLayer(d_model=256, nhead=8, dim_feedforward=1024, batch_first=True)\nseq = S5.flatten(2).transpose(1, 2)\nF5 = aifi(seq).transpose(1, 2).reshape(1, 256, 20, 20)\nprint(\"Entrée d'AIFI\", tuple(seq.shape), \"→ F5\", tuple(F5.shape))\n```\n\n```text\nEntrée d'AIFI (1, 400, 256) → F5 (1, 256, 20, 20)\n```\n\nLes dimensions suivent la Table A de l'annexe, avec une dimension d'embedding de 256, 8 têtes, une couche feed-forward de 1024 et une seule couche. Le code officiel ajoute aussi un encodage de position sin-cos 2D sur Q et K, omis dans cette démonstration.\n\n### CCFF : la fusion inter-échelles confiée aux convolutions\n\nCCFF reprend le chemin de PANet, d'abord de haut en bas puis de bas en haut, chaque étape fusionnant deux niveaux voisins en un nouveau niveau. Le bloc de fusion (Figure 5) concatène les caractéristiques de deux niveaux voisins et les répartit en deux branches, chacune passant par une convolution 1×1 ; l'une des branches enchaîne ensuite N RepBlocks, et les deux branches sont finalement additionnées élément par élément. RT-DETR utilise N = 3.\n\nUn RepBlock est formé de RepConv, la conception de RepVGG à plusieurs branches à l'entraînement, fusionnées en une seule convolution 3×3 à l'inférence. La démonstration ci-dessous les remplace par des convolutions 3×3 ordinaires, et omet aussi la BN, la SiLU ainsi que la convolution 1×1 supplémentaire que le code officiel applique avant l'agrandissement :\n\n```python\nclass Fusion(nn.Module):\n    \"\"\"Bloc de fusion de la Figure 5 : deux branches 1×1, dont l'une enchaîne N blocs de convolution, puis addition élément par élément\"\"\"\n    def __init__(self, n=3):\n        super().__init__()\n        self.a = nn.Conv2d(512, 256, 1)\n        self.b = nn.Conv2d(512, 256, 1)\n        self.blocks = nn.Sequential(*[nn.Conv2d(256, 256, 3, padding=1) for _ in range(n)])\n\n    def forward(self, x, y):\n        z = torch.cat([x, y], dim=1)            # deux échelles voisines, 256 + 256 canaux\n        return self.a(z) + self.blocks(self.b(z))\n\nup = lambda x: F.interpolate(x, scale_factor=2, mode=\"nearest\")\ndown = nn.ModuleList(nn.Conv2d(256, 256, 3, stride=2, padding=1) for _ in range(2))\nfuse = nn.ModuleList(Fusion() for _ in range(4))\n\n# De haut en bas : F5 agrandi deux fois fusionne avec S4, puis le résultat agrandi fusionne avec S3\nT4 = fuse[0](up(F5), S4)\nT3 = fuse[1](up(T4), S3)\n# De bas en haut : on réduit à nouveau, en fusionnant successivement avec T4 puis F5\nB4 = fuse[2](down[0](T3), T4)\nB5 = fuse[3](down[1](B4), F5)\nfor name, x in [(\"T3\", T3), (\"B4\", B4), (\"B5\", B5)]:\n    print(name, tuple(x.shape))\n\nmemory = torch.cat([x.flatten(2) for x in (T3, B4, B5)], dim=2).transpose(1, 2)\nprint(\"Sortie de l'encodeur\", tuple(memory.shape))\n```\n\n```text\nT3 (1, 256, 80, 80)\nB4 (1, 256, 40, 40)\nB5 (1, 256, 20, 20)\nSortie de l'encodeur (1, 8400, 256)\n```\n\nT3, B4 et B5 sont des noms choisis pour la démonstration ; l'article ne nomme pas ces résultats intermédiaires. La sortie compte toujours trois niveaux, de même taille qu'en entrée, tous à 256 canaux ; une fois aplatis et concaténés, ils forment une séquence de 8 400 caractéristiques.\n\nC'est le même nombre que les 8 400 boîtes de YOLOv8 vues plus haut, car les deux modèles prédisent sur trois grilles de pas 8, 16 et 32. Ensuite, leurs chemins se séparent. YOLO confie à la NMS les boîtes de toutes les positions, alors que RT-DETR n'en retient que 300, qu'il transmet au décodeur.\n\n\n> [!TIP]\n> \n> Les deux chemins de CCFF et le RepBlock ont chacun leur origine ; vous pouvez consulter nos notes :\n> \n> - **[[18.03] PANet : donnez-moi un raccourci](/blog/papers/feature-fusion/panet)**\n> - **[[21.01] RepVGG : rendre sa grandeur à VGG](/blog/papers/reparameterization/repvgg)**\n\n\n### Choisir 300 requêtes\n\nDans le DETR d'origine, les requêtes sont un ensemble de vecteurs appris, indépendants de l'image d'entrée et difficiles à optimiser. Les travaux suivants les choisissent plutôt parmi les sorties de l'encodeur. La version en deux étapes de Deformable DETR et DINO sélectionnent toutes deux les K meilleures caractéristiques selon le score de classification pour initialiser les requêtes (DINO ne s'en sert que comme requêtes de position). RT-DETR prend K = 300 ; les caractéristiques retenues servent de requêtes de contenu, et les boîtes qu'elles prédisent de point de départ aux requêtes de position.\n\nReprenons l'exemple précédent :\n\n```python\nscore_head = nn.Linear(256, 80)    # les 80 classes de COCO\nbox_head = nn.Linear(256, 4)\n\nlogits = score_head(memory)                       # score de chaque position pour les 80 classes\ntop_score, top_idx = logits.max(-1).values.topk(300, dim=1)\nidx = top_idx[..., None]\ncontent = memory.gather(1, idx.expand(-1, -1, 256))           # requêtes de contenu\ninit_box = box_head(memory).sigmoid().gather(1, idx.expand(-1, -1, 4))  # boîtes initiales\nprint(\"Scores\", tuple(logits.shape), \"→ sélection\", tuple(content.shape), \"et\", tuple(init_box.shape))\n```\n\n```text\nScores (1, 8400, 80) → sélection (1, 300, 256) et (1, 300, 4)\n```\n\nLes deux têtes de prédiction de la démonstration ont des poids aléatoires. Le code officiel place aussi une couche linéaire et une LayerNorm devant les têtes, et la prédiction de boîte y ajoute l'ancre de chaque position ; la sélection elle-même se réduit à un topk.\n\nLe problème vient du critère de sélection. Un score de classification élevé indique qu'un objet semble présent à cette position, sans garantir que la boîte prédite soit précise. Un détecteur doit à la fois trouver la bonne classe et placer la boîte au bon endroit ; si l'on ne regarde que le score de classification, des caractéristiques mal localisées peuvent être retenues, et le décodeur doit alors corriger à partir d'un mauvais point de départ.\n\nLes auteurs appellent cet écart incertitude, la définissent comme la distance entre les deux distributions prédites, de localisation et de classification, et l'intègrent à la perte de classification pour la minimiser avec elle :\n\n$$\n\\mathcal{U}(\\hat{\\mathcal{X}}) = \\lVert \\mathcal{P}(\\hat{\\mathcal{X}}) - \\mathcal{C}(\\hat{\\mathcal{X}}) \\rVert, \\quad \\hat{\\mathcal{X}} \\in \\mathbb{R}^D\n$$\n\n$$\n\\mathcal{L}(\\hat{\\mathcal{X}}, \\hat{\\mathcal{Y}}, \\mathcal{Y}) = \\mathcal{L}_{box}(\\hat{\\mathbf{b}}, \\mathbf{b}) + \\mathcal{L}_{cls}(\\mathcal{U}(\\hat{\\mathcal{X}}), \\hat{\\mathbf{c}}, \\mathbf{c})\n$$\n\nIci, $\\hat{\\mathcal{X}}$ désigne une caractéristique de l'encodeur, $\\hat{\\mathbf{c}}$ et $\\hat{\\mathbf{b}}$ la classe et la boîte prédites, $\\mathbf{c}$ et $\\mathbf{b}$ les annotations.\n\nLa formule est très abstraite ; le code officiel est bien plus concret. Il entraîne le score de classification avec la Varifocal loss. Si une prédiction est appariée à une annotation par l'appariement hongrois, sa cible d'entraînement pour cette classe passe de 1 à l'IoU entre sa boîte et la boîte annotée ; sans appariement, la cible est 0. Prenons trois caractéristiques sélectionnées :\n\n| Caractéristique | Annotation appariée | IoU entre boîte prédite et annotation | Cible de classification habituelle | Cible de classification de RT-DETR |\n| --- | --- | ---: | ---: | ---: |\n| 1 | chien | 0,45 | 1 | 0,45 |\n| 2 | chat | 0,90 | 1 | 0,90 |\n| 3 | aucune | — | 0 | 0 |\n\n*Tableau : exemple construit pour cette note.*\n\nLa boîte de la caractéristique 1 ne recouvre le chien qu'avec une IoU de 0,45 ; son score de classification est donc tiré vers 0,45 au lieu d'être poussé vers 1 comme auparavant. Le score de classification apprend ainsi à refléter la qualité de la boîte, et les 300 caractéristiques choisies selon ce score penchent vers celles dont la boîte est elle aussi précise. Cette perte s'applique à la fois aux 300 prédictions retenues dans la sortie de l'encodeur et à la sortie de chaque couche du décodeur.\n\n\n> [!TIP]\n> \n> Si vous cherchez le nom uncertainty-minimal dans le code officiel, vous ne le trouverez pas. Quelqu'un a posé la question sur GitHub dès août 2024, et un autre utilisateur a indiqué qu'il se trouve dans la fonction de perte `loss_labels_vfl` ([RT-DETR #438](https://github.com/lyuwenyu/RT-DETR/issues/438)). La v1 de l'article appelait cette conception IoU-aware query selection, un nom plus proche de ce que fait le code.\n\n\nQuel est l'effet ? Sur COCO val2017, les auteurs représentent en nuage de points les caractéristiques retenues par les deux méthodes. En abscisse l'IoU entre la boîte et l'annotation, en ordonnée le score de classification ; seuls les points de score supérieur à 0,5 sont tracés. En violet la nouvelle sélection, en vert la sélection d'origine par score de classification ; les courbes en haut et à droite montrent la densité des points :\n\n[![Nuage de points, abscisse IoU score de 0 à 1, ordonnée Classification score de 0,5 à 1. Les points violets, Uncertainty-minimal, sont très nombreux dans le coin supérieur droit, avec une IoU d'environ 0,6 à 0,9 et un score de classification de 0,7 ou plus ; les points verts, Vanilla, moins nombreux, se concentrent dans le coin inférieur droit, avec des scores de classification surtout entre 0,5 et 0,65. Sur les courbes de densité en haut et à droite, le violet est nettement plus haut que le vert.](/content/papers/assets/420e753b3717e561d473e69a1066095c23584488dab765ae7acee273b91819f8.png)](/content/papers/assets/420e753b3717e561d473e69a1066095c23584488dab765ae7acee273b91819f8.png)\n\n*Figure 6 de l'article, Zhao et al., extraite de la [**page 6 du PDF v3**](https://arxiv.org/pdf/2304.08069v3#page=6). Score de classification et IoU des caractéristiques d'encodeur retenues par les deux sélections de requêtes.*\n\nPlus un point est proche du coin supérieur droit, plus la classe et la boîte ont de chances d'être justes. Les points violets se massent dans ce coin ; les points verts se concentrent en bas à droite, avec des boîtes correctes mais des scores de classification plutôt bas, et ils sont bien moins nombreux. La Table 4 traduit cela en proportions (RT-DETR-R50, 12 époques d'entraînement) :\n\n| Sélection des requêtes | AP | Proportion avec score de classification > 0,5 | Proportion avec les deux > 0,5 |\n| --- | ---: | ---: | ---: |\n| par score de classification (d'origine) | 47,9 | 0,35 % | 0,30 % |\n| uncertainty-minimal | 48,7 | 0,82 % | 0,67 % |\n\n*Tableau : Table 4 de l'article.*\n\nLes deux proportions font plus que doubler, et l'AP gagne 0,8.\n\n### Décodeur : retirer les dernières couches\n\nLe décodeur de RT-DETR reprend presque tel quel celui de DINO, avec 6 couches, 300 requêtes et une cross-attention déformable à 4 points d'échantillonnage. L'entraînement conserve aussi l'exercice de débruitage de DINO ; la Table A de l'annexe fixe le nombre de débruitage à 200.\n\n\n> [!TIP]\n> \n> L'exercice de débruitage consiste à fournir au décodeur des boîtes annotées bruitées comme requêtes supplémentaires et à demander au modèle de retrouver les boîtes d'origine ; il disparaît entièrement à l'inférence. Le fichier de configuration officiel indique `num_denoising: 100` ; le code fait de chaque groupe une copie positive et une copie négative, si bien que le nombre de requêtes de débruitage réellement envoyées au décodeur tourne autour de 200 et varie selon l'image du lot qui compte le plus d'annotations. Sur l'origine de cette méthode, voir :\n> \n> - **[[22.03] DN-DETR : aujourd'hui le chien, demain le chat](/blog/papers/object-detection/dn-detr)**\n> - **[[22.03] DINO : proche, mais pas assez](/blog/papers/object-detection/dino-detr)**\n\n\nChaque couche du décodeur est suivie de sa propre tête de prédiction, et la sortie de chaque couche entre dans le calcul de la perte à l'entraînement. Chaque couche produit donc un ensemble complet de prédictions directement utilisables. À l'inférence, le code officiel désigne avec `eval_idx` la couche dont on prend la sortie ; la boucle s'arrête à cette couche, et les suivantes ne sont pas exécutées.\n\nLes auteurs entraînent des décodeurs de 4 à 7 couches, puis examinent les résultats quand l'inférence s'arrête à chaque couche (RT-DETR-R50, 72 époques d'entraînement) :\n\n| Couches utilisées à l'inférence | Det4 | Det5 | Det6 | Det7 | Latence (ms) |\n| ---: | ---: | ---: | ---: | ---: | ---: |\n| 7 | | | | 52,6 | 9,6 |\n| 6 | | | 53,1 | 52,6 | 9,3 |\n| 5 | | 52,9 | 53,0 | 52,5 | 8,8 |\n| 4 | 52,7 | 52,7 | 52,7 | 52,1 | 8,3 |\n| 3 | 52,4 | 52,3 | 52,4 | 51,5 | 7,9 |\n| 2 | 51,6 | 51,3 | 51,3 | 50,6 | 7,5 |\n| 1 | 49,6 | 48,8 | 49,1 | 48,3 | 7,0 |\n\n*Tableau : Table 5 de l'article. Det k désigne un décodeur entraîné avec k couches, et les cellules donnent l'AP ; la colonne de latence ne dépend que du nombre de couches utilisées à l'inférence.*\n\nRegardons d'abord la colonne Det6. En passant de 6 à 5 couches, l'AP ne perd que 0,1 et la latence baisse de 0,5 ms ; à 4 couches, l'AP est de 52,7 pour 8,3 ms. Chaque couche supplémentaire coûte environ 0,4 à 0,5 ms, et plus la couche est tardive, moins elle apporte à l'AP.\n\nComparons maintenant en ligne. Det6 arrêté à la couche 4 donne 52,7, autant que Det4 entraîné directement avec 4 couches ; arrêté à la couche 5, il donne 53,0, soit 0,1 de plus que Det5 entraîné directement avec 5 couches. Sur ce tableau, entraîner un modèle à 6 couches puis le tronquer revient à peu près à entraîner séparément un modèle moins profond. Quant au décodeur à 7 couches, son meilleur résultat n'est que de 52,6, moins bien qu'avec 6.\n\nQuelle est l'amplitude du réglage ? Avec la seule couche 1, on est à 7,0 ms ; avec les 6 couches, à 9,3 ms. En répondant à un utilisateur sur GitHub, un des auteurs a indiqué que les différentes parties de RT-DETR sont désormais relativement équilibrées, et que l'essentiel du temps est consommé par le backbone et le décodeur ([RT-DETR #266, 2024-04](https://github.com/lyuwenyu/RT-DETR/issues/266)). Comme le nombre de couches du décodeur se règle en plus sans réentraînement, on dispose d'un bouton de vitesse supplémentaire.\n\nLes modèles officiels s'en servent ainsi. Dans la Table B de l'annexe, la version réduite de RT-DETR-R50 divise par deux les canaux du RepBlock ; ce même modèle, arrêté entre la couche 2 et la couche 6, se règle de 50,3 AP à 154 FPS jusqu'à 52,2 AP à 125 FPS ; dans la même table, YOLOv8-M obtient 50,2 AP à 97 FPS. Le RT-DETR-R50-m du dépôt officiel (51,3 AP, 145 FPS) a `eval_idx: 2` dans son fichier de configuration et utilise donc la sortie de la couche 3.\n\n## Discussion\n\n### Face à YOLO et DINO\n\nLa Table 2 compare RT-DETR aux tailles L et X de YOLO. Les données sont celles de COCO val2017 ; RT-DETR et YOLO prennent des entrées de 640×640, les autres DETR de 800×1333 ; les FPS sont mesurés sur T4 en TensorRT FP16 avec un lot de 1, et YOLO utilise les poids officiels, avec la NMS branchée selon la méthode de bout en bout décrite plus haut :\n\n| Modèle | Paramètres (M) | GFLOPs | FPS | AP | AP<sub>S</sub> |\n| --- | ---: | ---: | ---: | ---: | ---: |\n| YOLOv6-L | 59 | 150 | 99 | 52,8 | 34,4 |\n| YOLOv7-X | 71 | 189 | 45 | 52,9 | 36,9 |\n| YOLOv8-L | 43 | 165 | 71 | 52,9 | 35,3 |\n| YOLOv8-X | 68 | 257 | 50 | 53,9 | 35,7 |\n| DINO-Deformable-DETR-R50 | 47 | 279 | 5 | 50,9 | 34,6 |\n| RT-DETR-R50 | 42 | 136 | 108 | 53,1 | 34,8 |\n| RT-DETR-R101 | 76 | 259 | 74 | 54,3 | 36,0 |\n\n*Tableau : extrait de la Table 2 de l'article. AP<sub>S</sub> est l'AP sur les petits objets.*\n\nVoyons quelques paires :\n\n- **RT-DETR-R50 contre YOLOv8-L** : 53,1 contre 52,9 AP, seulement 0,2 de plus ; 108 contre 71 FPS, soit 9,3 contre 14,1 ms par image. Les GFLOPs sont aussi un peu plus bas, 136 contre 165.\n- **RT-DETR-R101 contre YOLOv8-X** : 0,4 d'AP de plus, 74 contre 50 FPS.\n- **RT-DETR-R50 contre YOLOv6-L** : seulement 0,3 d'AP de plus, 108 contre 99 FPS ; l'écart est bien plus faible.\n\nC'est sur les petits objets que RT-DETR perd. L'AP<sub>S</sub> de RT-DETR-R50 est de 34,8, soit 0,5 de moins que les 35,3 de YOLOv8-L ; les 36,0 de R101 sont 0,9 en dessous des 36,9 de YOLOv7-X. Les auteurs l'inscrivent dans les limites de leur travail, comme une faiblesse commune aux DETR.\n\nLes conditions d'entraînement diffèrent aussi. Le backbone de RT-DETR est d'abord préentraîné sur ImageNet, puis le modèle est entraîné 72 époques sur COCO. Les poids R50 chargés par le code officiel sont ceux d'un ResNet50-vd entraîné par distillation de connaissances dans PaddleClas (`ssld_v2`) ; [le tableau des modèles de PaddleClas](https://paddleclas-doc.readthedocs.io/zh/latest/models/ResNet_and_vd_en.html) lui donne 83,0 % de top-1 sur ImageNet, contre 76,5 % pour le ResNet-50 d'origine. Les YOLOv7 et YOLOv8 du tableau sont au contraire entraînés de zéro ; l'annexe de l'article de YOLOv10 précise d'ailleurs qu'il est entraîné de zéro, comme YOLOv8, YOLOv7 et YOLOv9. Ce tableau compare des systèmes complets, poids du backbone et recette d'entraînement compris.\n\nAvec un préentraînement sur Objects365 puis un affinage sur COCO, R18, R50 et R101 atteignent respectivement 49,2 ; 55,3 et 56,2 AP, soit 2,7 ; 2,2 et 1,9 de plus qu'avec COCO seul (Table C de l'annexe).\n\nFace à DINO, RT-DETR-R50 a 2,2 d'AP de plus et tourne à 108 contre 5 FPS, soit environ 21 fois plus vite. Là encore, ce sont des systèmes complets qu'on compare. L'entrée de DINO est de 800×1333, soit 2,6 fois les pixels d'une image 640×640, et DINO s'entraîne 36 époques, contre 72 pour RT-DETR. Pour savoir ce que l'encodeur hybride économise à lui seul, il faut revenir à la comparaison à conditions égales de la Table 3.\n\nEn juin 2024, un utilisateur a contesté cette comparaison sur GitHub ([RT-DETR #349](https://github.com/lyuwenyu/RT-DETR/issues/349)). D'après ses mesures sur RTX 3090, RT-DETR a environ moitié moins de FLOPs que DINO, comme l'indique l'article, mais n'est qu'environ deux fois plus rapide ; il relève aussi que les deux modèles n'ont ni la même augmentation de données ni le même backbone. Un des auteurs a répondu que la vitesse de DINO doit se mesurer dans les conditions où il atteint la précision de son article, que l'équipe l'avait testée sur V100, T4 et 3090 avec les résultats attendus, et qu'entre modèles différents, seule la comparaison de la précision et de la vitesse finales est équitable. Après plusieurs échanges, chacun a conservé sa position.\n\n### Comment régler les seuils ?\n\nEn octobre 2023, un utilisateur a posé sur GitHub une question très concrète ([RT-DETR #77](https://github.com/lyuwenyu/RT-DETR/issues/77)). La méthode de bout en bout utilise les seuils de validation, 0,001 en confiance et 0,7 en IoU ; or, en production, on prend généralement un seuil de confiance autour de 0,1 qui élimine d'emblée la plupart des boîtes de fond, la NMS devient bien plus rapide, et l'avantage de vitesse de RT-DETR se réduit.\n\nUn des auteurs a répondu qu'avec un seuil de 0,1, YOLO n'atteint plus la mAP publiée. Mesurer la précision à 0,001 et la vitesse à 0,1 supposerait un modèle dans deux états à la fois, ce qui est impossible ; le seuil de score de RT-DETR ne fait que filtrer la sortie, et la vitesse en déploiement reste celle de l'évaluation.\n\nLes deux arguments se tiennent. La Table 1 donne justement des chiffres intermédiaires ; en montant le seuil de confiance de 0,001 à 0,05, le temps d'EfficientNMS baisse de 1,3 ms et l'AP de 1,7. De son côté, les 53,1 de RT-DETR sont eux aussi calculés en confiant au programme d'évaluation les 300 sorties de chaque image ; avec un seuil de score en déploiement, l'AP ne peut que rester stable ou baisser, la différence étant que sa vitesse, elle, ne bouge pas.\n\nEn lisant ces deux tableaux, gardez aussi en tête que les temps de YOLO de la Table 2 incluent tout le plugin NMS, alors que la Table 1 ne donne qu'un de ses kernels ; on ne peut pas soustraire directement les temps de l'une à ceux de l'autre.\n\n\n> [!TIP]\n> \n> Un an plus tard, YOLOv10 a repris cette méthode de mesure de bout en bout de RT-DETR pour sa latence, tout en retirant lui aussi la NMS de YOLO :\n> \n> - **[[24.05] YOLOv10 : adieu, NMS](/blog/papers/object-detection/yolov10)**\n\n\n### Changer de machine\n\nEn février 2024, un autre utilisateur a comparé avec trtexec, sur trois cartes graphiques, RT-DETR-R18 et YOLOv8s suivi de la NMS ([RT-DETR #214](https://github.com/lyuwenyu/RT-DETR/issues/214)). Sur RTX 4090, il obtient 1,065 contre 1,90 ms, un rapport conforme à l'article ; sur T4 en revanche, 5,6 contre 5,4 ms, YOLOv8s étant même un peu plus rapide ; sur la RTX 3050 d'un ordinateur portable, RT-DETR est plus lent de près de 2 ms.\n\nUn des auteurs a répondu que la vitesse de YOLO dans l'article est une moyenne sur les 5 000 images de val2017, puisque le temps de NMS varie avec le nombre de boîtes qui passent le seuil de score ; il a ajouté que RT-DETR utilise plus de types d'opérateurs que YOLOv8, inégalement pris en charge selon les versions de TensorRT. Le fil s'est arrêté sans conclusion. La vitesse de bout en bout dépend du matériel, des versions logicielles et des données ; pour l'appliquer à votre propre cas, il faut encore la mesurer sur votre propre machine.\n\n### Il reste parfois des doublons\n\nRT-DETR a supprimé la NMS, mais les doublons n'ont pas totalement disparu. En juillet 2024, un utilisateur a signalé que plusieurs boîtes apparaissaient parfois sur un même objet ; d'autres ont aussi rencontré des boîtes presque entièrement superposées, de classes différentes, voire de même classe ([RT-DETR #379](https://github.com/lyuwenyu/RT-DETR/issues/379)). Un des auteurs a conseillé de choisir soigneusement le seuil de score. Un autre utilisateur a répondu que les scores de ces doublons sont souvent très proches et qu'un seuil ne suffit pas à les séparer ; ajouter soi-même une NMS règle le problème, au prix de temps supplémentaire, et le cas est plus rare quand le modèle est bien entraîné.\n\n\n> [!NOTE]\n> \n> Si vous utilisez `rtdetr-l` ou `rtdetr-x` du paquet Ultralytics, ce sont les versions à backbone HGNetv2 listées dans le dépôt officiel, à 53,0 AP et 114 FPS et à 54,8 AP et 74 FPS respectivement, soit les deux modèles phares de la v1 de l'article. Cette v3 met plutôt en avant R50 et R101, à backbone ResNet.\n\n\n## Conclusion\n\nL'article RT-DETR fait deux choses. D'une part, il mesure la vitesse de YOLO NMS comprise, avec les seuils qui donnent l'AP publiée ; d'autre part, il démonte DETR pour voir où passe le temps et où l'on peut en gagner. Voici à peu près ce qu'on peut en retenir :\n\n1. **Le temps de NMS suit les seuils**. Sur T4, pour YOLOv8, quand le seuil de confiance passe de 0,001 à 0,05, le temps d'EfficientNMS descend de 2,36 à 1,06 ms, et l'AP de 52,9 à 51,2.\n2. **L'auto-attention seulement sur S5**. Sans l'interaction intra-niveau de S3 et S4, la latence baisse de 35 % sans perte d'AP ; avec AIFI et CCFF, on obtient 2,3 d'AP de plus et 30 % de latence en moins que la variante C à encodeur Transformer multi-échelle.\n3. **Un score de classification qui reflète la qualité de la boîte**. La sélection des requêtes prend l'IoU comme cible de classification, et l'AP gagne 0,8 avec 12 époques.\n4. **Le décodeur peut être tronqué**. Environ 0,5 ms par couche ; un modèle à 6 couches arrêté à la couche 4 est aussi précis qu'un modèle entraîné directement avec 4 couches.\n\nLa prochaine fois que vous lirez le tableau des vitesses d'un détecteur temps réel, regardez d'abord si la NMS est comptée et avec quels seuils, puis sur quelle carte et avec quelle version de TensorRT. La vitesse de RT-DETR ne varie pas avec le seuil, mais l'AP sur les petits objets et le préentraînement du backbone doivent aussi entrer dans la comparaison.\n\nCette méthode de mesure de bout en bout a ensuite été reprise par YOLOv10, et RT-DETR lui-même est devenu le point de départ des DETR temps réel qui ont suivi. Fin 2024, [DEIM](/blog/papers/object-detection/deim) a modifié l'entraînement de RT-DETRv2 et de D-FINE pour que ce type de modèle apprenne plus vite.\n","imageSizes":{"/content/papers/assets/3ce51df5cdcf3f7995e63085f9c6925d29364b00decdae24bb9a6f2b628bacb1.png":{"width":664,"height":508},"/content/papers/assets/aa6c94e8f5f296044711fbe3b976511ca9ae47680afa1399fb4629960e7f1b01.png":{"width":800,"height":2044},"/content/papers/assets/7e2de3c38daff439f51d86ec04a7bd391c3f708b817ae539f3c4df7c5546f4d6.png":{"width":1360,"height":332},"/content/papers/assets/420e753b3717e561d473e69a1066095c23584488dab765ae7acee273b91819f8.png":{"width":632,"height":620}}}
