{"notes":[{"id":"classic-cnns/xception","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"Xception","subtitle":"Au bout d'Inception","author":"Z. Yuan","paperDate":"2016-10-07","publishedOn":"2024-03-20","world":"external/xception","paperTitle":"Xception: Deep Learning with Depthwise Separable Convolutions","paperUrl":"https://arxiv.org/abs/1610.02357","bibliography":{"title":"Xception: Deep Learning with Depthwise Separable Convolutions","authors":["François Chollet"],"url":"https://arxiv.org/abs/1610.02357","pdfUrl":"https://arxiv.org/pdf/1610.02357v3","codeUrl":null,"preprint":{"id":"1610.02357","submittedOn":"2016-10-07","url":"https://arxiv.org/abs/1610.02357"},"publication":{"venue":"CVPR","year":2017,"kind":"conference","url":"https://openaccess.thecvf.com/content_cvpr_2017/html/Chollet_Xception_Deep_Learning_CVPR_2017_paper.html"},"citation":"@misc{classiccnnsxception2016,\n  title = {{Xception: Deep Learning with Depthwise Separable Convolutions}},\n  author = {François Chollet},\n  year = {2016},\n  eprint = {1610.02357},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1610.02357}\n}\n","citationPath":"/content/papers/citations/classic-cnns/xception.3a6fa7188c36.bib"},"thumbnail":{"src":"/content/papers/assets/b56e8b540dd5a4cb9856460b3e47a6e3e624df308ab08b70809438075caf5f0a.png","width":917,"height":538},"locale":"fr","path":"/content/papers/fr/classic-cnns/xception.c7752fc8989d.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-autre-regard-sur-le-module-inception","text":"Un autre regard sur le module Inception","depth":3},{"id":"diviser-jusquau-bout","text":"Diviser jusqu'au bout","depth":3},{"id":"lautre-extrémité-du-spectre","text":"L'autre extrémité du spectre","depth":3},{"id":"xception","text":"Xception","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"imagenet","text":"ImageNet","depth":3},{"id":"jft","text":"JFT","depth":3},{"id":"autant-de-paramètres-un-peu-plus-lent","text":"Autant de paramètres, un peu plus lent","depth":3},{"id":"les-connexions-résiduelles","text":"Les connexions résiduelles","depth":3},{"id":"une-activation-au-milieu","text":"Une activation au milieu ?","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/inception-v1","relation":"referenced","topic":""},{"id":"classic-cnns/inception-v3","relation":"referenced","topic":""},{"id":"lightweight/mobilenet-v1","relation":"referenced","topic":""}]},{"id":"classic-cnns/inception-v1","category":"classic-cnns","legacyPath":"classic-cnns/1409-inception-v1","legacySlug":"1409-inception-v1","legacyPaths":[],"title":"GoogLeNet","subtitle":"Ou encore Inception-V1","author":"Z. Yuan","paperDate":"2014-09-17","publishedOn":"2024-09-22","paperTitle":"Going Deeper with Convolutions","paperUrl":"https://arxiv.org/abs/1409.4842","bibliography":{"title":"Going Deeper with Convolutions","authors":["Christian Szegedy","Wei Liu","Yangqing Jia","Pierre Sermanet","Scott Reed","Dragomir Anguelov","Dumitru Erhan","Vincent Vanhoucke","Andrew Rabinovich"],"url":"https://arxiv.org/abs/1409.4842","pdfUrl":"https://arxiv.org/pdf/1409.4842","codeUrl":null,"preprint":{"id":"1409.4842","submittedOn":"2014-09-17","url":"https://arxiv.org/abs/1409.4842"},"publication":{"venue":"CVPR","year":2015,"kind":"conference","url":"https://doi.org/10.1109/cvpr.2015.7298594"},"citation":"@misc{classiccnnsinceptionv12014,\n  title = {{Going Deeper with Convolutions}},\n  author = {Christian Szegedy and Wei Liu and Yangqing Jia and Pierre Sermanet and Scott Reed and Dragomir Anguelov and Dumitru Erhan and Vincent Vanhoucke and Andrew Rabinovich},\n  year = {2014},\n  eprint = {1409.4842},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1409.4842}\n}\n","citationPath":"/content/papers/citations/classic-cnns/inception-v1.3d20e0e32bcd.bib"},"thumbnail":{"src":"/content/papers/assets/254b970b1374c24267b60c2d684338760e9ba8b10dad761a5fa15f1bb8bcf6ef.jpg","width":1330,"height":394},"locale":"fr","path":"/content/papers/fr/classic-cnns/inception-v1.8e21e48f0201.md","headings":[{"id":"résoudre-le-problème","text":"Résoudre le problème","depth":2},{"id":"le-module-inception","text":"Le module Inception","depth":3},{"id":"configuration-des-paramètres","text":"Configuration des paramètres","depth":3},{"id":"architecture-du-modèle","text":"Architecture du modèle","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"résultats-à-lilsvrc-2014","text":"Résultats à l'ILSVRC 2014","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/resnext","relation":"comparison","topic":"Branches, convolutions groupées et réutilisation des caractéristiques"},{"id":"classic-cnns/cspnet","relation":"comparison","topic":"Branches, convolutions groupées et réutilisation des caractéristiques"}]},{"id":"classic-cnns/inception-v3","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"Inception-v3","subtitle":"Les grands noyaux tirent leur révérence","author":"Z. Yuan","paperDate":"2015-12-02","publishedOn":"2023-11-15","world":"external/inception-v3","paperTitle":"Rethinking the Inception Architecture for Computer Vision","paperUrl":"https://arxiv.org/abs/1512.00567","bibliography":{"title":"Rethinking the Inception Architecture for Computer Vision","authors":["Christian Szegedy","Vincent Vanhoucke","Sergey Ioffe","Jonathon Shlens","Zbigniew Wojna"],"url":"https://arxiv.org/abs/1512.00567","pdfUrl":"https://arxiv.org/pdf/1512.00567v3","codeUrl":null,"preprint":{"id":"1512.00567","submittedOn":"2015-12-02","url":"https://arxiv.org/abs/1512.00567"},"publication":{"venue":"CVPR","year":2016,"kind":"conference","url":"https://openaccess.thecvf.com/content_cvpr_2016/html/Szegedy_Rethinking_the_Inception_CVPR_2016_paper.html"},"citation":"@misc{classiccnnsinceptionv32015,\n  title = {{Rethinking the Inception Architecture for Computer Vision}},\n  author = {Christian Szegedy and Vincent Vanhoucke and Sergey Ioffe and Jonathon Shlens and Zbigniew Wojna},\n  year = {2015},\n  eprint = {1512.00567},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1512.00567}\n}\n","citationPath":"/content/papers/citations/classic-cnns/inception-v3.5636c0bddf2d.bib"},"thumbnail":{"src":"/content/papers/assets/c87d3d6fa9ea76d954eba2570f702150eca32d62c7cdb3d724075a4be9a63080.png","width":419,"height":358},"locale":"fr","path":"/content/papers/fr/classic-cnns/inception-v3.448d0dcc2c16.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":"remplacer-un-55-par-deux-couches-33","text":"Remplacer un 5×5 par deux couches 3×3","depth":3},{"id":"décomposer-encore-en-1n-et-n1","text":"Décomposer encore en 1×n et n×1","depth":3},{"id":"le-véritable-rôle-des-classifieurs-auxiliaires","text":"Le véritable rôle des classifieurs auxiliaires","depth":3},{"id":"réduire-les-cartes-de-caractéristiques-sans-étrangler-linformation","text":"Réduire les cartes de caractéristiques sans étrangler l'information","depth":3},{"id":"assembler-les-pièces","text":"Assembler les pièces","depth":3},{"id":"rendre-le-modèle-moins-sûr-de-lui","text":"Rendre le modèle moins sûr de lui","depth":3},{"id":"réglages-dentraînement","text":"Réglages d'entraînement","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"ajouter-les-modifications-une-à-une","text":"Ajouter les modifications une à une","depth":3},{"id":"avec-une-résolution-plus-faible-combien-perd-on","text":"Avec une résolution plus faible, combien perd-on ?","depth":3},{"id":"comparaison-avec-dautres-modèles","text":"Comparaison avec d'autres modèles","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/inception-v1","relation":"referenced","topic":""},{"id":"normalization/batchnorm","relation":"referenced","topic":""},{"id":"classic-cnns/convnext","relation":"referenced","topic":""}]},{"id":"lightweight/mobilenet-v1","category":"lightweight","legacyPath":"lightweight/1704-mobilenet-v1","legacySlug":"1704-mobilenet-v1","legacyPaths":[],"title":"MobileNet-V1","subtitle":"Celui qui a popularisé la convolution séparable en profondeur","author":"Z. Yuan","paperDate":"2017-04-17","publishedOn":"2024-06-25","paperTitle":"MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications","paperUrl":"https://arxiv.org/abs/1704.04861","bibliography":{"title":"MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications","authors":["Andrew G. Howard","Menglong Zhu","Bo Chen","Dmitry Kalenichenko","Weijun Wang","Tobias Weyand","Marco Andreetto","Hartwig Adam"],"url":"https://arxiv.org/abs/1704.04861","pdfUrl":"https://arxiv.org/pdf/1704.04861","codeUrl":null,"preprint":{"id":"1704.04861","submittedOn":"2017-04-17","url":"https://arxiv.org/abs/1704.04861"},"publication":null,"citation":"@misc{lightweightmobilenetv12017,\n  title = {{MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications}},\n  author = {Andrew G. Howard and Menglong Zhu and Bo Chen and Dmitry Kalenichenko and Weijun Wang and Tobias Weyand and Marco Andreetto and Hartwig Adam},\n  year = {2017},\n  eprint = {1704.04861},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1704.04861}\n}\n","citationPath":"/content/papers/citations/lightweight/mobilenet-v1.e9c4c525fed4.bib"},"thumbnail":{"src":"/content/papers/assets/b8390bd8e0f21f1b1eaa6b2a5d4a1b0d9eba36e2fe5dd0878830d757839d30f1.jpg","width":1064,"height":1080},"locale":"fr","path":"/content/papers/fr/lightweight/mobilenet-v1.68f1f6423123.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":"convolution-séparable-en-profondeur","text":"Convolution séparable en profondeur","depth":3},{"id":"architecture-du-réseau","text":"Architecture du réseau","depth":2},{"id":"choix-dentraînement","text":"Choix d'entraînement","depth":2},{"id":"discussion","text":"Discussion","depth":2},{"id":"performance-de-la-convolution-séparable-en-profondeur","text":"Performance de la convolution séparable en profondeur","depth":3},{"id":"vaut-il-mieux-réduire-la-profondeur-ou-la-largeur-","text":"Vaut-il mieux réduire la profondeur ou la largeur ?","depth":3},{"id":"comparaison-avec-dautres-modèles","text":"Comparaison avec d'autres modèles","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"lightweight/mobilenet-v2","relation":"next","topic":"Convolutions efficaces sur mobile"}]}],"text":"En 2016, Inception en est déjà à sa quatrième génération, de GoogLeNet jusqu'à Inception-ResNet, qui y greffe les résidus, en passant par Inception-v2, qui ajoute la BN, et par Inception-v3, qui factorise les convolutions. Chaque génération se classe parmi les meilleures sur ImageNet, mais les modules se compliquent d'une génération à l'autre, avec des branches, des nombres de canaux et des formes de noyau à régler un par un.\n\n\n> [!TIP]\n> \n> Pour la série Inception, vous pouvez commencer par ces deux notes :\n> \n> - **[[14.09] GoogLeNet : Ou encore Inception-V1](/blog/papers/classic-cnns/inception-v1)**\n> - **[[15.12] Inception-v3 : Les grands noyaux tirent leur révérence](/blog/papers/classic-cnns/inception-v3)**\n\n\nFrançois Chollet, l'auteur de Keras, travaille alors chez Google. Il veut comprendre pourquoi le module Inception fonctionne, en quoi il diffère d'une convolution ordinaire, et dans quelle direction Inception peut encore aller ensuite.\n\nCet article est sa réponse.\n\n## Définir le problème\n\nDans une couche de convolution ordinaire, chaque noyau est un filtre en volume, qui s'étend sur la dimension des canaux en plus de la largeur et de la hauteur. Un noyau $3\\times3$ qui regarde 256 canaux d'entrée compte $3\\times3\\times256$ poids ; il traite **à la fois** les relations entre les différents canaux d'une même position (les corrélations inter-canaux) et les relations entre positions voisines (les corrélations spatiales).\n\nLe module Inception sépare ces deux tâches. Il traite d'abord les relations entre canaux avec quelques convolutions $1\\times1$, qui projettent l'entrée dans 3 ou 4 espaces plus petits, puis traite les relations spatiales dans chacun de ces petits espaces avec des convolutions $3\\times3$ ou $5\\times5$.\n\nL'auteur appelle l'idée qui sous-tend cette conception l'**hypothèse Inception**, selon laquelle les corrélations inter-canaux et les corrélations spatiales sont assez indépendantes pour qu'il vaille mieux les traiter séparément qu'ensemble.\n\nSi « séparer en partie » fonctionne, qu'en est-il d'une séparation plus poussée ?\n\n## Résoudre le problème\n\n### Un autre regard sur le module Inception\n\nL'auteur commence par simplifier le module Inception, en ne gardant qu'un seul type de convolution $3\\times3$ et en retirant la branche de pooling moyen. Restent trois branches, chacune faite d'une $1\\times1$ suivie d'une $3\\times3$.\n\nCes trois convolutions $1\\times1$ regardent la même entrée ; on peut donc les fusionner en une seule convolution $1\\times1$ avec davantage de canaux de sortie. Après la fusion, les trois convolutions $3\\times3$ traitent chacune un segment de ces canaux de sortie :\n\n[![Forme équivalente du module Inception simplifié, de bas en haut : Input entre dans une 1x1 conv, dont les canaux de sortie (Output channels) sont divisés par des accolades en trois segments ; chaque segment est suivi d'une 3x3 conv, et le tout se termine par Concat](/content/papers/assets/9fc3c2cfbe884067fa627f67d745ee0ab6eaf44ed451260083932eb5fa0742e0.png)](/content/papers/assets/9fc3c2cfbe884067fa627f67d745ee0ab6eaf44ed451260083932eb5fa0742e0.png)\n\n*Figure 3 de l'article, extraite de la [**page 2 du PDF v3**](https://arxiv.org/pdf/1610.02357v3#page=2). Une écriture strictement équivalente au module Inception simplifié.*\n\nLes deux écritures sont-elles vraiment identiques ? Essayons avec une entrée à 64 canaux. D'un côté, les trois branches font chacune une $1\\times1$ (de 64 à 32) puis une $3\\times3$ ; de l'autre, une convolution $1\\times1$ de 64 à 96 est suivie d'une convolution qui « divise les 96 canaux en trois groupes, chaque groupe faisant sa propre $3\\times3$ », avec des poids copiés directement depuis les trois branches :\n\n```python\nimport torch\nimport torch.nn as nn\n\ntorch.manual_seed(0)\nx = torch.randn(1, 64, 8, 8)                       # entrée à 64 canaux\n\n# Figure 2 : trois branches, chacune une 1×1 puis une 3×3\npw = [nn.Conv2d(64, 32, 1, bias=False) for _ in range(3)]\nsp = [nn.Conv2d(32, 32, 3, padding=1, bias=False) for _ in range(3)]\ny2 = torch.cat([s(p(x)) for p, s in zip(pw, sp)], dim=1)\n\n# Figure 3 : une grande convolution 1×1, puis les 96 canaux de sortie découpés en trois segments, chacun avec sa propre 3×3\nbig_pw = nn.Conv2d(64, 96, 1, bias=False)\ngrouped = nn.Conv2d(96, 96, 3, padding=1, groups=3, bias=False)\nwith torch.no_grad():\n    big_pw.weight.copy_(torch.cat([p.weight for p in pw]))\n    grouped.weight.copy_(torch.cat([s.weight for s in sp]))\ny3 = grouped(big_pw(x))\n\nprint(\"formes de sortie :\", tuple(y2.shape), tuple(y3.shape))\nprint(\"écart maximal entre les deux écritures :\", (y2 - y3).abs().max().item())\n```\n\n```text\nformes de sortie : (1, 96, 8, 8) (1, 96, 8, 8)\nécart maximal entre les deux écritures : 7.748603820800781e-07\n```\n\nL'écart se réduit à l'erreur d'arrondi en virgule flottante. Dans PyTorch, `groups=3` signifie précisément « diviser les canaux en trois segments, chacun convolué séparément » ; ce module Inception simplifié n'est donc rien d'autre qu'une convolution $1\\times1$ suivie d'une convolution $3\\times3$ divisée en trois groupes.\n\nVu sous cet angle, le module Inception simplifié n'a plus qu'un seul paramètre réglable, le nombre de segments entre lesquels on répartit les canaux.\n\n### Diviser jusqu'au bout\n\nPlus il y a de segments, moins chaque segment a de canaux. À l'extrême, chaque segment ne compte plus qu'un canal, et chaque canal de sortie reçoit sa propre convolution $3\\times3$ :\n\n[![Version extrême du module Inception, de bas en haut : Input entre dans une 1x1 conv, dont les canaux de sortie sont divisés en de nombreux segments, chacun suivi d'une seule 3x3, et le tout se termine par Concat](/content/papers/assets/b56e8b540dd5a4cb9856460b3e47a6e3e624df308ab08b70809438075caf5f0a.png)](/content/papers/assets/b56e8b540dd5a4cb9856460b3e47a6e3e624df308ab08b70809438075caf5f0a.png)\n\n*Figure 4 de l'article, extraite de la [**page 2 du PDF v3**](https://arxiv.org/pdf/1610.02357v3#page=2). Version « extrême » du module Inception, où chaque canal de sortie de la convolution $1\\times1$ a sa propre convolution spatiale.*\n\nFixons 96 canaux, et regardons comment évoluent les poids de la couche $3\\times3$ quand le nombre de segments passe de 1 à 96 :\n\n```python\nimport torch.nn as nn\n\nC = 96\nfor groups in (1, 3, 4, 32, 96):\n    conv = nn.Conv2d(C, C, 3, padding=1, groups=groups, bias=False)\n    print(f\"groups={groups:>2} : canaux par segment {C // groups:>2}, paramètres de la convolution 3×3 {conv.weight.numel():>6,}, forme des poids {tuple(conv.weight.shape)}\")\n```\n\n```text\ngroups= 1 : canaux par segment 96, paramètres de la convolution 3×3 82,944, forme des poids (96, 96, 3, 3)\ngroups= 3 : canaux par segment 32, paramètres de la convolution 3×3 27,648, forme des poids (96, 32, 3, 3)\ngroups= 4 : canaux par segment 24, paramètres de la convolution 3×3 20,736, forme des poids (96, 24, 3, 3)\ngroups=32 : canaux par segment  3, paramètres de la convolution 3×3  2,592, forme des poids (96, 3, 3, 3)\ngroups=96 : canaux par segment  1, paramètres de la convolution 3×3    864, forme des poids (96, 1, 3, 3)\n```\n\nLe deuxième nombre de la forme des poids indique combien de canaux d'entrée chaque noyau peut voir. `groups=1` est la convolution ordinaire, où chaque noyau voit les 96 canaux ; avec `groups=96`, chaque noyau ne voit qu'un seul canal, et il ne reste que la $3\\times3$ dans la direction spatiale.\n\nL'auteur voit toute cette série comme un spectre. À une extrémité se trouve la convolution ordinaire (précédée d'une $1\\times1$), avec un seul segment ; à l'autre, un segment par canal. Le module Inception se situe entre les deux, et divise quelques centaines de canaux en 3 ou 4 segments.\n\n### L'autre extrémité du spectre\n\nL'auteur fait remarquer que le module extrême, à l'autre bout du spectre, est presque identique à une opération qui existait déjà, la **convolution séparable en profondeur** (*depthwise separable convolution*). Elle se déroule en deux étapes :\n\n1. **Convolution depthwise** : chaque canal d'entrée subit sa propre convolution spatiale, sans échange entre canaux.\n2. **Convolution pointwise** : une convolution $1\\times1$ projette la sortie de l'étape précédente dans un nouvel espace de canaux.\n\nC'est elle que TensorFlow et Keras appellent separable convolution. Le domaine du traitement d'images emploie lui aussi le nom separable convolution, mais pour une séparation spatiale, comme décomposer un $7\\times7$ en $1\\times7$ et $7\\times1$, ce qu'Inception-v3 a d'ailleurs utilisé ; il ne faut pas confondre les deux.\n\n\n> [!NOTE]\n> \n> La convolution séparable en profondeur n'a pas été inventée par cet article, et l'auteur en indique l'origine. Laurent Sifre l'a mise au point en 2013, lors d'un stage chez Google Brain, et l'a appliquée à AlexNet, avec un léger gain de précision, une convergence bien plus rapide et un modèle bien plus petit ; la première couche de GoogLeNet et celle de BN-Inception l'ont aussi utilisée. À la même période, Andrew Howard, chez Google, s'en servait pour construire MobileNets, destinés aux appareils mobiles ; vous pouvez lire notre note sur [MobileNet-V1](/blog/papers/lightweight/mobilenet-v1).\n\n\nLe module Inception « extrême » et la convolution séparable en profondeur diffèrent encore sur deux petits points :\n\n1. **L'ordre.** La convolution séparable en profondeur fait d'abord la depthwise, puis la $1\\times1$ ; Inception commence par la $1\\times1$. Pour l'auteur, cela compte peu, car le réseau est un empilement de couches où la $1\\times1$ d'un module est suivie de la depthwise du module suivant, si bien que l'ordre ne diffère qu'au tout début et à la toute fin.\n2. **Une non-linéarité au milieu, ou non.** Dans Inception, les deux étapes sont suivies d'une ReLU ; la convolution séparable en profondeur n'a en général aucune fonction d'activation entre les deux. Selon l'auteur, ce point pourrait avoir une influence, et il le laisse aux expériences.\n\n### Xception\n\nPuisque l'hypothèse Inception veut que « les relations inter-canaux et spatiales puissent être en partie séparées », l'auteur pose carrément une hypothèse plus forte, selon laquelle elles peuvent l'être **complètement**. Le réseau fondé sur cette hypothèse est entièrement construit avec des convolutions séparables en profondeur ; son nom, tiré d'Extreme Inception, est Xception.\n\n[![Architecture de Xception, en trois colonnes. Entry flow : des images 299x299x3 passent par Conv 32 3x3 stride 2 et Conv 64 3x3, puis par trois modules, chacun composé de deux SeparableConv (128, 256 et 728 canaux) et d'un MaxPooling 3x3 stride 2, avec à côté un raccourci Conv 1x1 stride 2 ; la sortie est de 19x19x728. Middle flow : trois couches ReLU plus SeparableConv 728, entourées d'un raccourci identité, le tout répété 8 fois. Exit flow : SeparableConv 728, SeparableConv 1024 et MaxPooling, avec à côté un raccourci Conv 1x1 stride 2 ; puis SeparableConv 1536, SeparableConv 2048 et GlobalAveragePooling, qui donne un vecteur de 2048 dimensions, des couches entièrement connectées optionnelles, et enfin Logistic regression](/content/papers/assets/8b7417d8bf8ba80d77cf08ea108b8a7df9b994d3f4f08922532004a3ce0f7f69.png)](/content/papers/assets/8b7417d8bf8ba80d77cf08ea108b8a7df9b994d3f4f08922532004a3ce0f7f69.png)\n\n*Figure 5 de l'article, extraite de la [**page 5 du PDF v3**](https://arxiv.org/pdf/1610.02357v3#page=5). Chaque Conv et chaque SeparableConv est suivie d'une BN (omise sur la figure) ; la partie depthwise des SeparableConv n'augmente pas le nombre de canaux.*\n\nLes données traversent trois parties, de gauche à droite. L'entry flow part d'une image $299\\times299$ ; ses deux premières couches sont des convolutions ordinaires, suivies de trois modules qui divisent chacun par deux la taille de la carte de caractéristiques et élargissent les canaux, pour arriver à $19\\times19\\times728$. Le middle flow répète 8 fois le même module, sans changer la taille. L'exit flow réduit la taille une dernière fois, porte les canaux à 2048, puis applique un pooling moyen global suivi de la couche de classification.\n\nLe réseau compte 36 couches de convolution réparties en 14 modules ; tous les modules, sauf le premier et le dernier, sont entourés d'une connexion résiduelle. Les modules qui ne réduisent pas la taille utilisent un raccourci identité ; pour ceux qui la réduisent, le raccourci porte une convolution $1\\times1$ de pas 2.\n\nComparé à Inception-v3, ce schéma est beaucoup plus simple, avec seulement des convolutions séparables en profondeur et des connexions résiduelles empilées couche après couche ; selon l'auteur, l'écrire avec Keras ne prend que 30 à 40 lignes. Reconstruisons-le d'après la figure avec PyTorch :\n\n```python\nimport torch\nimport torch.nn as nn\n\nclass SeparableConv(nn.Sequential):\n    \"\"\"Convolution 3×3 canal par canal, suivie d'une convolution 1×1 ; aucune activation entre les deux, BN à la fin\"\"\"\n    def __init__(self, cin, cout):\n        super().__init__(\n            nn.Conv2d(cin, cin, 3, padding=1, groups=cin, bias=False),   # depthwise\n            nn.Conv2d(cin, cout, 1, bias=False),                          # pointwise\n            nn.BatchNorm2d(cout))\n\nclass Block(nn.Module):\n    def __init__(self, cin, widths, relu_first=True, downsample=True):\n        super().__init__()\n        layers, c = [], cin\n        for i, w in enumerate(widths):\n            if relu_first or i > 0:\n                layers.append(nn.ReLU())\n            layers.append(SeparableConv(c, w))\n            c = w\n        if downsample:\n            layers.append(nn.MaxPool2d(3, 2, padding=1))\n            self.shortcut = nn.Sequential(nn.Conv2d(cin, c, 1, 2, bias=False), nn.BatchNorm2d(c))\n        else:\n            self.shortcut = nn.Identity()\n        self.body = nn.Sequential(*layers)\n\n    def forward(self, x):\n        return self.body(x) + self.shortcut(x)\n\ndef conv_bn_relu(cin, cout, stride):\n    return nn.Sequential(nn.Conv2d(cin, cout, 3, stride, bias=False), nn.BatchNorm2d(cout), nn.ReLU())\n\nentry = nn.Sequential(conv_bn_relu(3, 32, 2), conv_bn_relu(32, 64, 1),\n                      Block(64, [128, 128], relu_first=False), Block(128, [256, 256]), Block(256, [728, 728]))\nmiddle = nn.Sequential(*[Block(728, [728, 728, 728], downsample=False) for _ in range(8)])\nexit_ = nn.Sequential(Block(728, [728, 1024]),\n                      SeparableConv(1024, 1536), nn.ReLU(), SeparableConv(1536, 2048), nn.ReLU(),\n                      nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(2048, 1000))\nmodel = nn.Sequential(entry, middle, exit_)\n\nx = torch.randn(1, 3, 299, 299)\nfor name, part in [(\"entry flow\", entry), (\"middle flow\", middle)]:\n    x = part(x)\n    print(f\"forme après {name} :\", tuple(x.shape[1:]))\nprint(\"forme après exit flow :\", tuple(exit_(x).shape[1:]))\n\nparams = sum(p.numel() for p in model.parameters())\nprint(f\"nombre de paramètres : {params:,}\")\n```\n\n```text\nforme après entry flow : (728, 19, 19)\nforme après middle flow : (728, 19, 19)\nforme après exit flow : (1000,)\nnombre de paramètres : 22,855,952\n```\n\nLe nombre de paramètres est exactement celui que rapporte l'article, 22 855 952. PyTorch n'a pas de couche separable convolution toute faite ; il suffit, comme ici, d'écrire une convolution $3\\times3$ avec `groups=nombre de canaux d'entrée` suivie d'une convolution $1\\times1$. Keras propose directement `SeparableConv2D`, et Xception lui-même figure dans Keras Applications.\n\nCôté détails, d'après la figure, chaque SeparableConv est précédée d'une ReLU et suivie d'une BN, et rien n'est inséré entre la depthwise et la pointwise. La première SeparableConv du premier module résiduel de l'entry flow n'a pas de ReLU devant elle, car la Conv 64 qui la précède est déjà suivie d'une ReLU ; dans les deux dernières couches de l'exit flow, à l'inverse, la ReLU est placée après.\n\n## Discussion\n\nL'auteur compare Xception à Inception-v3, car leurs nombres de paramètres sont presque identiques, de sorte que l'écart ne peut pas être attribué à la capacité du modèle. La comparaison porte sur deux jeux de données :\n\n- **ImageNet** : classification à étiquette unique en 1000 classes.\n- **JFT** : un jeu de données interne de Google, de plus de 350 millions d'images haute résolution réparties en 17 000 classes, chaque image pouvant porter plusieurs étiquettes. L'évaluation utilise un autre jeu, FastEval14k, de 14 000 images, avec en moyenne 36,5 étiquettes par image ; la métrique est la MAP@100 sur les 100 premières prédictions, pondérée selon la fréquence de chaque classe sur les réseaux sociaux.\n\nLes deux réseaux utilisent les mêmes réglages d'entraînement, soigneusement ajustés pour Inception-v3, et que l'auteur n'a pas réajustés pour Xception. Seule exception, le weight decay, car les 4e-5 d'Inception-v3 ne conviennent manifestement pas à Xception, qui passe à 1e-5. Tous les modèles sont entraînés sur 60 NVIDIA K80, à raison d'environ 3 jours par entraînement sur ImageNet et de plus d'un mois sur JFT, où l'entraînement n'est d'ailleurs pas mené jusqu'à la convergence.\n\n### ImageNet\n\n| Réseau | Précision top-1 | Précision top-5 |\n| --- | ---: | ---: |\n| VGG-16 | 0,715 | 0,901 |\n| ResNet-152 | 0,770 | 0,933 |\n| Inception-v3 | 0,782 | 0,941 |\n| **Xception** | **0,790** | **0,945** |\n\n*Tableau : extrait de la Table 1 de l'article ; résultats sur l'ensemble de validation, modèle unique, recadrage unique. Inception-v3 n'utilise pas de classifieur auxiliaire.*\n\nXception dépasse Inception-v3 de 0,8 point de pourcentage, un écart modeste. VGG-16 et ResNet-152 ne figurent qu'à titre de référence.\n\n### JFT\n\n| Réseau | MAP@100 sur FastEval14k |\n| --- | ---: |\n| Inception-v3, sans couches entièrement connectées | 6,36 |\n| Xception, sans couches entièrement connectées | 6,70 |\n| Inception-v3, avec couches entièrement connectées | 6,50 |\n| **Xception, avec couches entièrement connectées** | **6,78** |\n\n*Tableau : extrait de la Table 2 de l'article ; résultats après 30 millions d'itérations (environ un mois). « Avec couches entièrement connectées » signifie que deux couches entièrement connectées de 4096 dimensions sont ajoutées avant la couche de classification.*\n\nSur JFT, l'écart est bien plus grand, et la version avec couches entièrement connectées progresse de 4,3 % en relatif. Selon l'auteur, Inception-v3 a été développé pour ImageNet, et sa conception est peut-être déjà trop ajustée à ce jeu de données, alors qu'aucun des deux réseaux n'a été réglé pour JFT.\n\n### Autant de paramètres, un peu plus lent\n\n| Réseau | Nombre de paramètres | Pas d'entraînement par seconde |\n| --- | ---: | ---: |\n| Inception-v3 | 23 626 728 | 31 |\n| Xception | 22 855 952 | 28 |\n\n*Tableau : extrait de la Table 3 de l'article ; entraînement synchrone sur ImageNet avec 60 K80.*\n\nLes deux nombres de paramètres diffèrent de moins de 3,5 % ; l'auteur en conclut que le progrès de Xception vient d'une utilisation plus efficace des paramètres, et non d'une capacité accrue.\n\nCôté vitesse, Xception est au contraire un peu plus lent. Les convolutions depthwise ont beau avoir peu de paramètres et de calculs, il est difficile d'en tirer sur GPU la vitesse correspondante.\n\n### Les connexions résiduelles\n\nL'auteur a aussi entraîné une version de Xception privée de toutes ses connexions résiduelles. Sur ImageNet, cette version converge beaucoup plus lentement, et sa précision finale est nettement plus basse.\n\nL'auteur précise que la version sans résidus utilise les mêmes réglages d'entraînement, ce qui n'est peut-être pas très équitable pour elle, et que ce résultat montre seulement l'importance des connexions résiduelles pour **cette architecture**. Il a d'ailleurs essayé un réseau de type VGG dont toutes les convolutions sont remplacées par des convolutions séparables en profondeur, sans aucun résidu ; sur JFT, ce réseau bat lui aussi Inception-v3 à nombre de paramètres égal.\n\n### Une activation au milieu ?\n\nRevenons à la question laissée en suspens. Faut-il une non-linéarité entre la depthwise et la pointwise ? L'auteur essaie trois variantes, sans activation, avec une ReLU et avec une ELU :\n\n[![Courbes d'entraînement, précision de validation sur ImageNet en fonction du nombre de pas de descente de gradient. La courbe rouge, No intermediate activation, monte le plus vite et finit près de 0,79 ; la verte, Intermediate ELU, vient ensuite et s'arrête vers 0,78 ; la bleue, Intermediate ReLU, est la plus lente et s'arrête vers 0,77](/content/papers/assets/c1da1e28d13c72c20287fa73c73e3e10c9dc4f735991c6f580191ede66fd1b5b.png)](/content/papers/assets/c1da1e28d13c72c20287fa73c73e3e10c9dc4f735991c6f580191ede66fd1b5b.png)\n\n*Figure 10 de l'article, extraite de la [**page 7 du PDF v3**](https://arxiv.org/pdf/1610.02357v3#page=7). Courbes d'entraînement avec différentes fonctions d'activation entre la depthwise et la pointwise des SeparableConv.*\n\nLa courbe rouge, sans aucune activation, converge le plus vite et atteint aussi la meilleure précision finale ; la courbe verte, avec ELU, se place au milieu ; la courbe bleue, avec ReLU, est la moins bonne.\n\nC'est l'inverse du résultat de l'article d'Inception-v3. En décomposant un $5\\times5$ en deux $3\\times3$, Inception-v3 avait constaté qu'une ReLU au milieu était préférable. L'auteur suppose que tout dépend de « l'épaisseur » de l'espace de caractéristiques où opère la convolution spatiale. Dans le module Inception, chaque segment compte des dizaines, voire des centaines de canaux, et la non-linéarité aide ; dans la convolution séparable en profondeur, chaque segment n'a qu'un canal, et en supprimant les valeurs négatives, la ReLU risque de faire perdre de l'information.\n\nPlus tard, MobileNetV2 retirera la ReLU des couches les plus étroites du réseau en avançant le même argument, à savoir qu'une non-linéarité dans un espace de faible dimension fait perdre de l'information ; vous pouvez poursuivre avec [MobileNet-V2](/blog/papers/lightweight/mobilenet-v2).\n\n## Conclusion\n\nXception porte un autre regard sur Inception. Une fois simplifié, le module Inception n'est qu'une convolution $1\\times1$ suivie d'une convolution $3\\times3$ divisée en quelques groupes ; en faisant passer le nombre de groupes de 1 au nombre de canaux, on va de la convolution ordinaire à la convolution séparable en profondeur. Xception va droit au bout, avec un nombre de paramètres presque identique à celui d'Inception-v3, un résultat légèrement meilleur sur ImageNet, et nettement meilleur sur JFT.\n\nXception a beau s'appeler Extreme Inception, sa vitesse n'a rien d'extrême, avec même 3 pas d'entraînement par seconde de moins qu'Inception-v3. L'auteur s'attendait alors à ce qu'une meilleure implémentation des convolutions depthwise lui fasse reprendre l'avantage ; l'attente a duré plusieurs années, et [EfficientNetV2](/blog/papers/classic-cnns/efficientnet-v2) a fini par remplacer purement et simplement les convolutions depthwise du début du réseau.\n\nQuant à savoir si le bout du chemin est la meilleure position, l'auteur lui-même n'en est pas sûr ; il écrit en fin d'article que certains points au milieu du spectre pourraient faire mieux. Un peu plus d'un mois plus tard, [ResNeXt](/blog/papers/classic-cnns/resnext) utilise des convolutions divisées en 32 groupes, qui tombent justement au milieu.\n","imageSizes":{"/content/papers/assets/9fc3c2cfbe884067fa627f67d745ee0ab6eaf44ed451260083932eb5fa0742e0.png":{"width":917,"height":554},"/content/papers/assets/b56e8b540dd5a4cb9856460b3e47a6e3e624df308ab08b70809438075caf5f0a.png":{"width":917,"height":538},"/content/papers/assets/8b7417d8bf8ba80d77cf08ea108b8a7df9b994d3f4f08922532004a3ce0f7f69.png":{"width":2054,"height":1417},"/content/papers/assets/c1da1e28d13c72c20287fa73c73e3e10c9dc4f735991c6f580191ede66fd1b5b.png":{"width":946,"height":716}}}
