{"notes":[{"id":"classic-cnns/cam","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"CAM","subtitle":"Où regarde le modèle ?","author":"Z. Yuan","paperDate":"2015-12-14","publishedOn":"2026-01-05","world":"external/cam","paperTitle":"Learning Deep Features for Discriminative Localization","paperUrl":"https://arxiv.org/abs/1512.04150","bibliography":{"title":"Learning Deep Features for Discriminative Localization","authors":["Bolei Zhou","Aditya Khosla","Agata Lapedriza","Aude Oliva","Antonio Torralba"],"url":"https://arxiv.org/abs/1512.04150","pdfUrl":"https://arxiv.org/pdf/1512.04150v1","codeUrl":null,"preprint":{"id":"1512.04150","submittedOn":"2015-12-14","url":"https://arxiv.org/abs/1512.04150"},"publication":null,"citation":"@misc{classiccnnscam2015,\n  title = {{Learning Deep Features for Discriminative Localization}},\n  author = {Bolei Zhou and Aditya Khosla and Agata Lapedriza and Aude Oliva and Antonio Torralba},\n  year = {2015},\n  eprint = {1512.04150},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1512.04150}\n}\n","citationPath":"/content/papers/citations/classic-cnns/cam.2ce837236f9d.bib"},"thumbnail":{"src":"/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png","width":1372,"height":615},"locale":"fr","path":"/content/papers/fr/classic-cnns/cam.dc07c0da5f72.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":"redécomposer-le-score-dans-lespace","text":"Redécomposer le score dans l'espace","depth":3},{"id":"moyenne-ou-maximum-quelle-différence","text":"Moyenne ou maximum : quelle différence ?","depth":3},{"id":"augmenter-la-résolution-de-la-dernière-couche","text":"Augmenter la résolution de la dernière couche","depth":3},{"id":"de-la-carte-de-chaleur-à-la-boîte","text":"De la carte de chaleur à la boîte","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"sans-couches-entièrement-connectées-combien-perd-la-classification","text":"Sans couches entièrement connectées, combien perd la classification ?","depth":3},{"id":"localisation-faiblement-supervisée","text":"Localisation faiblement supervisée","depth":3},{"id":"vers-dautres-tâches","text":"Vers d'autres tâches","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/nin","relation":"referenced","topic":""},{"id":"classic-cnns/zfnet","relation":"referenced","topic":""},{"id":"classic-cnns/resnet","relation":"referenced","topic":""}]},{"id":"classic-cnns/nin","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"NiN","subtitle":"Le 1×1 en germe","author":"Z. Yuan","paperDate":"2013-12-16","publishedOn":"2025-09-24","world":"external/network-in-network","paperTitle":"Network In Network","paperUrl":"https://arxiv.org/abs/1312.4400","bibliography":{"title":"Network In Network","authors":["Min Lin","Qiang Chen","Shuicheng Yan"],"url":"https://arxiv.org/abs/1312.4400","pdfUrl":"https://arxiv.org/pdf/1312.4400v3","codeUrl":null,"preprint":{"id":"1312.4400","submittedOn":"2013-12-16","url":"https://arxiv.org/abs/1312.4400"},"publication":null,"citation":"@misc{classiccnnsnin2013,\n  title = {{Network In Network}},\n  author = {Min Lin and Qiang Chen and Shuicheng Yan},\n  year = {2013},\n  eprint = {1312.4400},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1312.4400}\n}\n","citationPath":"/content/papers/citations/classic-cnns/nin.db58639ac47c.bib"},"thumbnail":{"src":"/content/papers/assets/8024fc245eb852a2c0cd34b59c18c680b92766c33d621b934dbf102c1cacda13.png","width":896,"height":330},"locale":"fr","path":"/content/papers/fr/classic-cnns/nin.15718dc1a267.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":"un-petit-mlp-sur-chaque-patch","text":"Un petit MLP sur chaque patch","depth":3},{"id":"remplacer-les-couches-entièrement-connectées-par-une-moyenne","text":"Remplacer les couches entièrement connectées par une moyenne","depth":3},{"id":"le-réseau-complet","text":"Le réseau complet","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"cifar-10-et-cifar-100","text":"CIFAR-10 et CIFAR-100","depth":3},{"id":"svhn-et-mnist","text":"SVHN et MNIST","depth":3},{"id":"le-pooling-moyen-global-régularise-t-il-vraiment","text":"Le pooling moyen global régularise-t-il vraiment ?","depth":3},{"id":"la-dernière-couche-devient-elle-vraiment-une-carte-de-confiance","text":"La dernière couche devient-elle vraiment une carte de confiance ?","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/inception-v1","relation":"referenced","topic":""},{"id":"classic-cnns/resnet","relation":"referenced","topic":""},{"id":"classic-cnns/cam","relation":"referenced","topic":""}]},{"id":"classic-cnns/zfnet","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"ZFNet","subtitle":"Vue imprenable sur la boîte noire","author":"Z. Yuan","paperDate":"2013-11-12","publishedOn":"2025-09-06","world":"external/zfnet","paperTitle":"Visualizing and Understanding Convolutional Networks","paperUrl":"https://arxiv.org/abs/1311.2901","bibliography":{"title":"Visualizing and Understanding Convolutional Networks","authors":["Matthew D Zeiler","Rob Fergus"],"url":"https://arxiv.org/abs/1311.2901","pdfUrl":"https://arxiv.org/pdf/1311.2901v3","codeUrl":null,"preprint":{"id":"1311.2901","submittedOn":"2013-11-12","url":"https://arxiv.org/abs/1311.2901"},"publication":null,"citation":"@misc{classiccnnszfnet2013,\n  title = {{Visualizing and Understanding Convolutional Networks}},\n  author = {Matthew D Zeiler and Rob Fergus},\n  year = {2013},\n  eprint = {1311.2901},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1311.2901}\n}\n","citationPath":"/content/papers/citations/classic-cnns/zfnet.6d4dd35e9f91.bib"},"thumbnail":{"src":"/content/papers/assets/6c45d745e8a2f4b60cc45bea1029353e303d5e3bbee1e62f438c71874fddf883.png","width":1200,"height":1329},"locale":"fr","path":"/content/papers/fr/classic-cnns/zfnet.40936a0995d6.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":"parcourir-le-réseau-convolutif-à-rebours","text":"Parcourir le réseau convolutif à rebours","depth":3},{"id":"lunpooling-doit-se-souvenir-de-lemplacement-du-maximum","text":"L'unpooling doit se souvenir de l'emplacement du maximum","depth":3},{"id":"le-modèle-observé","text":"Le modèle observé","depth":3},{"id":"ce-que-voit-chaque-couche","text":"Ce que voit chaque couche","depth":3},{"id":"corriger-larchitecture-daprès-ce-quon-voit","text":"Corriger l'architecture d'après ce qu'on voit","depth":3},{"id":"masquer-une-zone-et-observer-le-classifieur","text":"Masquer une zone et observer le classifieur","depth":3},{"id":"correspondances-entre-images","text":"Correspondances entre images","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"imagenet-2012","text":"ImageNet 2012","depth":3},{"id":"quelles-couches-pèsent-le-plus","text":"Quelles couches pèsent le plus ?","depth":3},{"id":"changer-de-jeu-de-données","text":"Changer de jeu de données","depth":3},{"id":"les-couches-hautes-sont-elles-toujours-plus-utiles","text":"Les couches hautes sont-elles toujours plus utiles ?","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/alexnet","relation":"referenced","topic":""},{"id":"classic-cnns/vgg","relation":"referenced","topic":""},{"id":"classic-cnns/resnet","relation":"referenced","topic":""}]},{"id":"classic-cnns/resnet","category":"classic-cnns","legacyPath":"classic-cnns/1512-resnet","legacySlug":"1512-resnet","legacyPaths":[],"title":"ResNet","subtitle":"Un réseau convolutif de 100 couches","author":"Z. Yuan","paperDate":"2015-12-10","publishedOn":"2024-08-08","paperTitle":"Deep Residual Learning for Image Recognition","paperUrl":"https://arxiv.org/abs/1512.03385","bibliography":{"title":"Deep Residual Learning for Image Recognition","authors":["Kaiming He","Xiangyu Zhang","Shaoqing Ren","Jian Sun"],"url":"https://arxiv.org/abs/1512.03385","pdfUrl":"https://arxiv.org/pdf/1512.03385","codeUrl":null,"preprint":{"id":"1512.03385","submittedOn":"2015-12-10","url":"https://arxiv.org/abs/1512.03385"},"publication":{"venue":"CVPR","year":2016,"kind":"conference","url":"https://doi.org/10.1109/cvpr.2016.90"},"citation":"@misc{classiccnnsresnet2015,\n  title = {{Deep Residual Learning for Image Recognition}},\n  author = {Kaiming He and Xiangyu Zhang and Shaoqing Ren and Jian Sun},\n  year = {2015},\n  eprint = {1512.03385},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1512.03385}\n}\n","citationPath":"/content/papers/citations/classic-cnns/resnet.cf4014e8baae.bib"},"thumbnail":{"src":"/content/papers/assets/b8fe87fe757249ea6d7bb667fef3196754b9f80e28bf842a69b540ca94585d08.jpg","width":1224,"height":408},"locale":"fr","path":"/content/papers/fr/classic-cnns/resnet.2cc70ebed590.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":"quest-ce-quun-résidu-","text":"Qu'est-ce qu'un résidu ?","depth":3},{"id":"apprentissage-résiduel","text":"Apprentissage résiduel","depth":3},{"id":"blocs-résiduels","text":"Blocs résiduels","depth":3},{"id":"architecture-du-réseau","text":"Architecture du réseau","depth":3},{"id":"configuration-des-paramètres","text":"Configuration des paramètres","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"resnet-18-contre-resnet-34","text":"ResNet-18 contre ResNet-34","depth":3},{"id":"blocs-goulots-pour-les-réseaux-plus-profonds","text":"Blocs goulots pour les réseaux plus profonds","depth":3},{"id":"résultats-sur-imagenet","text":"Résultats sur ImageNet","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/vgg","relation":"background","topic":"Entraîner des réseaux convolutifs plus profonds"},{"id":"classic-cnns/densenet","relation":"next","topic":"Entraîner des réseaux convolutifs plus profonds"}]}],"text":"Un réseau de classification qui n'a jamais vu que des étiquettes de classe sait-il où se trouve l'objet ?\n\nDans leurs travaux de l'année précédente, Bolei Zhou et ses collègues du MIT avaient constaté que la réponse était le plus souvent oui : dans un réseau convolutif entraîné à classer des scènes, les unités de convolution de chaque couche se comportent comme des détecteurs d'objets, alors que personne ne leur a indiqué pendant l'entraînement où étaient les objets. Mais cette capacité disparaît dans les couches entièrement connectées. La sortie des couches de convolution conserve la position spatiale ; une fois aplatie et envoyée dans une couche entièrement connectée, le « où » et le « quoi » se mélangent et ne peuvent plus être séparés.\n\nEn décembre 2015, la même équipe (Bolei Zhou, Aditya Khosla, Agata Lapedriza, Aude Oliva, Antonio Torralba) publie cet article, présenté ensuite à CVPR 2016. Elle s'intéresse à une pièce qui existe déjà : le pooling moyen global, proposé par NiN et repris par GoogLeNet.\n\n\n> [!TIP]\n> \n> Pour l'origine du pooling moyen global et l'allure des cartes de caractéristiques de la dernière couche de NiN, vous pouvez commencer par notre note :\n> \n> - **[[13.12] NiN : Le 1×1 en germe](/blog/papers/classic-cnns/nin)**\n\n\n## Définir le problème\n\nCet article traite de **localisation d'objets faiblement supervisée** : à l'entraînement, on ne dispose que d'étiquettes de classe au niveau de l'image, mais au test, il faut encadrer la position de l'objet.\n\nPlusieurs approches existaient déjà. Certaines masquent l'image morceau par morceau pour voir quelle zone, une fois masquée, fait le plus chuter la réponse ; d'autres combinent l'apprentissage multi-instances et les caractéristiques convolutives ; d'autres encore font passer le réseau sur un grand nombre de petits patchs qui se chevauchent. Ces méthodes ne s'entraînent pas de bout en bout et demandent de nombreuses passes avant, ce qui les rend difficiles à appliquer à grande échelle.\n\nLa plus proche est celle d'Oquab et al., qui localise les objets avec un pooling **max** global. Elle ne trouve qu'un point sur la frontière de l'objet et ne peut pas en indiquer l'étendue.\n\nUne autre ligne de recherche voisine porte sur la visualisation des réseaux convolutifs. [ZFNet](/blog/papers/classic-cnns/zfnet) utilise la déconvolution pour voir quel motif cherche chaque unité, et les travaux antérieurs de Zhou et al. eux-mêmes ont révélé des détecteurs d'objets dans les réseaux de scènes ; toutes ces études n'analysent que les couches de convolution et laissent de côté les couches entièrement connectées.\n\nCe que veulent les auteurs : qu'avec une seule passe avant, le réseau puisse dire sur quelles régions de l'image il s'appuie pour décider de la classe.\n\n## Résoudre le problème\n\n### Redécomposer le score dans l'espace\n\nLe réseau utilisé par les auteurs a une structure proche de NiN et de GoogLeNet : surtout des couches de convolution, puis, après la dernière, un pooling moyen global, une couche entièrement connectée et un softmax.\n\nSoit $f_k(x, y)$ l'activation de la $k$-ième unité de la dernière couche de convolution à la position $(x, y)$. Le pooling moyen global réduit chaque carte de caractéristiques à un nombre :\n\n$$\nF_k = \\sum_{x,y} f_k(x, y)\n$$\n\nL'article l'écrit ici directement comme une somme, sans diviser par le nombre de positions ; ce facteur constant ne change rien à la suite du raisonnement.\n\nLe score de la classe $c$ est la somme pondérée de ces nombres :\n\n$$\nS_c = \\sum_k w_k^c F_k\n$$\n\n$w_k^c$ est un poids de la couche entièrement connectée et indique l'importance de la $k$-ième carte de caractéristiques pour la classe $c$. Les auteurs fixent à 0 le biais du softmax, en expliquant qu'il n'a presque aucun effet sur la classification.\n\nVient alors l'idée clé de l'article. Substituons $F_k$ et échangeons l'ordre des deux sommes :\n\n$$\nS_c = \\sum_k w_k^c \\sum_{x,y} f_k(x, y) = \\sum_{x,y} \\sum_k w_k^c f_k(x, y)\n$$\n\nLa somme intérieure $\\sum_k w_k^c f_k(x, y)$ porte encore la position $(x, y)$. Les auteurs la définissent comme la **carte d'activation de classe** (class activation map, CAM) de la classe $c$ :\n\n$$\nM_c(x, y) = \\sum_k w_k^c f_k(x, y)\n$$\n\nOn a donc $S_c = \\sum_{x,y} M_c(x, y)$. Le score de la classe est la somme de toutes les positions de la CAM, et $M_c(x, y)$ dit directement combien la position $(x, y)$ contribue à ce score.\n\nAutrement dit, la classification fait d'abord la moyenne, puis la pondération ; la CAM inverse l'ordre : elle pondère d'abord et garde l'espace sans faire la moyenne. Vérifions-le sur un petit exemple :\n\n```python\nimport torch\n\ntorch.manual_seed(0)\nK, C = 4, 3                                   # 4 cartes de caractéristiques, 3 classes\nf = torch.relu(torch.randn(K, 7, 7))          # sortie de la dernière couche de convolution : 4 × 7 × 7\nw = torch.randn(C, K)                         # couche entièrement connectée après le GAP : 4 poids par classe\n\n# Classification : moyenne d'abord, pondération ensuite\nscores = w @ f.mean(dim=(1, 2))               # [3]\n\n# CAM : pondération d'abord, en gardant l'espace\ncams = torch.einsum('ck,khw->chw', w, f)      # [3, 7, 7]\n\nprint(\"scores de classification :\", scores)\nprint(\"moyenne de chaque CAM :   \", cams.mean(dim=(1, 2)))\nprint(cams.shape)\n```\n\n```text\nscores de classification : tensor([-1.0399, -0.8521, -0.5210])\nmoyenne de chaque CAM :    tensor([-1.0399, -0.8521, -0.5210])\ntorch.Size([3, 7, 7])\n```\n\nLes deux séries de nombres sont identiques. La CAM n'utilise aucun nouveau paramètre et ne demande pas de refaire passer le réseau : elle se contente de « projeter » les poids déjà appris de la couche entièrement connectée sur les cartes de caractéristiques de la dernière couche de convolution.\n\n[![La moitié supérieure montre le réseau : une photo d'un garçon tenant un chien traverse une série de couches de convolution ; les cartes de caractéristiques de la dernière couche de convolution passent par le GAP et sont reliées à la classe Australian terrier par les poids w1, w2 jusqu'à wn. La moitié inférieure, intitulée Class Activation Mapping : w1, w2 et wn multiplient chacun la carte de caractéristiques correspondante, puis les résultats sont additionnés, ce qui donne une carte de chaleur superposée à l'image d'origine, la plus lumineuse sur le chien](/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png)](/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png)\n\n*Figure 2 de l'article, extraite de la [**page 3 du PDF v1**](https://arxiv.org/pdf/1512.04150v1#page=3). Le score de classe prédit est projeté sur la couche de convolution précédente, ce qui produit la carte d'activation de classe.*\n\nLa moitié inférieure de la figure correspond au calcul de $M_c$. Chaque carte de caractéristiques détecte un motif visuel particulier : l'une s'allume sur la tête du chien, une autre sur le corps, plus bas ; multipliées par les poids de la classe « Australian terrier » puis additionnées, elles donnent la carte de chaleur de cette classe. La dernière couche de convolution a une très faible résolution (dans l'article, $13\\times13$ ou $14\\times14$) ; en agrandissant la CAM à la taille de l'image d'entrée, on voit quelles régions de l'image sont les plus liées à la classe.\n\nSur la même image, changer de classe revient à changer de jeu de poids, et la CAM change avec lui :\n\n[![En haut à gauche, une photo nocturne d'un bâtiment à dôme, intitulée dome. Les cinq autres cases montrent les CAM et les scores des cinq premières classes prédites : palace 0,459 s'allume sur le corps horizontal du palais, en bas ; dome 0,195 sur le dôme, en haut ; pour church 0,146, altar 0,091 et monastery 0,051, les zones lumineuses se concentrent en bas à gauche du bâtiment et autour de la flèche](/content/papers/assets/5feae5d21639fe423e9457b6eee11d7c4357e0fab4014dac2dd2482a810f0c45.png)](/content/papers/assets/5feae5d21639fe423e9457b6eee11d7c4357e0fab4014dac2dd2482a810f0c45.png)\n\n*Figure 4 de l'article, extraite de la [**page 4 du PDF v1**](https://arxiv.org/pdf/1512.04150v1#page=4). La vraie classe est dome (dôme) ; les cinq autres cases montrent les CAM des cinq premières classes prédites, avec la classe et le score au-dessus de chacune.*\n\nPour palace (palais), la carte s'allume sur le corps du bâtiment, plat et bas ; pour dome, sur le dôme, en haut. Pour juger de ces deux classes, le réseau regarde des endroits différents de la même image.\n\n### Moyenne ou maximum : quelle différence ?\n\nComme Oquab et al. utilisaient le pooling max global, les auteurs expliquent en détail pourquoi ils choisissent la moyenne.\n\nLeur intuition : avec la moyenne, le meilleur moyen d'augmenter le score est de trouver **toutes** les parties discriminantes de l'objet, puisque toute activation faible tire la moyenne vers le bas ; avec le maximum, seule la position la plus forte compte, et les autres n'ont aucune influence, qu'elles soient hautes ou basses.\n\nC'est à la rétropropagation que cette différence se voit le mieux :\n\n```python\nimport torch\n\ntorch.manual_seed(0)\nx = torch.rand(1, 1, 4, 4, requires_grad=True)\n\nx.mean().backward()                           # pooling moyen global\nprint(\"gradient du GAP :\\n\", x.grad[0, 0])\n\nx.grad = None\nx.amax().backward()                           # max pooling global\nprint(\"gradient du GMP :\\n\", x.grad[0, 0])\n```\n\n```text\ngradient du GAP :\n tensor([[0.0625, 0.0625, 0.0625, 0.0625],\n        [0.0625, 0.0625, 0.0625, 0.0625],\n        [0.0625, 0.0625, 0.0625, 0.0625],\n        [0.0625, 0.0625, 0.0625, 0.0625]])\ngradient du GMP :\n tensor([[0., 0., 0., 0.],\n        [0., 0., 0., 1.],\n        [0., 0., 0., 0.],\n        [0., 0., 0., 0.]])\n```\n\nLe pooling moyen répartit le gradient à parts égales entre les 16 positions, et chacune est entraînée ; le pooling max n'envoie le gradient qu'à la case la plus grande, et les 15 autres n'apprennent rien à cette étape. Les expériences vérifieront plus loin si cette intuition tient pour la localisation.\n\n### Augmenter la résolution de la dernière couche\n\nLes auteurs modifient trois réseaux, AlexNet, VGGnet et GoogLeNet : ils retirent les dernières couches entièrement connectées et les remplacent par un GAP suivi d'une couche softmax entièrement connectée.\n\nIls constatent que plus la résolution spatiale de la dernière couche de convolution avant le GAP est élevée, meilleure est la localisation. Les auteurs appellent cette résolution la mapping resolution. Pour l'augmenter, ils suppriment encore quelques couches :\n\n- **AlexNet** : toutes les couches après conv5 sont supprimées, ce qui donne $13\\times13$.\n- **VGGnet** : toutes les couches après conv5-3 sont supprimées, ce qui donne $14\\times14$.\n- **GoogLeNet** : toutes les couches après inception4e sont supprimées, ce qui donne $14\\times14$.\n\nChaque réseau reçoit ensuite une couche de convolution $3\\times3$, de pas 1, avec un remplissage de 1 et 1024 unités, suivie du GAP et du softmax, puis il est affiné sur la classification à 1000 classes avec les 1,3 million d'images d'entraînement de l'ILSVRC. Les réseaux obtenus s'appellent AlexNet-GAP, VGGnet-GAP et GoogLeNet-GAP. Les auteurs indiquent en note de bas de page qu'un entraînement depuis zéro donne des résultats similaires.\n\n### De la carte de chaleur à la boîte\n\nL'évaluation de la localisation de l'ILSVRC demande une boîte. La méthode des auteurs est très simple :\n\n1. Agrandir la CAM à la taille de l'image.\n2. Garder les régions dont la valeur dépasse 20 % du maximum.\n3. Prendre la plus grande composante connexe et tracer sa boîte englobante.\n\nFaisons l'essai avec une fausse CAM de $14\\times14$, qui présente une grande réponse à gauche et une petite en haut à droite :\n\n```python\nimport torch\nimport torch.nn.functional as F\nfrom scipy import ndimage\n\n# Une CAM 14×14 : une grande réponse à gauche, une petite en haut à droite\nyy, xx = torch.meshgrid(torch.arange(14.), torch.arange(14.), indexing='ij')\ncam = 1.0 * torch.exp(-((yy - 8) ** 2 / 6 + (xx - 4) ** 2 / 4)) \\\n    + 0.6 * torch.exp(-((yy - 2) ** 2 + (xx - 11) ** 2) / 1.5)\n\n# 1. Agrandir à la taille de l'image d'entrée\ncam = F.interpolate(cam[None, None], size=(224, 224), mode='bilinear', align_corners=False)[0, 0]\n\n# 2. Garder les zones qui dépassent 20 % du maximum\nmask = (cam > 0.2 * cam.max()).numpy()\n\n# 3. Trouver la plus grande composante connexe et prendre sa boîte englobante\nlabels, n = ndimage.label(mask)\nsizes = ndimage.sum(mask, labels, range(1, n + 1))\nlargest = labels == (sizes.argmax() + 1)\nrows, cols = largest.any(axis=1).nonzero()[0], largest.any(axis=0).nonzero()[0]\n\nprint(\"composantes connexes :\", n, \", pixels de chacune :\", sizes.astype(int).tolist())\nprint(\"boîte (x0, y0, x1, y1) :\", cols[0], rows[0], cols[-1], rows[-1])\n```\n\n```text\ncomposantes connexes : 2 , pixels de chacune : [1344, 6552]\nboîte (x0, y0, x1, y1) : 30 85 113 186\n```\n\nLa zone en haut à droite dépasse elle aussi le seuil, mais elle est petite, et la boîte finale n'entoure que la grande région de gauche. On voit aussi la limite de la méthode : quand une image contient deux objets, un seul est encadré. Pour l'évaluation top-5 de la localisation, l'opération est répétée pour chacune des cinq premières classes prédites.\n\n## Discussion\n\n### Sans couches entièrement connectées, combien perd la classification ?\n\n| Réseau | Erreur top-1 | Erreur top-5 |\n| --- | ---: | ---: |\n| AlexNet | 42,6 | 19,5 |\n| AlexNet-GAP | 51,1 | 26,3 |\n| AlexNet\\*-GAP | 44,9 | 20,9 |\n| VGGnet | 31,2 | 11,4 |\n| VGGnet-GAP | 33,4 | 12,2 |\n| GoogLeNet | 31,9 | 11,3 |\n| GoogLeNet-GAP | 35,0 | 13,2 |\n| GoogLeNet-GMP | 35,6 | 13,9 |\n| NIN | 41,9 | 19,6 |\n\n*Tableau : extrait de la Table 1 de l'article, ensemble de validation de l'ILSVRC, en pourcentage ; l'ordre des lignes a été modifié pour faciliter la comparaison avec les réseaux d'origine.*\n\nUne fois quelques couches retirées, l'erreur top-1 de VGGnet et de GoogLeNet augmente respectivement de 2,2 et 3,1 points de pourcentage, et leur erreur top-5 de moins de 2 points dans les deux cas. AlexNet est le plus touché : son erreur top-1 passe de 42,6 % à 51,1 % ; en ajoutant deux couches de convolution avant le GAP, les auteurs obtiennent AlexNet\\*-GAP, qui fait à peu près jeu égal avec l'AlexNet d'origine. GAP et GMP se valent aussi en classification, comme prévu.\n\nLa classification ne doit pas trop se dégrader, car l'évaluation de la localisation exige que la classe et la boîte soient toutes deux justes.\n\n### Localisation faiblement supervisée\n\n| Méthode | Erreur top-1 | Erreur top-5 |\n| --- | ---: | ---: |\n| **GoogLeNet-GAP** | **56,40** | **43,00** |\n| VGGnet-GAP | 57,20 | 45,14 |\n| GoogLeNet-GMP | 57,78 | 45,26 |\n| GoogLeNet | 60,09 | 49,34 |\n| AlexNet\\*-GAP | 63,75 | 49,53 |\n| Backprop on GoogLeNet | 61,31 | 50,55 |\n| Backprop on VGGnet | 61,12 | 51,46 |\n| AlexNet-GAP | 67,19 | 52,16 |\n| Backprop on AlexNet | 65,17 | 52,64 |\n| NIN | 65,47 | 54,19 |\n\n*Tableau : extrait de la Table 2 de l'article ; taux d'erreur de localisation sur l'ensemble de validation de l'ILSVRC, triés par erreur top-5. Backprop désigne le remplacement de la CAM par la carte de saillance par rétropropagation de Simonyan et al.*\n\nQuelques comparaisons :\n\n- **CAM contre rétropropagation** : sur GoogLeNet et VGGnet, l'erreur top-5 de la CAM est inférieure de plus de 6 points de pourcentage à celle de la rétropropagation ; sur AlexNet, les deux sont proches (52,16 contre 52,64), et en top-1, c'est même la rétropropagation qui fait un peu mieux.\n- **GoogLeNet-GAP contre GoogLeNet** : le GoogLeNet d'origine classe mieux, mais localise moins bien que GoogLeNet-GAP, amputé de quelques couches. Les auteurs l'attribuent à la dernière couche du GoogLeNet d'origine, qui n'est qu'en $7\\times7$ : une résolution trop faible.\n- **GAP contre GMP** : à classification à peu près égale, le GAP réduit l'erreur top-5 de localisation de 2,26 points de pourcentage, ce qui appuie l'intuition présentée plus haut sur le pooling moyen.\n\n[![Deux groupes, à gauche et à droite. a) Quatre exemples, chacun avec une image d'origine et sa CAM : alligator américain, labyrinthe, berger des Shetland, sauterelle ; sur l'image d'origine, la boîte verte est l'annotation et la rouge celle déduite de la CAM, les deux se recouvrant à peu près. b) Les deux exemples du haut montrent la localisation par GoogLeNet-GAP d'une motoneige et d'un tablier, avec une carte de chaleur concentrée sur l'objet ; les deux exemples du bas montrent la rétropropagation sur AlexNet, avec une carte de chaleur en points épars et une boîte déduite trop grande](/content/papers/assets/dce42d560109e416d7229359699d2bca63f7ec9a74374b2d62fce387c9bbed22.png)](/content/papers/assets/dce42d560109e416d7229359699d2bca63f7ec9a74374b2d62fce387c9bbed22.png)\n\n*Figure 6 de l'article, extraite de la [**page 6 du PDF v1**](https://arxiv.org/pdf/1512.04150v1#page=6). a) Exemples de localisation par GoogLeNet-GAP ; b) en haut, deux exemples de GoogLeNet-GAP, en bas, deux exemples de rétropropagation sur AlexNet. Les boîtes vertes sont les annotations, les rouges celles déduites de la carte de chaleur.*\n\nLa comparaison de droite parle d'elle-même : la carte de saillance par rétropropagation est un semis de points lumineux, et une fois seuillée, elle donne une boîte trop grande ; la zone chaude de la CAM est concentrée, et la boîte colle davantage à l'objet.\n\nSur l'ensemble de test, les auteurs changent de stratégie de sélection des boîtes : pour chacune des deux premières classes, une boîte serrée et une boîte large, et pour la troisième, une boîte large ; cette heuristique a été réglée sur l'ensemble de validation.\n\n| Méthode | Supervision | Erreur top-5 en test |\n| --- | --- | ---: |\n| GoogLeNet-GAP (sélection heuristique des boîtes) | Faible | **37,1** |\n| GoogLeNet-GAP | Faible | 42,9 |\n| Backprop | Faible | 46,4 |\n| GoogLeNet | Complète | 26,7 |\n| OverFeat | Complète | 29,9 |\n| AlexNet | Complète | 34,2 |\n\n*Tableau : extrait de la Table 3 de l'article, ensemble de test de l'ILSVRC.*\n\n37,1 % s'approche déjà de l'AlexNet entièrement supervisé (34,2 %), alors que GoogLeNet-GAP n'a vu aucune boîte pendant l'entraînement. Les auteurs reconnaissent que l'écart reste important : avec la même architecture GoogLeNet, la supervision complète atteint 26,7 %.\n\n### Vers d'autres tâches\n\nLes auteurs veulent aussi savoir si ces caractéristiques capables de localiser peuvent servir directement sur d'autres jeux de données. Ils figent GoogLeNet-GAP, entraînent un SVM linéaire sur la sortie du GAP, puis calculent la CAM avec les poids du SVM à la place de ceux du softmax d'origine.\n\nUtilisées comme caractéristiques d'image génériques, celles de GoogLeNet-GAP donnent sur huit jeux de données, dont SUN397, MIT Indoor67 et Caltech256, des résultats proches de ceux de la couche de pooling moyen du GoogLeNet d'origine, et le plus souvent nettement meilleurs que ceux de la fc7 d'AlexNet.\n\nLe jeu de données d'oiseaux CUB-200-2011, pour la classification fine, est plus intéressant. Il compte 200 espèces d'oiseaux, 5 994 images d'entraînement et 5 794 images de test, avec en plus des boîtes autour des oiseaux.\n\n| Méthode | Annotations utilisées à l'entraînement / au test | Précision |\n| --- | --- | ---: |\n| GoogLeNet-GAP, image entière | Aucune | 63,0 % |\n| GoogLeNet-GAP, boîte déduite de la CAM | Aucune | 67,8 % |\n| GoogLeNet-GAP, boîte annotée | Boîtes | 70,5 % |\n| PANDA R-CNN | Boîtes + parties | 76,4 % |\n\n*Tableau : quelques lignes extraites de la Table 4 de l'article.*\n\nSi l'on encadre d'abord l'oiseau avec la CAM, puis qu'on extrait de nouveau les caractéristiques dans cette boîte, la précision passe de 63,0 % à 67,8 %, sans aucune annotation. Les boîtes déduites de la CAM sont justes dans 41,0 % des cas selon le critère IoU 0,5, contre 5,5 % au hasard. En classification fine, les différences tiennent souvent à quelques détails ; concentrer d'abord l'attention sur l'oiseau aide nettement.\n\nLa dernière série d'expériences consiste à « découvrir des motifs » : à partir d'un groupe d'images partageant un même concept, on regarde si la CAM s'allume sur ce concept. L'un des exemples concerne la détection de texte :\n\n[![En haut, six photos de devantures de magasins dans la rue ; en bas, les CAM correspondantes. Les zones chaudes tombent toutes sur le texte des enseignes, comme le nom du magasin sur un auvent ou l'enseigne au-dessus de la porte](/content/papers/assets/89c6a4410389b611bdcc7e40a414223d52106f89494e17ede86188b4552d73a0.png)](/content/papers/assets/89c6a4410389b611bdcc7e40a414223d52106f89494e17ede86188b4552d73a0.png)\n\n*Figure 11 de l'article, extraite de la [**page 8 du PDF v1**](https://arxiv.org/pdf/1512.04150v1#page=8). Un détecteur de texte faiblement supervisé : le réseau n'a été entraîné ni avec du texte ni avec la moindre boîte, et la CAM s'allume pourtant sur le texte.*\n\nLes exemples positifs sont 350 images Google Street View contenant du texte, tirées du jeu de données SVT ; les négatifs sont des scènes extérieures tirées au hasard dans le jeu de données SUN. Avec la seule étiquette binaire « cette image contient-elle du texte ? », la CAM se pose sur le texte des enseignes. L'article essaie aussi la localisation d'objets clés dans des scènes et de concepts exprimés par des expressions, ainsi que l'explication des réponses d'un modèle de question-réponse visuelle.\n\nLes auteurs font encore autre chose : avec les poids qui suivent le GAP, ils classent les unités de la dernière couche de convolution selon leur importance pour une classe, ce qui montre directement quelles unités comptent le plus pour juger de cette classe. Par exemple, pour le lakeland terrier, une race de terrier, les unités importantes détectent la tête du chien et son pelage ; pour le salon, ce sont le canapé, la table et la cheminée.\n\n## Conclusion\n\nLa CAM elle-même tient en une formule : multiplier les poids de la couche de classification par les cartes de caractéristiques de la dernière couche. Elle fonctionne parce que le GAP n'est suivi que d'une seule couche linéaire, ce qui permet de redécomposer le score de classe position par position. C'est aussi sa condition : le réseau doit avoir une structure « convolutions, GAP, une couche entièrement connectée » ; les autres réseaux doivent d'abord être modifiés et réaffinés, ce qui peut faire baisser leur précision de classification.\n\nExpérimentalement, la CAM obtient 37,1 % d'erreur top-5 en localisation faiblement supervisée sur l'ILSVRC, un niveau proche de l'AlexNet entièrement supervisé, mais encore assez loin du GoogLeNet entièrement supervisé de même architecture. Comme elle ne garde par image que la boîte de la plus grande composante connexe, elle convient mieux aux images qui contiennent un seul objet principal.\n\nLa contrainte d'architecture de la CAM a vite été levée : en octobre 2016, [Grad-CAM](https://arxiv.org/abs/1610.02391) utilise les gradients pour fixer le poids de chaque carte de caractéristiques, sans passer à une architecture GAP ni réentraîner.\n\nLa CAM est très accessible : il suffit que le réseau se termine par un GAP suivi d'une couche entièrement connectée pour la calculer en quelques lignes. Le [ResNet](/blog/papers/classic-cnns/resnet) qu'on rencontre partout aujourd'hui a justement cette structure ; pour savoir où regarde le modèle, il suffit d'en tracer une.\n\nMais ne prenez pas les cartes de chaleur trop au sérieux. Elles signalent les parties les plus discriminantes, comme la tête d'un animal, et ne couvrent souvent qu'une partie de l'objet.\n","imageSizes":{"/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png":{"width":1372,"height":615},"/content/papers/assets/5feae5d21639fe423e9457b6eee11d7c4357e0fab4014dac2dd2482a810f0c45.png":{"width":672,"height":462},"/content/papers/assets/dce42d560109e416d7229359699d2bca63f7ec9a74374b2d62fce387c9bbed22.png":{"width":1381,"height":304},"/content/papers/assets/89c6a4410389b611bdcc7e40a414223d52106f89494e17ede86188b4552d73a0.png":{"width":669,"height":235}}}
