{"notes":[{"id":"classic-cnns/nfnet","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"NFNet","subtitle":"La vie sans BN","author":"Z. Yuan","paperDate":"2021-02-11","publishedOn":"2025-01-21","world":"external/nfnet","paperTitle":"High-Performance Large-Scale Image Recognition Without Normalization","paperUrl":"https://arxiv.org/abs/2102.06171","bibliography":{"title":"High-Performance Large-Scale Image Recognition Without Normalization","authors":["Andrew Brock","Soham De","Samuel L. Smith","Karen Simonyan"],"url":"https://arxiv.org/abs/2102.06171","pdfUrl":"https://arxiv.org/pdf/2102.06171v1","codeUrl":null,"preprint":{"id":"2102.06171","submittedOn":"2021-02-11","url":"https://arxiv.org/abs/2102.06171"},"publication":{"venue":"ICML","year":2021,"kind":"conference","url":"https://proceedings.mlr.press/v139/brock21a.html"},"citation":"@misc{classiccnnsnfnet2021,\n  title = {{High-Performance Large-Scale Image Recognition Without Normalization}},\n  author = {Andrew Brock and Soham De and Samuel L. Smith and Karen Simonyan},\n  year = {2021},\n  eprint = {2102.06171},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/2102.06171}\n}\n","citationPath":"/content/papers/citations/classic-cnns/nfnet.91baba59df96.bib"},"thumbnail":{"src":"/content/papers/assets/7552bff30433f4f33bec5edb2421acd94d61d452888dde6df17557778e8fb1c6.png","width":1004,"height":783},"locale":"fr","path":"/content/papers/fr/classic-cnns/nfnet.37f58fe74219.md","headings":[{"id":"définir-le-problème","text":"Définir le problème","depth":2},{"id":"ce-que-batchnorm-apporte-vraiment","text":"Ce que BatchNorm apporte vraiment","depth":3},{"id":"à-quoi-ressemble-le-décalage-de-la-moyenne","text":"À quoi ressemble le décalage de la moyenne","depth":3},{"id":"résoudre-le-problème","text":"Résoudre le problème","depth":2},{"id":"compléter-dabord-nf-resnet","text":"Compléter d'abord NF-ResNet","depth":3},{"id":"écrêtage-adaptatif-du-gradient","text":"Écrêtage adaptatif du gradient","depth":3},{"id":"larchitecture-nfnet","text":"L'architecture NFNet","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"lapport-de-chaque-modification","text":"L'apport de chaque modification","depth":3},{"id":"retirer-la-bn-rend-il-vraiment-plus-rapide","text":"Retirer la BN rend-il vraiment plus rapide ?","depth":3},{"id":"face-aux-autres-modèles","text":"Face aux autres modèles","depth":3},{"id":"préentraîner-puis-affiner","text":"Préentraîner, puis affiner","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"normalization/batchnorm","relation":"referenced","topic":""}]},{"id":"normalization/batchnorm","category":"normalization","legacyPath":"normalization/1502-batchnorm","legacySlug":"1502-batchnorm","legacyPaths":[],"title":"BatchNorm","subtitle":"Normalisation par lots","author":"Z. Yuan","paperDate":"2015-02-11","publishedOn":"2024-08-01","paperTitle":"Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift","paperUrl":"https://arxiv.org/abs/1502.03167","bibliography":{"title":"Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift","authors":["Sergey Ioffe","Christian Szegedy"],"url":"https://arxiv.org/abs/1502.03167","pdfUrl":"https://arxiv.org/pdf/1502.03167","codeUrl":null,"preprint":{"id":"1502.03167","submittedOn":"2015-02-11","url":"https://arxiv.org/abs/1502.03167"},"publication":{"venue":"ICML","year":2015,"kind":"conference","url":"https://proceedings.mlr.press/v37/ioffe15.html"},"citation":"@misc{normalizationbatchnorm2015,\n  title = {{Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift}},\n  author = {Sergey Ioffe and Christian Szegedy},\n  year = {2015},\n  eprint = {1502.03167},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1502.03167}\n}\n","citationPath":"/content/papers/citations/normalization/batchnorm.bfa8abb33f32.bib"},"thumbnail":{"src":"/content/papers/assets/48b236d4b897164b93764eccfe22db29e5b9bce290d864336b8f12ec127c1e4b.jpg","width":1016,"height":824},"locale":"fr","path":"/content/papers/fr/normalization/batchnorm.0faad80bd1c0.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":"augmenter-le-taux-dapprentissage","text":"Augmenter le taux d'apprentissage","depth":3},{"id":"peut-on-retirer-dropout-","text":"Peut-on retirer Dropout ?","depth":3},{"id":"discussion","text":"Discussion","depth":2},{"id":"expérience-sur-mnist","text":"Expérience sur MNIST","depth":3},{"id":"expérience-sur-imagenet","text":"Expérience sur ImageNet","depth":3},{"id":"conclusion","text":"Conclusion","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"normalization/layernorm","relation":"next","topic":"Choisir les axes de normalisation"}]}],"text":"À partir de 2015, presque tous les réseaux de classification d'images contiennent une BatchNorm. Elle rend les réseaux profonds faciles à entraîner, autorise de grands taux d'apprentissage et apporte en prime un léger effet régularisateur ; elle est si pratique que l'on ne pense presque plus à ses défauts.\n\n\n> [!TIP]\n> \n> Pour BatchNorm, vous pouvez commencer par notre note :\n> \n> - **[[15.02] BatchNorm : Normalisation par lots](/blog/papers/normalization/batchnorm)**\n\n\nSes défauts sont pourtant nombreux. Il faut calculer les statistiques du lot, ce qui prend de la mémoire et du temps ; l'entraînement utilise les statistiques du lot et l'inférence des moyennes mobiles, si bien que le comportement diffère entre les deux ; enfin, les échantillons d'un même lot s'influencent les uns les autres, et c'est le point le plus gênant. Avec des lots trop petits, les résultats se dégradent ; en entraînement distribué, les erreurs arrivent vite ; en apprentissage contrastif, il faut un traitement particulier pour que le modèle ne lorgne pas sur la réponse à travers les statistiques du lot.\n\nChez DeepMind, Andrew Brock, Soham De, Samuel L. Smith et Karen Simonyan veulent supprimer complètement la BatchNorm. Dans leur article précédent, ils avaient déjà obtenu NF-ResNet, un réseau sans couche de normalisation dont la précision rattrapait celle des BN-ResNet de même taille ; celui-ci s'attaque aux deux problèmes restants.\n\n## Définir le problème\n\n### Ce que BatchNorm apporte vraiment\n\nPour retirer la BatchNorm, il faut d'abord savoir ce qu'elle fait, puis lui trouver un remplaçant pour chacun de ses rôles. Les auteurs en recensent quatre :\n\n1. **Elle réduit la sortie de la branche résiduelle.** Placée sur la branche résiduelle, la BN rend la sortie de la branche assez petite à l'initialisation ; le signal passe surtout par le raccourci, et les gradients sont plus stables en début d'entraînement.\n2. **Elle élimine le décalage de la moyenne.** Les fonctions d'activation comme ReLU ou GELU ont une sortie de moyenne non nulle ; couche après couche, les caractéristiques d'échantillons différents se ressemblent de plus en plus. La BN ramène la moyenne à 0 à chaque couche.\n3. **Un effet régularisateur.** Les statistiques du lot sont bruitées, un peu comme si l'on ajoutait du bruit pendant l'entraînement.\n4. **Elle rend possible l'entraînement en grands lots.** La BN lisse la surface de perte, ce qui autorise des taux d'apprentissage plus grands ; c'est ce qui donne tout son intérêt à l'entraînement en grands lots.\n\nLe NF-ResNet de l'article précédent traitait les deux premiers points par l'initialisation et la standardisation des poids, et compensait le troisième par Dropout et Stochastic Depth. Restaient deux problèmes. L'entraînement devient instable avec des lots de 4096 ou plus, et les meilleurs résultats restent en deçà d'EfficientNet.\n\n### À quoi ressemble le décalage de la moyenne\n\nLe deuxième point est plus abstrait ; voyons d'abord ce qui se passe sans normalisation. Prenons 64 échantillons aléatoires sans corrélation entre eux, faisons-les passer à chaque couche par une ReLU puis par des poids initialisés selon He, empilons 20 couches, et mesurons la similarité cosinus moyenne entre échantillons différents ; l'autre groupe suit la méthode de NF-ResNet, que nous expliquerons plus loin :\n\n```python\nimport math\nimport torch\nimport torch.nn.functional as F\n\ntorch.manual_seed(0)\nx = torch.randn(64, 512)                      # 64 échantillons sans corrélation, 512 caractéristiques chacun\n\ndef ws(W):\n    \"\"\"Scaled Weight Standardization : on retire la moyenne des poids de chaque unité de sortie, puis on divise par std·√N\"\"\"\n    N = W.shape[1]\n    return (W - W.mean(1, keepdim=True)) / (W.std(1, unbiased=False, keepdim=True) * math.sqrt(N))\n\ndef mean_cos(h):\n    \"\"\"Similarité cosinus moyenne entre échantillons différents\"\"\"\n    h = F.normalize(h, dim=1)\n    sim = h @ h.T\n    return (sim.sum() - sim.diagonal().sum()).item() / (h.shape[0] * (h.shape[0] - 1))\n\ngamma = math.sqrt(2 / (1 - 1 / math.pi))      # gain de la ReLU\nprint(f\"γ = {gamma:.4f}\")\nplain, std = x.clone(), x.clone()\nfor layer in range(1, 21):\n    W = torch.randn(512, 512)\n    plain = F.relu(plain) @ (W * math.sqrt(2 / 512)).T      # initialisation de He, sans normalisation\n    std = (gamma * F.relu(std)) @ ws(W).T                   # poids standardisés et gain\n    if layer in (1, 5, 10, 20):\n        print(f\"Couche {layer:>2} | init. He : similarité entre échantillons {mean_cos(plain):.2f}\"\n              f\" | Scaled WS : similarité {mean_cos(std):.2f}, variance {std.var().item():.2f}\")\n```\n\n```text\nγ = 1.7129\nCouche  1 | init. He : similarité entre échantillons 0.31 | Scaled WS : similarité 0.00, variance 0.99\nCouche  5 | init. He : similarité entre échantillons 0.72 | Scaled WS : similarité -0.00, variance 0.98\nCouche 10 | init. He : similarité entre échantillons 0.87 | Scaled WS : similarité 0.00, variance 0.95\nCouche 20 | init. He : similarité entre échantillons 0.96 | Scaled WS : similarité 0.00, variance 0.88\n```\n\nLes 64 échantillons d'entrée n'avaient au départ aucune corrélation ; après 20 couches, la similarité moyenne du groupe initialisé selon He atteint déjà 0,96, et presque tous les échantillons se ressemblent. La raison en est que la sortie de la ReLU est toujours positive ou nulle, si bien que chaque couche ajoute à tous les échantillons une même moyenne positive. Selon des travaux cités par l'article, ce décalage pousse un réseau profond à prédire, dès l'initialisation, la même classe pour tous les échantillons.\n\n## Résoudre le problème\n\n### Compléter d'abord NF-ResNet\n\nLe groupe de droite utilise les deux techniques du NF-ResNet de l'article précédent.\n\nLa première est la **Scaled Weight Standardization**. Pour chaque unité de sortie d'une couche de convolution, on retire aux poids leur propre moyenne, puis on les divise par leur écart-type et par $\\sqrt{N}$ :\n\n$$\n\\hat{W}_{ij}=\\frac{W_{ij}-\\mu_i}{\\sqrt{N}\\,\\sigma_i},\n$$\n\n$N$ est le fan-in. Une fois la moyenne retirée, les poids de chaque unité de sortie ont une somme nulle, et la valeur positive commune introduite par la fonction d'activation s'annule ; c'est pourquoi la similarité reste à 0 ci-dessus.\n\nLa seconde est le gain $\\gamma$ par lequel on multiplie la sortie de la fonction d'activation. Pour la ReLU, $\\gamma=\\sqrt{2/(1-1/\\pi)}$, soit environ 1,71, ce qui compense exactement la perte de variance due à la suppression des valeurs négatives par la ReLU. Avec les deux, la variance de sortie de chaque couche reste à peu près à 1. Les 0,88 obtenus après 20 couches sont de petits écarts accumulés couche après couche, bien peu de chose à côté de l'autre groupe, où les échantillons deviennent presque identiques.\n\nAvec une branche qui « conserve à peu près la variance à chaque couche », le bloc résiduel de NF-ResNet s'écrit\n\n$$\nh_{i+1}=h_i+\\alpha\\,f_i\\!\\left(h_i/\\beta_i\\right).\n$$\n\n$\\beta_i$ est l'écart-type de l'entrée, calculé à l'avance, de sorte que le signal qui entre dans la branche est toujours ramené à une variance de 1 ; $\\alpha$ est une petite constante (généralement 0,2) qui règle de combien chaque bloc augmente la variance. La variance du chemin principal se calcule donc directement, et augmente de $\\alpha^2$ à chaque bloc. Vérifions-le sur 12 blocs :\n\n```python\nimport math\nimport torch\nimport torch.nn.functional as F\n\ntorch.manual_seed(0)\ngamma = math.sqrt(2 / (1 - 1 / math.pi))\n\ndef ws(W):\n    N = W.shape[1]\n    return (W - W.mean(1, keepdim=True)) / (W.std(1, unbiased=False, keepdim=True) * math.sqrt(N))\n\ndef branch(h):\n    \"\"\"Branche résiduelle : deux couches « ReLU avec gain + poids standardisés », variance de sortie ≈ variance d'entrée\"\"\"\n    for _ in range(2):\n        h = (gamma * F.relu(h)) @ ws(torch.randn(512, 512)).T\n    return h\n\nalpha = 0.2\nh = torch.randn(4096, 512)\nexpected = 1.0\nfor i in range(1, 13):\n    beta = math.sqrt(expected)                # écart-type de l'entrée, calculé à l'avance\n    h = h + alpha * branch(h / beta)          # h_{i+1} = h_i + α·f(h_i / β_i)\n    expected += alpha ** 2                    # Var(h_{i+1}) = Var(h_i) + α²\n    if i in (1, 4, 8, 12):\n        print(f\"Après le bloc {i:>2} : variance attendue {expected:.2f}, mesurée {h.var().item():.2f}\")\n```\n\n```text\nAprès le bloc  1 : variance attendue 1.04, mesurée 1.04\nAprès le bloc  4 : variance attendue 1.16, mesurée 1.16\nAprès le bloc  8 : variance attendue 1.32, mesurée 1.32\nAprès le bloc 12 : variance attendue 1.48, mesurée 1.48\n```\n\nLa variance mesurée correspond exactement à la variance attendue. Voilà le remplaçant de la première fonction de BatchNorm. Comme $\\alpha$ est petit, le signal passe surtout par le raccourci, et la branche n'apporte au départ que de petites corrections. Dans les blocs qui réduisent la carte de caractéristiques, le raccourci reçoit lui aussi l'entrée mise à l'échelle, et le calcul de la variance repart de $1+\\alpha^2$. Par ailleurs, chaque branche résiduelle se termine par un scalaire apprenable initialisé à 0 (SkipInit) ; au tout début de l'entraînement, la branche entière est donc comme éteinte.\n\nÀ ce stade, NF-ResNet rattrape déjà BN-ResNet avec des lots de 1024, mais devient instable dès que les lots grossissent.\n\n### Écrêtage adaptatif du gradient\n\nL'entraînement en grands lots exige un grand taux d'apprentissage ; si le gradient est grand, une seule mise à jour peut pousser les paramètres trop loin. La solution courante est l'écrêtage du gradient :\n\n$$\nG\\rightarrow\n\\begin{cases}\n\\lambda\\dfrac{G}{\\lVert G\\rVert} & \\text{if }\\lVert G\\rVert>\\lambda,\\\\\nG & \\text{otherwise.}\n\\end{cases}\n$$\n\nLes auteurs constatent que cela permet effectivement de tenir avec des lots plus grands, mais le seuil $\\lambda$ est très difficile à régler. Dès que la profondeur du modèle, la taille de lot ou le taux d'apprentissage changent, il faut le chercher à nouveau.\n\nLe problème est qu'un seuil fixe ignore la taille des paramètres eux-mêmes. Un même gradient de 0,01 ne représente qu'une variation de 1 % sur un poids de taille 1, mais renverse complètement un poids de taille 0,01. Les auteurs regardent donc plutôt le rapport\n\n$$\n\\frac{\\lVert G^\\ell\\rVert_F}{\\lVert W^\\ell\\rVert_F},\n$$\n\nqui indique grosso modo dans quelle proportion une mise à jour modifie les poids. En pratique, on ne le calcule pas une fois pour toute la couche, mais **par unité**, c'est-à-dire pour chaque ligne de la matrice de poids (pour une convolution, chaque canal de sortie avec son étendue spatiale) :\n\n$$\nG_i^\\ell\\rightarrow\n\\begin{cases}\n\\lambda\\dfrac{\\lVert W_i^\\ell\\rVert_F^\\star}{\\lVert G_i^\\ell\\rVert_F}\\,G_i^\\ell & \\text{if }\\dfrac{\\lVert G_i^\\ell\\rVert_F}{\\lVert W_i^\\ell\\rVert_F^\\star}>\\lambda,\\\\[6pt]\nG_i^\\ell & \\text{otherwise.}\n\\end{cases}\n$$\n\n$\\lVert W_i\\rVert_F^\\star=\\max(\\lVert W_i\\rVert_F,\\epsilon)$, avec $\\epsilon=10^{-3}$, pour éviter que le gradient des paramètres initialisés à 0 soit toujours écrêté à 0. C'est l'Adaptive Gradient Clipping (AGC), ou écrêtage adaptatif du gradient.\n\nVoyons comment il écrête sur un petit exemple à trois unités :\n\n```python\nimport torch\n\nW = torch.tensor([[1.0, 0.0, 0.0],\n                  [0.0, 0.1, 0.0],\n                  [0.0, 0.0, 2.0]])           # trois unités de sortie, aux poids de tailles différentes\nG = torch.tensor([[0.005, 0.0, 0.0],\n                  [0.0, 0.05, 0.0],\n                  [0.0, 0.0, 0.01]])          # gradients des trois lignes\n\nlam, eps = 0.01, 1e-3\nw_norm = W.norm(dim=1, keepdim=True).clamp(min=eps)\ng_norm = G.norm(dim=1, keepdim=True)\nratio = g_norm / w_norm\nclipped = torch.where(ratio > lam, G * (lam * w_norm / g_norm), G)\n\nfor i in range(3):\n    print(f\"Unité {i} : ‖W‖={w_norm[i].item():.2f}, ‖G‖={g_norm[i].item():.3f}, rapport {ratio[i].item():.3f}\"\n          f\" → après écrêtage ‖G‖={clipped[i].norm().item():.4f}\")\nprint(\"Norme globale du gradient :\", round(G.norm().item(), 4))\n```\n\n```text\nUnité 0 : ‖W‖=1.00, ‖G‖=0.005, rapport 0.005 → après écrêtage ‖G‖=0.0050\nUnité 1 : ‖W‖=0.10, ‖G‖=0.050, rapport 0.500 → après écrêtage ‖G‖=0.0010\nUnité 2 : ‖W‖=2.00, ‖G‖=0.010, rapport 0.005 → après écrêtage ‖G‖=0.0100\nNorme globale du gradient : 0.0512\n```\n\nSeule l'unité 1 est écrêtée. Son poids ne vaut que 0,1 alors que son gradient atteint 0,05 ; une seule mise à jour en modifierait la moitié, d'où sa réduction à $\\lambda\\times0.1=0.001$. Les deux autres unités ont un rapport sous le seuil et restent intactes. Avec un écrêtage global classique, les trois unités seraient réduites ensemble par un même coefficient, et celle qui en a vraiment besoin ne serait pas forcément assez réduite.\n\nCette implémentation donne les mêmes résultats que `timm.utils.agc.adaptive_clip_grad` de timm, soit respectivement 0,005, 0,001 et 0,01 pour les trois unités après écrêtage.\n\n[![Deux graphiques en courbes. (a) En abscisse, la taille de lot (de 256 à 4096) ; en ordonnée, la précision top-1 ; la courbe bleue correspond à BatchNorm, l'orange à NF-ResNet et la verte à NF-ResNet+AGC, en trait plein pour ResNet50 et en pointillés pour ResNet200. La courbe orange chute brutalement au-delà d'un lot de 2048, tandis que la verte reste partout proche de la bleue, ou au-dessus. (b) Précision de ResNet50 pour différents seuils d'écrêtage (de 0,01 à 0,16), une courbe par taille de lot ; la courbe violette du lot de 4096 s'effondre au-delà du seuil 0,02](/content/papers/assets/9c1e7cd2c8c545de3ebab245e654b0dd69b3921c4f0479f0e99131833bdfc5f2.png)](/content/papers/assets/9c1e7cd2c8c545de3ebab245e654b0dd69b3921c4f0479f0e99131833bdfc5f2.png)\n\n*Figure 2 de l'article, extraite de la [**page 4 du PDF v1**](https://arxiv.org/pdf/2102.06171v1#page=4). (a) AGC permet à NF-ResNet d'utiliser de plus grands lots ; (b) résultats pour différents seuils d'écrêtage $\\lambda$.*\n\nDans la figure de gauche, la courbe orange est NF-ResNet sans AGC, qui s'effondre au-delà d'un lot de 2048 ; la courbe verte, avec AGC, tient jusqu'à 4096, avec une précision comparable, voire supérieure, à celle de la courbe bleue de BatchNorm. La figure de droite porte sur le seuil. Plus le lot est grand, plus le seuil doit être petit, et avec un lot de 4096, seuls 0,01 et 0,02 tiennent.\n\nLes auteurs ont aussi cherché quelles couches il faut écrêter. Mieux vaut ne pas écrêter la dernière couche entièrement connectée ; la première convolution peut souvent s'en passer, mais avec un lot de 4096, les poids des quatre étages doivent tous être écrêtés. Dans la suite des expériences, AGC s'applique à tous les paramètres sauf ceux de la dernière couche.\n\n### L'architecture NFNet\n\nAvec AGC en main, les auteurs conçoivent ensuite un nouveau réseau. L'objectif n'est plus cette fois les FLOPs ou le nombre de paramètres, mais **la vitesse réelle d'entraînement sur TPU et GPU**. Les auteurs soulignent que les FLOPs d'EfficientNet sont très bas sans que l'entraînement en soit plus rapide ; EfficientNet-B0 n'a que le dixième des FLOPs de ResNet-50, mais s'entraîne à peu près aussi lentement sur GPU ou TPU.\n\n[![Schéma du bloc bottleneck de NFNet. L'entrée est d'abord multipliée par 1/β, passe par une convolution 1x1, deux convolutions 3x3, puis une autre convolution 1x1, et est multipliée par α avant d'être additionnée au raccourci. À droite figurent la largeur et la profondeur de chaque étage ; largeurs de ResNet [256, 512, 1024, 2048] et de NFNet [256, 512, 1536, 1536] ; profondeurs de ResNet [3, 4, 6, 3], [3, 4, 23, 3]…, et de NFNet [1, 2, 6, 3] multipliées par N](/content/papers/assets/6dc27a9ccca4de619c057562ea6384aab3c095ddc08b22f475601ab03fcf03bb.png)](/content/papers/assets/6dc27a9ccca4de619c057562ea6384aab3c095ddc08b22f475601ab03fcf03bb.png)\n\n*Figure 3 de l'article, extraite de la [**page 5 du PDF v1**](https://arxiv.org/pdf/2102.06171v1#page=5). Le bloc bottleneck de NFNet, et ses différences de largeur et de profondeur avec ResNet.*\n\nLe point de départ est SE-ResNeXt-D, avec GELU comme fonction d'activation, auquel les auteurs apportent quatre modifications :\n\n1. **Largeur de groupe fixée à 128.** Des groupes plus fins réduisent les FLOPs, mais la densité de calcul baisse aussi, et, sur les accélérateurs, le réseau ne va pas plus vite en pratique.\n2. **Profondeurs [1, 2, 6, 3].** Les modèles plus grands multiplient simplement ce motif par N ; F1, par exemple, a [2, 4, 12, 6]. ResNet n'approfondit traditionnellement que les deux étages du milieu, ce que les auteurs jugent sous-optimal.\n3. **Largeurs [256, 512, 1536, 1536].** La capacité est concentrée sur le troisième étage, et le quatrième est légèrement réduit, sans changer grand-chose à la vitesse d'entraînement.\n4. **Une convolution groupée $3\\times3$ de plus dans le bloc.** Ce sont les deux 3x3 consécutives de la figure. D'après les auteurs, cette convolution supplémentaire n'ajoute que très peu de FLOPs et, sur leurs accélérateurs, n'a presque aucun effet sur le temps d'entraînement.\n\nPour agrandir le modèle, on n'augmente que la profondeur et la résolution, pas la largeur ; les auteurs ont constaté qu'élargir fonctionne mal pour les backbones de type ResNet. À chaque échelon, le temps d'entraînement double à peu près, et le Dropout augmente lui aussi, de 0,2 pour F0 à 0,5 pour F6. Privé de la régularisation implicite de BatchNorm, le modèle surapprend fortement s'il ne s'appuie pas sur des régularisations explicites comme le Dropout.\n\n## Discussion\n\n### L'apport de chaque modification\n\n| Configuration | F0 | F1 | F2 | F3 |\n| --- | ---: | ---: | ---: | ---: |\n| Point de départ | 80,4 % | 81,7 % | 82,0 % | 82,3 % |\n| + largeurs modifiées | 80,9 % | 81,8 % | 82,0 % | 82,3 % |\n| + seconde $3\\times3$ | 81,3 % | 82,2 % | 82,4 % | 82,7 % |\n| + MixUp | 82,2 % | 82,9 % | 83,1 % | 83,5 % |\n| + RandAugment | 83,2 % | 84,6 % | 84,8 % | 85,0 % |\n| + CutMix | **83,6 %** | **84,7 %** | **85,1 %** | **85,7 %** |\n| Largeurs d'origine + augmentations ci-dessus | 83,1 % | 84,5 % | 85,0 % | 85,5 % |\n\n*Tableau : extrait de la Table 2 de l'article ; précision top-1 sur ImageNet, moyenne sur 3 graines aléatoires, entraînement de 360 époques avec des lots de 4096.*\n\nLes deux modifications d'architecture apportent ensemble de 0,4 à 0,9 point de pourcentage ; c'est l'augmentation de données qui fait vraiment la différence, RandAugment apportant à lui seul de 1 à 1,7 point de pourcentage. Ces augmentations fortes sont justement ce que le NF-ResNet d'origine ne supportait pas, et AGC permet de s'en servir. La dernière ligne revient aux largeurs d'origine de ResNet, et les quatre tailles y perdent, ce qui montre que l'ajustement des largeurs est réellement utile.\n\nLes auteurs ont aussi appliqué les mêmes augmentations à EfficientNet. RandAugment aide, mais porter son nombre de couches au-delà de 2, ou ajouter MixUp et CutMix, n'améliore rien. Ils en concluent que le progrès de NFNet ne vient pas seulement des augmentations.\n\n### Retirer la BN rend-il vraiment plus rapide ?\n\nEn réentraînant la même architecture avec BatchNorm, on obtient la précision et le temps d'entraînement par itération suivants :\n\n| Modèle | Version NF | Version BN |\n| --- | --- | --- |\n| F0 | 83,6 %, 73,3 ms | 83,4 %, 111,7 ms |\n| F1 | 84,7 %, 158,5 ms | 84,4 %, 258,0 ms |\n| F2 | 85,1 %, 295,8 ms | 85,1 %, 396,3 ms |\n\n*Tableau : extrait de la Table 6 de l'article ; précision top-1 sur ImageNet et temps d'entraînement par itération sur TPUv3.*\n\nLes précisions sont proches, la version NF faisant un peu mieux ou jeu égal, tandis que le temps par itération baisse de 25 à 40 %. Les auteurs ajoutent que les plus grands modèles, F4 et F5, ne s'entraînent pas de façon stable avec BatchNorm, avec ou sans AGC ; ils soupçonnent le bfloat16, utilisé pour faire tenir ces modèles en mémoire, de mal s'accorder avec le calcul des statistiques du lot.\n\n### Face aux autres modèles\n\n[![Nuage de points reliés par des courbes ; en abscisse, le temps d'entraînement par itération sur TPUv3 (en secondes), en ordonnée, la précision top-1 sur ImageNet (de 80 % à 87 %). La courbe rouge en trait plein, de NFNet-F0 à F5, se trouve en haut à gauche de tous les autres modèles ; la courbe bleue en pointillés correspond à EfficientNet B2 à B7, la verte à LambdaNet, l'orange à BoTNet, et les points noirs à DeiT. NFNet-F1 atteint la même précision qu'EffNet-B7, tout à droite, avec un temps d'entraînement bien plus court](/content/papers/assets/7552bff30433f4f33bec5edb2421acd94d61d452888dde6df17557778e8fb1c6.png)](/content/papers/assets/7552bff30433f4f33bec5edb2421acd94d61d452888dde6df17557778e8fb1c6.png)\n\n*Figure 1 de l'article, extraite de la [**page 1 du PDF v1**](https://arxiv.org/pdf/2102.06171v1#page=1). Précision de validation sur ImageNet en fonction du temps d'entraînement sur TPUv3, modèle unique, recadrage unique.*\n\nToute la courbe rouge se trouve en haut à gauche des autres modèles ; autrement dit, à temps d'entraînement égal, la précision est plus élevée. Les chiffres les plus cités opposent NFNet-F1 à EfficientNet-B7. Tous deux atteignent 84,7 %, mais F1 ne demande que 158,5 ms par itération, contre 1 397,0 ms pour B7, soit près de 9 fois plus rapide. Le plus grand, NFNet-F5, atteint 86,0 % ; avec l'optimiseur SAM, F5 monte à 86,3 % et F6 à 86,5 %, le meilleur résultat de l'époque sans données supplémentaires.\n\nCette courbe rouge est toutefois tracée en fonction du temps d'entraînement. En nombre de paramètres, NFNet n'a rien d'économe. F0 compte 71,5 M de paramètres et 12,38 GFLOPs, contre seulement 5,3 M et 0,39 GFLOPs pour EfficientNet-B0 ; F1 a aussi deux fois plus de paramètres que B7. Les auteurs l'écrivent clairement dans l'article ; c'est le temps d'entraînement qu'ils optimisent, et les FLOPs comme le nombre de paramètres ne sont pas leur objectif.\n\n### Préentraîner, puis affiner\n\nDans une dernière série d'expériences, les auteurs préentraînent sur 300 millions d'images annotées, puis affinent sur ImageNet. Avec autant de données, l'effet régularisateur de BatchNorm pourrait même devenir une gêne, et les auteurs supposent que les réseaux sans BN conviennent mieux à ce cadre :\n\n| Modèle | 224px | 320px | 384px |\n| --- | ---: | ---: | ---: |\n| BN-ResNet-50 | 78,1 % | 79,6 % | 79,9 % |\n| NF-ResNet-50 | **79,5 %** | **80,9 %** | **81,1 %** |\n| BN-ResNet-200 | 81,8 % | 83,1 % | 83,5 % |\n| NF-ResNet-200 | **82,9 %** | **84,1 %** | **84,3 %** |\n\n*Tableau : deux groupes extraits de la Table 4 de l'article ; précision top-1 après préentraînement puis affinage sur ImageNet. Le tableau complet comprend aussi ResNet-101 et 152, avec des résultats concordants.*\n\nLa version NF l'emporte dans chaque groupe, d'environ 1 point de pourcentage. Avec NFNet-F4+, une version un peu élargie préentraînée sur 20 époques, les auteurs atteignent 89,2 % après affinage, le meilleur résultat obtenu à l'époque par apprentissage par transfert.\n\n## Conclusion\n\nSans BatchNorm, l'entraînement va même plus vite. Seulement, tout ce que BatchNorm faisait au passage, il faut le reprendre soi-même, un point après l'autre. Le décalage de la moyenne revient à la Scaled Weight Standardization et au gain, la taille de la branche résiduelle à $\\alpha$, $\\beta$ et SkipInit, la régularisation au Dropout et aux fortes augmentations de données, et l'entraînement en grands lots tient grâce à AGC.\n\nPour l'utiliser directement, timm propose `dm_nfnet_f0` à `dm_nfnet_f6`, ainsi que la série `eca_nfnet_l0`, entraînée par timm lui-même et allégée pour les GPU ; AGC peut aussi s'appliquer seul à votre propre modèle avec `adaptive_clip_grad`.\n\nAh, au fait, si vous entraînez une variante de NFNet, quelle qu'elle soit, pensez à activer l'écrêtage du gradient. Inutile de tenter le diable !\n","imageSizes":{"/content/papers/assets/9c1e7cd2c8c545de3ebab245e654b0dd69b3921c4f0479f0e99131833bdfc5f2.png":{"width":1004,"height":417},"/content/papers/assets/6dc27a9ccca4de619c057562ea6384aab3c095ddc08b22f475601ab03fcf03bb.png":{"width":966,"height":288},"/content/papers/assets/7552bff30433f4f33bec5edb2421acd94d61d452888dde6df17557778e8fb1c6.png":{"width":1004,"height":783}}}
