{"notes":[{"id":"classic-cnns/deformable-convnets","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"Deformable ConvNets","subtitle":"Un sacré jeu de jambes","author":"Z. Yuan","paperDate":"2017-03-17","publishedOn":"2024-05-01","world":"external/deformable-convnets","paperTitle":"Deformable Convolutional Networks","paperUrl":"https://arxiv.org/abs/1703.06211","bibliography":{"title":"Deformable Convolutional Networks","authors":["Jifeng Dai","Haozhi Qi","Yuwen Xiong","Yi Li","Guodong Zhang","Han Hu","Yichen Wei"],"url":"https://arxiv.org/abs/1703.06211","pdfUrl":"https://arxiv.org/pdf/1703.06211v3","codeUrl":null,"preprint":{"id":"1703.06211","submittedOn":"2017-03-17","url":"https://arxiv.org/abs/1703.06211"},"publication":{"venue":"ICCV","year":2017,"kind":"conference","url":"https://openaccess.thecvf.com/content_iccv_2017/html/Dai_Deformable_Convolutional_Networks_ICCV_2017_paper.html"},"citation":"@misc{classiccnnsdeformableconvnets2017,\n  title = {{Deformable Convolutional Networks}},\n  author = {Jifeng Dai and Haozhi Qi and Yuwen Xiong and Yi Li and Guodong Zhang and Han Hu and Yichen Wei},\n  year = {2017},\n  eprint = {1703.06211},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1703.06211}\n}\n","citationPath":"/content/papers/citations/classic-cnns/deformable-convnets.f724f3ff1784.bib"},"thumbnail":{"src":"/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png","width":987,"height":633},"locale":"fr","path":"/content/papers/fr/classic-cnns/deformable-convnets.20c475e9ecf6.md","headings":[{"id":"définir-le-problème","text":"Définir le problème","depth":2},{"id":"une-grille-figée","text":"Une grille figée","depth":3},{"id":"faire-apprendre-la-déformation-au-réseau-une-idée-déjà-explorée","text":"Faire apprendre la déformation au réseau, une idée déjà explorée","depth":3},{"id":"résoudre-le-problème","text":"Résoudre le problème","depth":2},{"id":"les-deux-étapes-dune-convolution","text":"Les deux étapes d'une convolution","depth":3},{"id":"un-décalage-pour-chaque-point","text":"Un décalage pour chaque point","depth":3},{"id":"échantillonner-une-position-fractionnaire","text":"Échantillonner une position fractionnaire","depth":3},{"id":"doù-viennent-les-décalages","text":"D'où viennent les décalages ?","depth":3},{"id":"deux-cas-particuliers","text":"Deux cas particuliers","depth":3},{"id":"roi-pooling-déformable","text":"RoI pooling déformable","depth":3},{"id":"dans-le-réseau","text":"Dans le réseau","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"le-champ-réceptif-suit-vraiment-lobjet","text":"Le champ réceptif suit vraiment l'objet","depth":3},{"id":"combien-de-couches-remplacer","text":"Combien de couches remplacer ?","depth":3},{"id":"face-à-la-convolution-dilatée","text":"Face à la convolution dilatée","depth":3},{"id":"le-coût","text":"Le coût","depth":3},{"id":"coco","text":"COCO","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/stn","relation":"referenced","topic":""},{"id":"object-detection/deformable-detr","relation":"referenced","topic":""}]},{"id":"classic-cnns/stn","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"STN","subtitle":"Remuer ciel et terre","author":"Z. Yuan","paperDate":"2015-06-05","publishedOn":"2023-10-25","world":"external/stn","paperTitle":"Spatial Transformer Networks","paperUrl":"https://arxiv.org/abs/1506.02025","bibliography":{"title":"Spatial Transformer Networks","authors":["Max Jaderberg","Karen Simonyan","Andrew Zisserman","Koray Kavukcuoglu"],"url":"https://arxiv.org/abs/1506.02025","pdfUrl":"https://arxiv.org/pdf/1506.02025v3","codeUrl":null,"preprint":{"id":"1506.02025","submittedOn":"2015-06-05","url":"https://arxiv.org/abs/1506.02025"},"publication":{"venue":"NeurIPS","year":2015,"kind":"conference","url":"https://papers.nips.cc/paper_files/paper/2015/hash/33ceb07bf4eeb3da587e268d663aba1a-Abstract.html"},"citation":"@misc{classiccnnsstn2015,\n  title = {{Spatial Transformer Networks}},\n  author = {Max Jaderberg and Karen Simonyan and Andrew Zisserman and Koray Kavukcuoglu},\n  year = {2015},\n  eprint = {1506.02025},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1506.02025}\n}\n","citationPath":"/content/papers/citations/classic-cnns/stn.e823b15c17eb.bib"},"thumbnail":{"src":"/content/papers/assets/7b29191fd34e282fbd2d0fa901014f5770192cf1641d814b39b43a903817fb7f.png","width":480,"height":364},"locale":"fr","path":"/content/papers/fr/classic-cnns/stn.5e7782001ccc.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":"trois-composants","text":"Trois composants","depth":3},{"id":"le-générateur-de-grille-partir-de-la-sortie","text":"Le générateur de grille : partir de la sortie","depth":3},{"id":"léchantillonneur-prendre-des-valeurs-entre-les-points-de-la-grille","text":"L'échantillonneur : prendre des valeurs entre les points de la grille","depth":3},{"id":"linsérer-dans-le-réseau","text":"L'insérer dans le réseau","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"mnist-déformé","text":"MNIST déformé","depth":3},{"id":"numéros-de-rue","text":"Numéros de rue","depth":3},{"id":"classification-fine-des-oiseaux","text":"Classification fine des oiseaux","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"text-recognition/rare","relation":"referenced","topic":""}]},{"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"}]}],"text":"Sur une même photo, le chien au premier plan est grand, celui du fond est petit ; un même chat peut être assis, couché, ou en train de s'étirer. Outre les classes, un modèle de reconnaissance doit composer avec ces variations géométriques.\n\nPour y faire face, les réseaux convolutifs comptent surtout sur les données. À l'entraînement, on retourne, redimensionne et recadre les images pour que le réseau voie suffisamment de variantes, puis on compte sur un modèle assez grand pour les retenir. La structure du réseau, elle, reste figée. Une convolution $3\\times3$ échantillonne toujours sur une grille carrée, le pooling réduit toujours selon un rapport fixe, et le RoI pooling découpe toujours une région candidate selon une grille fixe.\n\nEn mars 2017, Jifeng Dai et ses collègues de Microsoft Research Asia publient cet article. Ils veulent que les positions d'échantillonnage de la convolution se mettent à bouger d'elles-mêmes.\n\n## Définir le problème\n\n### Une grille figée\n\nLes auteurs rappellent qu'il existe traditionnellement deux façons de gérer les variations géométriques. La première est l'augmentation des données, pour que les données d'entraînement couvrent assez de variations ; la seconde consiste à concevoir des caractéristiques et des algorithmes insensibles à ces variations, comme SIFT ou la détection par fenêtre glissante.\n\nLes deux approches reposent sur la même hypothèse, à savoir que l'on connaît à l'avance les variations à attendre. L'augmentation des données exige de décider d'avance comment tourner et redimensionner les images ; quant aux caractéristiques invariantes conçues à la main, elles deviennent impossibles à construire pour des variations trop complexes. Dès qu'une nouvelle tâche présente une variation imprévue, ces approches se retrouvent démunies.\n\nLes réseaux convolutifs ont le même problème, à cause de leur structure figée. Les auteurs donnent deux exemples :\n\n1. **Dans une même couche, tous les champs réceptifs ont la même taille.** Les caractéristiques de haut niveau portent du sens, et des positions différentes peuvent correspondre à des objets de tailles et de formes différentes, mais les champs réceptifs sont strictement identiques. Pour une tâche qui exige une localisation fine, comme la segmentation sémantique, on aimerait que le champ réceptif s'adapte à l'objet.\n2. **La détection extrait encore les caractéristiques dans des boîtes.** Les méthodes de détection d'objets de l'époque finissent toutes par extraire les caractéristiques dans des régions candidates rectangulaires. Pour des objets à la forme variable, comme les personnes ou les animaux, une bonne moitié de la boîte est occupée par le fond.\n\n### Faire apprendre la déformation au réseau, une idée déjà explorée\n\nPour que le réseau apprenne lui-même une transformation géométrique, il existait déjà STN. Il prédit un ensemble de paramètres de transformation globaux, par exemple une matrice affine, puis déforme toute la carte de caractéristiques en conséquence.\n\n\n> [!TIP]\n> \n> Pour STN, vous pouvez lire notre note :\n> \n> - **[[15.06] STN : Remuer ciel et terre](/blog/papers/classic-cnns/stn)**\n\n\nSTN applique une même transformation à toute la carte de caractéristiques, ce qui coûte cher en calcul, et ses paramètres de transformation sont difficiles à apprendre ; ses succès de l'époque concernent surtout la classification d'images à petite échelle. Une scène de détection ou de segmentation contient de nombreux objets, chacun avec sa taille et sa posture, et une seule transformation globale ne peut pas tous les prendre en charge.\n\nPeut-on laisser chaque point d'échantillonnage de la convolution, à chaque position, se déplacer selon le contenu qu'il a sous les yeux ?\n\n## Résoudre le problème\n\n### Les deux étapes d'une convolution\n\nCommençons par écrire précisément la convolution ordinaire. Les auteurs décomposent la convolution 2D en deux étapes. On échantillonne d'abord la carte de caractéristiques d'entrée selon une grille régulière $\\mathcal{R}$, puis on multiplie les valeurs échantillonnées par les poids et on les somme.\n\nLa grille d'une convolution $3\\times3$ est\n\n$$\n\\mathcal{R}=\\{(-1,-1),(-1,0),\\ldots,(0,1),(1,1)\\},\n$$\n\nsoit 9 points. Pour chaque position $\\mathbf{p}_0$ de la carte de caractéristiques de sortie,\n\n$$\n\\mathbf{y}(\\mathbf{p}_0)=\\sum_{\\mathbf{p}_n\\in\\mathcal{R}}\\mathbf{w}(\\mathbf{p}_n)\\cdot\\mathbf{x}(\\mathbf{p}_0+\\mathbf{p}_n).\n$$\n\n$\\mathbf{p}_n$ parcourt tour à tour les 9 points de la grille. Cette équation est la même pour chaque canal ; nous ne regarderons donc plus, dans la suite, que les positions spatiales 2D.\n\n### Un décalage pour chaque point\n\nLa convolution déformable ne change qu'une chose, en ajoutant à chaque point de la grille un décalage $\\Delta\\mathbf{p}_n$ :\n\n$$\n\\mathbf{y}(\\mathbf{p}_0)=\\sum_{\\mathbf{p}_n\\in\\mathcal{R}}\\mathbf{w}(\\mathbf{p}_n)\\cdot\\mathbf{x}(\\mathbf{p}_0+\\mathbf{p}_n+\\Delta\\mathbf{p}_n).\n$$\n\nLes poids $\\mathbf{w}$ restent les mêmes ; ce qui change, c'est l'endroit où l'on va chercher les valeurs.\n\n[![Quatre schémas des positions d'échantillonnage d'une convolution 3x3, sur une grille en pointillés. (a) Les 9 points verts forment une grille 3x3 régulière ; (b) chaque position d'origine, en vert clair, est poussée par une flèche bleu clair vers une nouvelle position, en bleu foncé, avec des directions et des distances toutes différentes ; (c) les 9 points sont repoussés depuis le centre vers l'extérieur dans les mêmes proportions, et forment une grille agrandie ; (d) les 9 points ont tourné ensemble d'un certain angle](/content/papers/assets/45479d40b2beb8096d042f56871e08317e36ffc2be1857a325732d436b36da0f.png)](/content/papers/assets/45479d40b2beb8096d042f56871e08317e36ffc2be1857a325732d436b36da0f.png)\n\n*Figure 1 de l'article, extraite de la [**page 2 du PDF v3**](https://arxiv.org/pdf/1703.06211v3#page=2). (a) Grille d'échantillonnage régulière d'une convolution ordinaire ; (b) convolution déformable, où chaque point reçoit un décalage ; (c)(d) sont des cas particuliers de (b), qui correspondent respectivement à un changement d'échelle et à une rotation.*\n\n(a) est la convolution ordinaire. En (b), les 9 points sont chacun poussés vers une nouvelle position, avec des directions et des distances toutes différentes. (c) et (d) sont des cas particuliers de (b). En (c), tous les points sont repoussés depuis le centre, ce qui revient à agrandir le noyau ; en (d), tous les points tournent ensemble autour du centre, ce qui revient à une rotation. Autrement dit, changement d'échelle, rapport d'aspect et rotation sont tous contenus dans « un décalage propre à chaque point ».\n\n### Échantillonner une position fractionnaire\n\nLes décalages sont appris et ne tombent généralement pas sur des entiers, si bien que les points d'échantillonnage atterrissent entre les nœuds de la grille. Les auteurs obtiennent la valeur par interpolation bilinéaire :\n\n$$\n\\mathbf{x}(\\mathbf{p})=\\sum_{\\mathbf{q}}G(\\mathbf{q},\\mathbf{p})\\cdot\\mathbf{x}(\\mathbf{q}),\\qquad\nG(\\mathbf{q},\\mathbf{p})=g(q_x,p_x)\\cdot g(q_y,p_y),\\qquad g(a,b)=\\max(0,1-|a-b|).\n$$\n\n$\\mathbf{q}$ parcourt toutes les positions entières de la carte de caractéristiques, mais $g$ n'est non nul que pour une distance inférieure à 1 ; en pratique, seuls les quatre points les plus proches ont un poids. Faisons le calcul sur une carte de caractéristiques $5\\times5$, avec la position centrale $\\mathbf{p}_0=(2,2)$, le point du coin supérieur droit de la grille $\\mathbf{p}_n=(-1,1)$, et un décalage appris supposé égal à $(0.25,0.5)$ :\n\n```python\nimport torch\n\nx = torch.arange(25.).reshape(5, 5)                # une carte de caractéristiques 5×5, dont les valeurs vont simplement de 0 à 24\nprint(x)\n\ndef g(a, b):\n    return max(0.0, 1 - abs(a - b))\n\ndef sample(x, py, px):\n    \"\"\"Équations (3) et (4) : pondération sur toutes les positions entières q ; seuls les quatre points les plus proches ont un poids non nul\"\"\"\n    total = 0.0\n    for qy in range(x.shape[0]):\n        for qx in range(x.shape[1]):\n            w = g(qy, py) * g(qx, px)\n            if w > 0:\n                print(f\"  q=({qy},{qx}) valeur {x[qy, qx].item():4.1f} poids {w:.3f}\")\n                total += w * x[qy, qx].item()\n    return total\n\np0, pn, dp = (2, 2), (-1, 1), (0.25, 0.5)           # position centrale, coin supérieur droit de la grille, décalage appris\npy, px = p0[0] + pn[0] + dp[0], p0[1] + pn[1] + dp[1]\nprint(f\"position d'échantillonnage p0 + pn + Δpn = ({py}, {px})\")\nprint(\"résultat de l'interpolation :\", sample(x, py, px))\n```\n\n```text\ntensor([[ 0.,  1.,  2.,  3.,  4.],\n        [ 5.,  6.,  7.,  8.,  9.],\n        [10., 11., 12., 13., 14.],\n        [15., 16., 17., 18., 19.],\n        [20., 21., 22., 23., 24.]])\nposition d'échantillonnage p0 + pn + Δpn = (1.25, 3.5)\n  q=(1,3) valeur  8.0 poids 0.375\n  q=(1,4) valeur  9.0 poids 0.375\n  q=(2,3) valeur 13.0 poids 0.125\n  q=(2,4) valeur 14.0 poids 0.125\nrésultat de l'interpolation : 9.75\n```\n\nLe point du coin supérieur droit se trouvait à l'origine en $(1,3)$, avec la valeur 8. Une fois décalé, il tombe en $(1.25,3.5)$ ; les quatre voisins se partagent les poids selon la distance, et la somme de ces quatre poids vaut 1. Les valeurs de cette carte valent justement $5\\times\\text{ligne}+\\text{colonne}$ ; en remplaçant, on obtient $5\\times1.25+3.5=9.75$, comme le résultat de l'interpolation.\n\nL'interpolation est continue, et la sortie est différentiable par rapport à $\\Delta\\mathbf{p}_n$ ; le gradient peut donc remonter jusqu'aux décalages.\n\n### D'où viennent les décalages ?\n\n[![Schéma de la convolution déformable. À gauche, l'input feature map, avec un cadre d'échantillonnage 3x3. Une flèche verte envoie l'entrée vers une conv, qui produit un offset field à 2N canaux ; on y prélève les offsets de cette position, dessinés comme 3x3 petites flèches d'orientations différentes. Les offsets descendent vers la deformable convolution ; sur la carte d'entrée, les positions d'échantillonnage déformées sont reliées par des pointillés bleus à un point de l'output feature map, à droite](/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png)](/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png)\n\n*Figure 2 de l'article, extraite de la [**page 2 du PDF v3**](https://arxiv.org/pdf/1703.06211v3#page=2). Convolution déformable $3\\times3$.*\n\nLes décalages sont calculés par une autre couche de convolution à partir de **la même carte de caractéristiques d'entrée**, par le chemin vert en haut de la figure. Cette convolution a la même taille et la même dilatation que la convolution principale ; l'offset field qu'elle produit a la même taille spatiale que l'entrée, avec $2N$ canaux. Une convolution $3\\times3$ a $N=9$ points d'échantillonnage, et chaque point a besoin de deux nombres $(\\Delta y,\\Delta x)$, d'où 18 canaux.\n\nAutrement dit, chaque position de la carte de caractéristiques a son propre ensemble de 9 décalages, eux-mêmes calculés à partir du contenu au voisinage de cette position. C'est ce que les auteurs qualifient de local, dense et adaptatif (*local, dense, adaptive*).\n\nConstruisons une couche complète avec le `deform_conv2d` de torchvision :\n\n```python\nimport torch\nimport torch.nn as nn\nfrom torchvision.ops import deform_conv2d\n\nclass DeformableConv(nn.Module):\n    def __init__(self, cin, cout, k=3):\n        super().__init__()\n        self.weight = nn.Parameter(torch.randn(cout, cin, k, k) * 0.01)\n        # convolution qui prédit les décalages : 3×3 comme la convolution principale, 2N = 18 canaux de sortie, initialisée à 0\n        self.offset = nn.Conv2d(cin, 2 * k * k, k, padding=k // 2)\n        nn.init.zeros_(self.offset.weight)\n        nn.init.zeros_(self.offset.bias)\n\n    def forward(self, x):\n        offset = self.offset(x)                        # [B, 18, H, W] : 9 couples (Δy, Δx) pour chaque position de sortie\n        print(\"forme des décalages :\", tuple(offset.shape))\n        return deform_conv2d(x, offset, self.weight, padding=1)\n\nm = DeformableConv(256, 256)\ny = m(torch.randn(2, 256, 38, 38))\nprint(\"forme de la sortie :\", tuple(y.shape))\nprint(f\"paramètres de la convolution principale {m.weight.numel():,}, paramètres de la convolution des décalages {sum(p.numel() for p in m.offset.parameters()):,}\")\n```\n\n```text\nforme des décalages : (2, 18, 38, 38)\nforme de la sortie : (2, 256, 38, 38)\nparamètres de la convolution principale 589,824, paramètres de la convolution des décalages 41,490\n```\n\nLa convolution des décalages n'ajoute qu'environ 7 % des paramètres de la convolution principale. Ses poids sont initialisés à 0 ; au début de l'entraînement, les décalages sont donc tous nuls, et la couche entière se comporte comme une convolution ordinaire. Le taux d'apprentissage de la convolution des décalages vaut $\\beta$ fois celui des autres couches, avec $\\beta=1$ par défaut. Les poids de la convolution principale et les décalages sont appris ensemble pendant l'entraînement, sans aucune annotation supplémentaire.\n\n### Deux cas particuliers\n\nL'agrandissement proportionnel de la Figure 1(c) n'est autre qu'une convolution dilatée. Vérifions directement les deux cas particuliers :\n\n```python\nimport torch\nimport torch.nn.functional as F\nfrom torchvision.ops import deform_conv2d\n\ntorch.manual_seed(0)\nx = torch.randn(1, 8, 7, 7)\nw = torch.randn(16, 8, 3, 3)\n\n# 1. Décalages tous nuls : identique à une convolution 3×3 ordinaire\noffset = torch.zeros(1, 2 * 9, 7, 7)                # 9 points d'échantillonnage, chacun avec (Δy, Δx)\ndiff = (deform_conv2d(x, offset, w, padding=1) - F.conv2d(x, w, padding=1)).abs().max()\nprint(\"décalages nuls, écart avec une convolution ordinaire :\", diff.item())\n\n# 2. Chaque point d'échantillonnage poussé d'une case vers l'extérieur : équivaut à une convolution dilatée avec dilation=2\ngrid = [(i, j) for i in (-1, 0, 1) for j in (-1, 0, 1)]\noffset = torch.tensor([v for i, j in grid for v in (i, j)], dtype=torch.float32)\noffset = offset.view(1, 18, 1, 1).expand(1, 18, 7, 7)\ndiff = (deform_conv2d(x, offset, w, padding=1) - F.conv2d(x, w, padding=2, dilation=2)).abs().max()\nprint(\"décalages égaux aux coordonnées de la grille, écart avec dilation=2 :\", diff.item())\n```\n\n```text\ndécalages nuls, écart avec une convolution ordinaire : 0.0\ndécalages égaux aux coordonnées de la grille, écart avec dilation=2 : 0.0\n```\n\nDans le second exemple, le point $(i,j)$ de la grille, augmenté du décalage $(i,j)$, arrive en $(2i,2j)$, soit exactement une convolution dilatée de dilatation 2. Le facteur d'agrandissement d'une convolution dilatée doit être fixé à l'avance ; la convolution déformable, elle, laisse le réseau en décider lui-même à chaque position.\n\n### RoI pooling déformable\n\nLa même idée s'applique au RoI pooling de la détection. Le RoI pooling découpe une région candidate en $k\\times k$ cases, fait la moyenne dans chaque case, et produit des caractéristiques de taille fixe. Le RoI pooling déformable ajoute un décalage à chaque case, pour que les cases puissent se déplacer vers l'objet.\n\nLe calcul des décalages est un peu différent. Les auteurs font d'abord un RoI pooling ordinaire, suivi d'une couche entièrement connectée qui produit des décalages « normalisés » $\\Delta\\hat{\\mathbf{p}}_{ij}$, qu'ils multiplient ensuite par la largeur et la hauteur de la région candidate pour obtenir les décalages réels :\n\n$$\n\\Delta\\mathbf{p}_{ij}=\\gamma\\cdot\\Delta\\hat{\\mathbf{p}}_{ij}\\circ(w,h),\\qquad \\gamma=0.1.\n$$\n\nLa multiplication par la largeur et la hauteur rend l'apprentissage des décalages indépendant de la taille de la région candidate. Le position-sensitive RoI pooling de R-FCN a lui aussi une version déformable, où les décalages sont calculés sur toute l'image par une couche de convolution.\n\n### Dans le réseau\n\nLes auteurs intègrent la convolution déformable à deux backbones préentraînés sur ImageNet, ResNet-101 et Aligned-Inception-ResNet, qui corrige le problème d'alignement des caractéristiques. Comme le veut la pratique courante de l'époque, le pas du dernier étage (conv5) passe de 2 à 1, et ses convolutions passent à une dilatation de 2, ce qui porte la résolution de la carte de caractéristiques de sortie de 1/32 à 1/16.\n\nLa convolution déformable ne remplace que les dernières convolutions $3\\times3$ de conv5, par défaut les 3 dernières. Le backbone est suivi de quatre réseaux de tâche, à savoir DeepLab pour la segmentation sémantique, un class-aware RPN à une seule étape, ainsi que Faster R-CNN et R-FCN pour la détection en deux étapes. Dans ces deux derniers, on peut en plus remplacer le RoI pooling par sa version déformable.\n\n## Discussion\n\nLa segmentation sémantique utilise PASCAL VOC et Cityscapes, avec la mIoU comme métrique ; la détection utilise PASCAL VOC et COCO, avec la mAP.\n\n### Le champ réceptif suit vraiment l'objet\n\n[![Schéma des champs réceptifs de deux couches de convolution, sur une photo en niveaux de gris montrant un petit et un grand mouton, en trois plans superposés. (a) standard convolution : dans le plan supérieur, un point sur le petit mouton et un point sur le grand ; dans les deux plans inférieurs, les points d'échantillonnage correspondants forment des grilles carrées de taille fixe. (b) deformable convolution : pour les deux mêmes points, les points d'échantillonnage du petit mouton se regroupent sur le petit mouton, tandis que ceux du grand mouton s'écartent largement et couvrent tout le grand mouton](/content/papers/assets/e05ab32de27fdd8551794ded9ab71fafae0ea423979ef7250f9e7d31cedf1534.png)](/content/papers/assets/e05ab32de27fdd8551794ded9ab71fafae0ea423979ef7250f9e7d31cedf1534.png)\n\n*Figure 5 de l'article, extraite de la [**page 4 du PDF v3**](https://arxiv.org/pdf/1703.06211v3#page=4). Champs réceptifs de deux couches de convolution ordinaire (a) et de deux couches de convolution déformable (b).*\n\nEn partant d'un point de la couche supérieure et en redescendant à travers deux convolutions $3\\times3$, on aboutit à un ensemble de points d'échantillonnage dans la couche inférieure. En (a), avec la convolution ordinaire, la zone échantillonnée est une grille carrée de même taille, que le point se trouve sur le petit ou sur le grand mouton. En (b), une fois passé à la convolution déformable, les points du petit mouton ne regardent que le petit mouton, tandis que ceux du grand mouton s'étalent sur tout l'animal.\n\nCe n'est qu'un schéma ; les auteurs ont aussi quantifié le phénomène. Ils définissent une mesure appelée dilatation effective (*effective dilation*), la distance moyenne entre points d'échantillonnage adjacents d'un noyau, que l'on peut voir grossièrement comme la taille du champ réceptif. Ils font ensuite tourner, sur l'ensemble de test de VOC 2007, un R-FCN doté de 3 couches de convolution déformable, et regroupent les statistiques selon le type d'objet sur lequel tombe le centre du noyau :\n\n| Couche | Petits objets | Objets moyens | Grands objets | Fond |\n| --- | ---: | ---: | ---: | ---: |\n| res5c | 5,3 ± 3,3 | 5,8 ± 3,5 | 8,4 ± 4,5 | 6,2 ± 3,0 |\n| res5b | 2,5 ± 1,3 | 3,1 ± 1,5 | 5,1 ± 2,5 | 3,2 ± 1,2 |\n| res5a | 2,2 ± 1,2 | 2,9 ± 1,3 | 4,2 ± 1,6 | 3,1 ± 1,1 |\n\n*Tableau : extrait de la Table 2 de l'article ; moyenne et écart-type de la dilatation effective. Les objets sont répartis en trois classes, petits, moyens et grands, selon l'aire de leur boîte, avec des seuils à $96^2$ et $224^2$ pixels.*\n\nDans les trois couches, la tendance est la même, et plus l'objet est grand, plus les points d'échantillonnage s'écartent. Les valeurs du fond se situent entre celles des objets moyens et des grands objets ; selon les auteurs, reconnaître le fond demande aussi un champ réceptif assez grand.\n\n### Combien de couches remplacer ?\n\n| Couches de convolution déformable | DeepLab (Cityscapes) | class-aware RPN | Faster R-CNN | R-FCN |\n| --- | ---: | ---: | ---: | ---: |\n| 0 (référence) | 70,4 | 44,9 | 62,1 | 61,8 |\n| 1 | 73,5 | 54,4 | 63,8 | 63,0 |\n| 2 | 74,4 | 56,3 | 63,3 | 63,8 |\n| 3 (par défaut) | **75,2** | 57,2 | 63,3 | 64,7 |\n| 6 | 75,1 | **57,7** | **64,0** | **65,4** |\n\n*Tableau : extrait de la Table 1 de l'article, ResNet-101. mIoU sur Cityscapes pour DeepLab, mAP@0.7 sur l'ensemble de test de VOC 2007 pour les trois détecteurs.*\n\nRien qu'en remplaçant la dernière couche, DeepLab gagne 3 points de pourcentage, et le class-aware RPN près de 10 points. Ces deux méthodes n'ont pas de RoI pooling et calculent leur résultat directement à partir des caractéristiques de convolution, ce qui explique peut-être qu'elles en profitent le plus. DeepLab sature dès 3 couches, alors que les détecteurs progressent encore à 6 couches ; les auteurs retiennent 3 couches, comme compromis entre les tâches.\n\n### Face à la convolution dilatée\n\nPuisque la convolution dilatée en est un cas particulier, augmenter directement la dilatation aurait-il le même effet ?\n\n| Configuration | DeepLab (VOC / Cityscapes) | class-aware RPN | Faster R-CNN | R-FCN |\n| --- | ---: | ---: | ---: | ---: |\n| Convolution dilatée (2,2,2), par défaut | 69,7 / 70,4 | 68,0 / 44,9 | 78,1 / 62,1 | 80,0 / 61,8 |\n| Convolution dilatée (4,4,4) | 73,1 / 71,9 | 72,8 / 53,1 | 78,6 / 63,1 | 80,5 / 63,0 |\n| Convolution dilatée (6,6,6) | 73,6 / 72,7 | 73,6 / 55,2 | 78,5 / 62,3 | 80,2 / 63,5 |\n| Convolution dilatée (8,8,8) | 73,2 / 72,4 | 73,2 / 55,1 | 77,8 / 61,8 | 80,3 / 63,2 |\n| Convolution déformable | **75,3 / 75,2** | **74,5 / 57,2** | 78,6 / 63,3 | 81,4 / 64,7 |\n| RoI pooling déformable | – | – | 78,3 / 66,6 | 81,2 / 65,0 |\n| Les deux à la fois | – | – | **79,3 / 66,9** | **82,6 / 68,5** |\n\n*Tableau : extrait de la Table 3 de l'article, ResNet-101. mIoU pour DeepLab, mAP@0.5 / mAP@0.7 sur l'ensemble de test de VOC 2007 pour les détecteurs. Entre parenthèses, la dilatation des trois dernières convolutions $3\\times3$.*\n\nAvec une dilatation plus grande, toutes les tâches s'améliorent, signe que le champ réceptif par défaut était trop petit. Mais la meilleure dilatation varie selon la tâche, 6 pour DeepLab et 4 pour Faster R-CNN, et aucune valeur ne convient à toutes. Sur chaque tâche, la convolution déformable fait au moins aussi bien que la meilleure dilatation, sans avoir à être réglée tâche par tâche.\n\nUtilisé seul, le RoI pooling déformable améliore déjà nettement la mAP@0.7 des deux détecteurs, une métrique plus stricte qui exige des boîtes plus précises. Avec les deux modules réunis, R-FCN passe de 80,0 / 61,8 à 82,6 / 68,5.\n\n### Le coût\n\n| Méthode | Paramètres | Propagation avant (s) | Exécution totale (s) |\n| --- | ---: | ---: | ---: |\n| DeepLab (Cityscapes) | 46,0 M | 0,610 | 0,650 |\n| Avec modules déformables | 46,1 M | 0,656 | 0,696 |\n| Faster R-CNN | 58,3 M | 0,147 | 0,190 |\n| Avec modules déformables | 59,9 M | 0,192 | 0,234 |\n| R-FCN | 47,1 M | 0,143 | 0,170 |\n| Avec modules déformables | 49,5 M | 0,169 | 0,193 |\n\n*Tableau : trois groupes extraits de la Table 4 de l'article ; ResNet-101, CPU Intel E5-2650 v2 et GPU NVIDIA K40. Le temps d'exécution total comprend le redimensionnement de l'image et les post-traitements comme la NMS.*\n\nLes paramètres n'augmentent que de 2,4 M au plus ; les auteurs en déduisent que le progrès vient de la capacité à modéliser les transformations géométriques, et non d'un modèle plus gros. Côté temps, DeepLab ne prend qu'environ 7 % de plus, tandis que la propagation avant de Faster R-CNN passe de 0,147 à 0,192 seconde, soit 30 % de plus.\n\nUn autre coût apparaît au déploiement. La convolution déformable n'est pas une convolution standard ; au début, exporter le `deform_conv2d` de torchvision en ONNX exigeait encore un [paquet tiers](https://pypi.org/project/deform-conv2d-onnx-exporter/) qui la décompose en opérations ordinaires, et la vitesse en pâtissait aussi.\n\n### COCO\n\nEnfin, une comparaison complète sur COCO test-dev, avec la mAP@[0.5:0.95] comme métrique :\n\n| Méthode | Backbone | Ordinaire | Déformable |\n| --- | --- | ---: | ---: |\n| class-aware RPN | ResNet-101 | 23,2 | 25,8 |\n| Faster R-CNN | ResNet-101 | 29,4 | 33,1 |\n| R-FCN | ResNet-101 | 30,8 | 34,5 |\n| Faster R-CNN | Aligned-Inception-ResNet | 30,8 | 34,1 |\n| R-FCN | Aligned-Inception-ResNet | 32,9 | 36,1 |\n| R-FCN, test multi-échelle et moyennage des boîtes | Aligned-Inception-ResNet | 35,5 | **37,5** |\n\n*Tableau : extrait de la Table 5 de l'article.*\n\nSur ResNet-101, les trois méthodes progressent respectivement de 11 %, 13 % et 12 % en relatif. Avec un backbone plus fort, ou avec des astuces comme le test multi-échelle, le gain demeure. Si l'on ventile par taille d'objet, en valeur absolue, ce sont les objets moyens et grands qui progressent le plus ; par exemple, la mAP des grands objets de Faster R-CNN passe de 47,1 à 51,2, et celle des petits objets de 9,0 à 11,6.\n\n## Conclusion\n\nLa convolution déformable ajoute un décalage appris à chaque point d'échantillonnage de la convolution. La modification est minime, avec seulement quelques pour cent de paramètres en plus, mais le champ réceptif peut alors suivre la taille et la forme des objets, avec des gains réguliers en segmentation comme en détection, surtout sur la mAP@0.7, qui exige des boîtes plus précises.\n\nPour l'essayer dans votre propre réseau, le `deform_conv2d` de torchvision et une convolution des décalages initialisée à 0 suffisent. Le déploiement est moins simple, car ce n'est pas une convolution standard, et les frameworks d'inférence ne la prennent pas toujours en charge. Les points d'échantillonnage ont désormais un sacré jeu de jambes ; les ingénieurs chargés du déploiement devront peut-être travailler le leur.\n\nCette idée a fait beaucoup de chemin par la suite. La v2 dote les points d'échantillonnage de poids de modulation, InternImage s'appuie sur la v3 pour en faire le backbone d'un grand modèle, et [Deformable DETR](/blog/papers/object-detection/deformable-detr) la transpose à l'attention. Nous la recroiserons dans les prochaines notes.\n","imageSizes":{"/content/papers/assets/45479d40b2beb8096d042f56871e08317e36ffc2be1857a325732d436b36da0f.png":{"width":988,"height":316},"/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png":{"width":987,"height":633},"/content/papers/assets/e05ab32de27fdd8551794ded9ab71fafae0ea423979ef7250f9e7d31cedf1534.png":{"width":1000,"height":571}}}
