{"notes":[{"id":"classic-cnns/nfnet","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"NFNet","subtitle":"沒有 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":"zh-hant","path":"/content/papers/zh-hant/classic-cnns/nfnet.2b49d5235475.md","headings":[{"id":"定義問題","text":"定義問題","depth":2},{"id":"batchnorm-到底幫了什麼","text":"BatchNorm 到底幫了什麼","depth":3},{"id":"平均值偏移長什麼樣子","text":"平均值偏移長什麼樣子","depth":3},{"id":"解決問題","text":"解決問題","depth":2},{"id":"先把-nf-resnet-補齊","text":"先把 NF-ResNet 補齊","depth":3},{"id":"自適應梯度裁剪","text":"自適應梯度裁剪","depth":3},{"id":"nfnet-的架構","text":"NFNet 的架構","depth":3},{"id":"討論","text":"討論","depth":2},{"id":"每一步改動的貢獻","text":"每一步改動的貢獻","depth":3},{"id":"拿掉-bn-真的比較快嗎","text":"拿掉 BN 真的比較快嗎","depth":3},{"id":"和其他模型比","text":"和其他模型比","depth":3},{"id":"預訓練之後再微調","text":"預訓練之後再微調","depth":3},{"id":"結論","text":"結論","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":"批次標準化","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":"zh-hant","path":"/content/papers/zh-hant/normalization/batchnorm.062711be4913.md","headings":[{"id":"定義問題","text":"定義問題","depth":2},{"id":"解決問題","text":"解決問題","depth":2},{"id":"提高學習率","text":"提高學習率","depth":3},{"id":"不需要-dropout","text":"不需要 Dropout","depth":3},{"id":"討論","text":"討論","depth":2},{"id":"在-mnist-實驗","text":"在 MNIST 實驗","depth":3},{"id":"在-imagenet-實驗","text":"在 ImageNet 實驗","depth":3},{"id":"結論","text":"結論","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"normalization/layernorm","relation":"next","topic":"正規化究竟沿哪個維度算"}]}],"text":"2015 年之後，幾乎每個影像分類網路裡都有 BatchNorm。它讓深網路好訓練、可以用大學習率，還附帶一點正規化效果，好用到大家很少再去想它的缺點。\n\n\n> [!TIP]\n> \n> BatchNorm 可以先看我們的筆記：\n> \n> - **[[15.02] BatchNorm：批次標準化](/blog/papers/normalization/batchnorm)**\n\n\n缺點其實不少。它要算批次統計量，佔記憶體也花時間；訓練時用批次統計、推論時用移動平均，兩邊行為不一致；它還讓同一個批次裡的樣本互相影響，這是最麻煩的一點。批次太小時效果變差，分散式訓練時容易出錯，在對比學習裡還得特別處理，免得模型靠批次統計偷看答案。\n\nDeepMind 的 Andrew Brock、Soham De、Samuel L. Smith 與 Karen Simonyan 想把 BatchNorm 整個拿掉。他們在前一篇論文已經做出不用正規化層的 NF-ResNet，準確率追上同級的 BN-ResNet；這篇要解決的是剩下的兩個問題。\n\n## 定義問題\n\n### BatchNorm 到底幫了什麼\n\n要拿掉 BatchNorm，得先知道它做了哪些事，再一件一件找替代品。作者整理了四項：\n\n1. **把殘差分支的輸出壓小。** BN 放在殘差分支上時，初始化階段的分支輸出比較小，訊號主要走捷徑，訓練初期的梯度比較穩。\n2. **消除平均值偏移。** ReLU、GELU 這些激活函數的輸出平均值不是 0，一層層疊下去，不同樣本的特徵會越來越像。BN 每層把平均值拉回 0。\n3. **正規化效果。** 批次統計量有雜訊，有點像在訓練時加噪音。\n4. **讓大批次訓練可行。** BN 讓損失曲面比較平滑，可以用更大的學習率，大批次訓練才有意義。\n\n前一篇的 NF-ResNet 用初始化和權重標準化處理了前兩項，再加 Dropout 和 Stochastic Depth 補第三項。剩下的問題有兩個：批次大到 4096 以上時訓練不穩，最好的成績也還追不上 EfficientNet。\n\n### 平均值偏移長什麼樣子\n\n第二項比較抽象，我們先看看沒有正規化時會發生什麼事。取 64 個互不相關的隨機樣本，每次經過 ReLU 和一層用 He 初始化的權重，疊 20 層，量不同樣本之間的平均 cosine 相似度；另一組換成 NF-ResNet 的做法，等一下再解釋：\n\n```python\nimport math\nimport torch\nimport torch.nn.functional as F\n\ntorch.manual_seed(0)\nx = torch.randn(64, 512)                      # 64 張互不相關的樣本，各 512 個特徵\n\ndef ws(W):\n    \"\"\"Scaled Weight Standardization：每個輸出單元的權重減去平均，再除以 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    \"\"\"不同樣本之間的平均 cosine 相似度\"\"\"\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))      # 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      # He 初始化，沒有正規化\n    std = (gamma * F.relu(std)) @ ws(W).T                   # 標準化權重加增益\n    if layer in (1, 5, 10, 20):\n        print(f\"第 {layer:>2} 層｜He 初始化：樣本間相似度 {mean_cos(plain):.2f}\"\n              f\"｜Scaled WS：相似度 {mean_cos(std):.2f}，變異數 {std.var().item():.2f}\")\n```\n\n```text\nγ = 1.7129\n第  1 層｜He 初始化：樣本間相似度 0.31｜Scaled WS：相似度 0.00，變異數 0.99\n第  5 層｜He 初始化：樣本間相似度 0.72｜Scaled WS：相似度 -0.00，變異數 0.98\n第 10 層｜He 初始化：樣本間相似度 0.87｜Scaled WS：相似度 0.00，變異數 0.95\n第 20 層｜He 初始化：樣本間相似度 0.96｜Scaled WS：相似度 0.00，變異數 0.88\n```\n\n輸入的 64 個樣本本來互不相關，經過 20 層之後，He 初始化那組的平均相似度已經到 0.96，幾乎所有樣本都長得一樣。原因是 ReLU 的輸出都大於等於 0，每一層都把同一個正的平均值加進所有樣本。論文引用的研究指出，這種偏移會讓深網路在初始化時對所有樣本預測同一個類別。\n\n## 解決問題\n\n### 先把 NF-ResNet 補齊\n\n右邊那組用的是前一篇 NF-ResNet 的兩個技巧。\n\n第一個是 **Scaled Weight Standardization**。卷積層的每個輸出單元，權重先減去自己的平均，再除以標準差和 $\\sqrt{N}$：\n\n$$\n\\hat{W}_{ij}=\\frac{W_{ij}-\\mu_i}{\\sqrt{N}\\,\\sigma_i},\n$$\n\n$N$ 是 fan-in。減掉平均之後，每個輸出單元的權重加起來等於 0，激活函數帶進來的那個共同的正值就被抵消了，這就是上面相似度一直停在 0 的原因。\n\n第二個是激活函數後面乘上的增益 $\\gamma$。對 ReLU 來說 $\\gamma=\\sqrt{2/(1-1/\\pi)}$，大約 1.71，剛好補回 ReLU 砍掉負值造成的變異數損失。兩者合起來，每一層的輸出變異數大致維持在 1。20 層後的 0.88 是一層層累積下來的小偏差，和另一組樣本幾乎變成同一個比起來，小得多。\n\n有了「每層大致保持變異數」的分支，NF-ResNet 的殘差區塊寫成\n\n$$\nh_{i+1}=h_i+\\alpha\\,f_i\\!\\left(h_i/\\beta_i\\right).\n$$\n\n$\\beta_i$ 是預先算好的輸入標準差，分支吃進去的訊號一律縮回變異數 1；$\\alpha$ 是一個小常數（通常取 0.2），控制每個區塊讓變異數增加多少。所以主幹的變異數可以直接算出來，每過一個區塊加 $\\alpha^2$。我們搭 12 個區塊驗證：\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    \"\"\"殘差分支：兩層「增益 ReLU + 標準化權重」，輸出變異數約等於輸入\"\"\"\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)                # 預先算好的輸入標準差\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\"第 {i:>2} 個區塊後：預期變異數 {expected:.2f}，實際 {h.var().item():.2f}\")\n```\n\n```text\n第  1 個區塊後：預期變異數 1.04，實際 1.04\n第  4 個區塊後：預期變異數 1.16，實際 1.16\n第  8 個區塊後：預期變異數 1.32，實際 1.32\n第 12 個區塊後：預期變異數 1.48，實際 1.48\n```\n\n實際的變異數和預期完全一致。這就是 BatchNorm 第一項功能的替代品：因為 $\\alpha$ 很小，訊號主要走捷徑，分支一開始只做小修正。遇到要縮小特徵圖的區塊，捷徑也吃縮放過的輸入，變異數重新從 $1+\\alpha^2$ 算起。另外，每個殘差分支最後還有一個初始化為 0 的可學習純量（SkipInit），訓練剛開始時整個分支等於關掉。\n\n到這裡，NF-ResNet 在批次 1024 時已經能追上 BN-ResNet，可是批次一大就不穩。\n\n### 自適應梯度裁剪\n\n大批次訓練要用大學習率，梯度一大，參數就可能被一步推得太遠。常見的解法是梯度裁剪：\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\n作者發現這樣確實能撐到比較大的批次，可是門檻 $\\lambda$ 非常難調。模型深度、批次大小、學習率一變，就得重新找。\n\n問題出在一個固定的門檻不管參數本身有多大。同樣是 0.01 的梯度，加在大小 1 的權重上只是 1% 的變動，加在大小 0.01 的權重上就是整個翻掉。作者因此改看比值：\n\n$$\n\\frac{\\lVert G^\\ell\\rVert_F}{\\lVert W^\\ell\\rVert_F},\n$$\n\n這個比值大致代表一步更新會讓權重改變多少比例。實際做的時候不是整層算一次，而是以**單元**為單位，也就是權重矩陣的每一列（卷積的話是每個輸出通道，連同它的空間範圍）：\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)$，$\\epsilon=10^{-3}$，避免初始化為 0 的參數梯度永遠被裁成 0。這就是 Adaptive Gradient Clipping（AGC）。\n\n拿一個三個單元的小例子看它怎麼裁：\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]])           # 三個輸出單元，權重大小不同\nG = torch.tensor([[0.005, 0.0, 0.0],\n                  [0.0, 0.05, 0.0],\n                  [0.0, 0.0, 0.01]])          # 三列梯度\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\"單元 {i}：‖W‖={w_norm[i].item():.2f}，‖G‖={g_norm[i].item():.3f}，比值 {ratio[i].item():.3f}\"\n          f\" → 裁剪後 ‖G‖={clipped[i].norm().item():.4f}\")\nprint(\"整體梯度範數：\", round(G.norm().item(), 4))\n```\n\n```text\n單元 0：‖W‖=1.00，‖G‖=0.005，比值 0.005 → 裁剪後 ‖G‖=0.0050\n單元 1：‖W‖=0.10，‖G‖=0.050，比值 0.500 → 裁剪後 ‖G‖=0.0010\n單元 2：‖W‖=2.00，‖G‖=0.010，比值 0.005 → 裁剪後 ‖G‖=0.0100\n整體梯度範數： 0.0512\n```\n\n只有單元 1 被裁了。它的權重只有 0.1，梯度卻有 0.05，一步就要改掉一半，所以被壓到 $\\lambda\\times0.1=0.001$。另外兩個單元的比值都在門檻以下，原封不動。如果用一般的整體裁剪，三個單元會被同一個係數一起縮小，真正需要壓的那一個反而不一定壓得夠。\n\n上面的寫法和 timm 的 `timm.utils.agc.adaptive_clip_grad` 結果一樣，三個單元裁剪後分別是 0.005、0.001、0.01。\n\n[![兩張折線圖。(a) 橫軸是批次大小（256 到 4096），縱軸是 Top-1 準確率；藍線是 BatchNorm，橘線是 NF-ResNet，綠線是 NF-ResNet+AGC，實線為 ResNet50、虛線為 ResNet200。橘線在批次 2048 之後急遽下滑，綠線一路維持在和藍線相近或更高的位置。(b) ResNet50 在不同裁剪門檻（0.01 到 0.16）下的準確率，每條線是一種批次大小；批次 4096 的紫線在門檻 0.02 之後崩掉](/content/papers/assets/9c1e7cd2c8c545de3ebab245e654b0dd69b3921c4f0479f0e99131833bdfc5f2.png)](/content/papers/assets/9c1e7cd2c8c545de3ebab245e654b0dd69b3921c4f0479f0e99131833bdfc5f2.png)\n\n*原論文 Figure 2，取自 [**v1 PDF 第 4 頁**](https://arxiv.org/pdf/2102.06171v1#page=4)。(a) AGC 讓 NF-ResNet 能用更大的批次；(b) 不同裁剪門檻 $\\lambda$ 的表現。*\n\n左圖的橘線是沒有 AGC 的 NF-ResNet，批次到 2048 以後就崩了；加上 AGC 的綠線一路撐到 4096，準確率和 BatchNorm 的藍線差不多甚至更好。右圖看門檻：批次越大，門檻要越小，批次 4096 時只有 0.01 和 0.02 撐得住。\n\n作者也試了哪些層需要裁。最後的全連接層不裁比較好；第一層卷積常常可以不裁，四個 stage 的權重則在批次 4096 時都得裁。之後的實驗都對最後一層以外的所有參數使用 AGC。\n\n### NFNet 的架構\n\n有了 AGC，作者接著設計新的網路。這次的目標不是 FLOPs 或參數量，而是**在 TPU 和 GPU 上實際的訓練速度**。作者指出 EfficientNet 的 FLOPs 很低，訓練速度卻沒有跟著快：EfficientNet-B0 的 FLOPs 只有 ResNet-50 的十分之一，在 GPU 或 TPU 上訓練起來卻差不多慢。\n\n[![NFNet 的 bottleneck 區塊示意圖：輸入先乘上 1/β，經過 1x1 卷積、兩個 3x3 卷積、再一個 1x1 卷積，乘上 α 後和捷徑相加。右側列出各 stage 的寬度與深度：ResNet 寬度 [256, 512, 1024, 2048]、NFNet 寬度 [256, 512, 1536, 1536]；ResNet 深度 [3, 4, 6, 3]、[3, 4, 23, 3]…，NFNet 深度 [1, 2, 6, 3] 乘上 N](/content/papers/assets/6dc27a9ccca4de619c057562ea6384aab3c095ddc08b22f475601ab03fcf03bb.png)](/content/papers/assets/6dc27a9ccca4de619c057562ea6384aab3c095ddc08b22f475601ab03fcf03bb.png)\n\n*原論文 Figure 3，取自 [**v1 PDF 第 5 頁**](https://arxiv.org/pdf/2102.06171v1#page=5)。NFNet 的 bottleneck 區塊，以及和 ResNet 在寬度、深度上的差別。*\n\n起點是 SE-ResNeXt-D，激活函數換成 GELU，再改四個地方：\n\n1. **分組寬度固定 128。** 分組越細 FLOPs 越低，但計算密度也跟著下降，在加速器上實際不會變快。\n2. **深度配置 [1, 2, 6, 3]。** 較大的模型直接乘上 N，例如 F1 是 [2, 4, 12, 6]。ResNet 傳統上只加深中間兩個 stage，作者認為不是最好的做法。\n3. **寬度配置 [256, 512, 1536, 1536]。** 把容量集中到第三個 stage，第四個 stage 略為縮小，訓練速度大致不變。\n4. **區塊裡多一層 $3\\times3$ 分組卷積。** 就是圖上連續的兩個 3x3。作者說它只多一點點 FLOPs，在他們的加速器上，訓練時間幾乎不受影響。\n\n放大模型時只加深度和解析度，不加寬度，作者發現加寬對 ResNet 類的骨幹效果不好。每往上一級，訓練時間大約加倍，Dropout 也跟著調高，從 F0 的 0.2 到 F6 的 0.5。少了 BatchNorm 的隱性正規化，不靠 Dropout 這些明確的正規化，模型會嚴重過擬合。\n\n## 討論\n\n### 每一步改動的貢獻\n\n| 設定 | F0 | F1 | F2 | F3 |\n| --- | ---: | ---: | ---: | ---: |\n| 起點 | 80.4% | 81.7% | 82.0% | 82.3% |\n| ＋修改寬度 | 80.9% | 81.8% | 82.0% | 82.3% |\n| ＋第二層 $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| 原本寬度＋上述增強 | 83.1% | 84.5% | 85.0% | 85.5% |\n\n*表：摘自原論文 Table 2，ImageNet top-1 準確率，3 個隨機種子的平均，訓練 360 個 epoch、批次 4096。*\n\n架構上的兩個改動合起來多了 0.4 到 0.9 個百分點，真正拉開差距的是資料增強，光 RandAugment 就有 1 到 1.7 個百分點。這些強增強正是原本的 NF-ResNet 撐不住的東西，AGC 讓它們能用上。最後一列換回 ResNet 原本的寬度配置，四個尺寸都比較差，證明寬度的調整確實有用。\n\n作者也把同樣的增強套到 EfficientNet 上：RandAugment 有幫助，但層數超過 2、或再加 MixUp、CutMix 都沒有更好。他們據此認為 NFNet 的進步不單是增強帶來的。\n\n### 拿掉 BN 真的比較快嗎\n\n把同樣的架構改回 BatchNorm 訓練，準確率和每一步的訓練時間如下：\n\n| 模型 | NF 版本 | 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*表：摘自原論文 Table 6，ImageNet top-1 準確率與 TPUv3 上每一步的訓練時間。*\n\n準確率差不多，NF 版本略好或打平，每一步的訓練時間則少了兩成五到四成。作者還提到，更大的 F4、F5 改用 BatchNorm 時，加不加 AGC 都訓練不穩，他們推測是為了塞進記憶體而用的 bfloat16，和批次統計量的計算處不來。\n\n### 和其他模型比\n\n[![散佈折線圖，橫軸是 TPUv3 上每一步的訓練時間（秒），縱軸是 ImageNet Top-1 準確率（80% 到 87%）。紅色實線是 NFNet-F0 到 F5，位置在所有其他模型的左上方；藍色虛線是 EfficientNet B2 到 B7，綠色是 LambdaNet，橘色是 BoTNet，黑點是 DeiT。NFNet-F1 的準確率和最右邊的 EffNet-B7 相當，訓練時間卻短得多](/content/papers/assets/7552bff30433f4f33bec5edb2421acd94d61d452888dde6df17557778e8fb1c6.png)](/content/papers/assets/7552bff30433f4f33bec5edb2421acd94d61d452888dde6df17557778e8fb1c6.png)\n\n*原論文 Figure 1，取自 [**v1 PDF 第 1 頁**](https://arxiv.org/pdf/2102.06171v1#page=1)。ImageNet 驗證準確率對 TPUv3 訓練時間，單一模型、單一裁切。*\n\n紅線整條都在其他模型的左上方，也就是同樣的訓練時間準確率更高。最常被引用的一組數字是 NFNet-F1 和 EfficientNet-B7：兩者都是 84.7%，F1 每一步只要 158.5 ms，B7 要 1397.0 ms，快了將近 9 倍。最大的 NFNet-F5 做到 86.0%，加上 SAM 優化器，F5 到 86.3%，F6 到 86.5%，是當時不用額外資料的最佳成績。\n\n不過這條紅線是按訓練時間畫的。換成參數量來看，NFNet 一點也不省：F0 有 71.5M 參數、12.38 GFLOPs，EfficientNet-B0 只有 5.3M、0.39 GFLOPs；F1 的參數也是 B7 的兩倍。作者在論文裡說得很清楚，他們優化的就是訓練時間，FLOPs 和參數量不是目標。\n\n### 預訓練之後再微調\n\n最後一組實驗，作者在 3 億張有標註的影像上預訓練，再到 ImageNet 微調。BatchNorm 的正規化效果在這種資料量下可能反而礙事，作者推測沒有 BN 的網路更適合這種情境：\n\n| 模型 | 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*表：摘自原論文 Table 4 的兩組，預訓練後在 ImageNet 微調的 top-1 準確率。完整的表格還有 ResNet-101 與 152，結果一致。*\n\n每一組都是 NF 版本贏，差距大約 1 個百分點。作者再用稍微加寬的 NFNet-F4+ 預訓練 20 個 epoch，微調後做到 89.2%，是當時用遷移學習得到的最高成績。\n\n## 結論\n\nBatchNorm 拿掉了，訓練反而更快。只是 BatchNorm 原本順手做掉的事，得一件件自己補回來，平均值偏移交給 Scaled Weight Standardization 和增益，殘差分支的大小交給 $\\alpha$、$\\beta$ 和 SkipInit，正規化交給 Dropout 和強資料增強，大批次訓練則靠 AGC 撐住。\n\n想直接用的話，timm 裡有 `dm_nfnet_f0` 到 `dm_nfnet_f6`，還有 timm 自己訓練、為 GPU 瘦身過的 `eca_nfnet_l0` 系列；AGC 也能用 `adaptive_clip_grad` 單獨套在自己的模型上。\n\n噢，對了，如果你要訓練任何 NFNet 變體，要記得開梯度裁剪，別不信邪呀！\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}}}
