JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

馴服資料飢渴:DeiT、蒸餾與混合架構

純 ViT 需要海量資料——學會讓 Transformer 在你手邊資料集上也能訓練的技巧(蒸餾、混合架構、巧用歸納偏置)。

資料飢渴的問題

在前三篇導引中,我們從零打造了一個 視覺 Transformer:把影像切成 patch、讓這些 patch 透過注意力彼此交談,再堆疊成一個深層編碼器。它運作得很漂亮——但前提是你餵得夠多。原始 ViT 論文揭露了一個令人不太舒服的結論:當一個普通的 ViT 與規模相當的卷積神經網路(CNN)都從零開始、只在像 ImageNet-1k(約 130 萬張標註照片)這種中等規模的資料集上訓練時,通常是 CNN 勝出。ViT 只有在先於某個極龐大的資料集上預訓練——例如 Google 內部、約 3 億張影像的 JFT-300M——之後才會超車 CNN。同樣的架構,結論卻天差地別,完全取決於你灌進去多少資料。

為什麼?回想第一篇導引中我們認識的 歸納偏置——模型在看到任何一個範例之前就帶著的內建假設。CNN 天生就相信關於影像的兩件事:相鄰的像素彼此相關(局部性),以及一個物體不論出現在哪裡都還是同一個物體(平移不變性)。普通的 ViT 幾乎什麼都不信。它的注意力層能以同等的容易程度把任一 patch 連到任一其他 patch,所以在模型眼中,一張照片一開始只是一袋沒有順序的小磚塊,沒有「近」或「遠」的概念。CNN 免費得到的一切——局部性、貓在畫面任何角落都還是貓——ViT 都必須從原始範例中自己發現。從資料中發現一條自然律,所需的範例遠多於直接把它當成假設交到手上。

規模曲線:在小資料上 CNN 領先,但 ViT 較陡的曲線會在越過某個資料門檻後超車。

在對數座標上的兩條『準確率對資料量』曲線;CNN 曲線起點較高並趨於平緩,而 ViT 曲線起點較低、上升較陡,並在資料量大時越過 CNN 之上。

把它放進腦中最清楚的方式是一條規模曲線:縱軸畫準確率,橫軸畫資料集大小(取對數刻度),CNN 一條線、ViT 一條線。在左側資料稀少之處,CNN 的線位置較高——它的假設正在扛起資料還無法提供的重擔。ViT 的線起點較低,但爬升更陡,因為它有更多自由可以發揮、更少被寫死的假設拖住。兩條線會在某個資料門檻交叉;只有在交叉點的右側——在數億張影像的國度裡——ViT 才會超前並持續拉開。在門檻以下,CNN 免費的假設根本就是更划算的買賣。

再談歸納偏置:CNN 的免費禮物

我們放慢腳步,精確點名 CNN 免費得到了什麼,因為每一份免費禮物都是「我們必須補回什麼」的線索。歸納偏置不過就是一種先驗——一種在任何訓練發生之前就形塑模型『覺得什麼容易學』的內建信念。好的先驗像護欄:它縮小模型必須搜尋的可能性空間,讓它能用少得多的範例找到合理的答案。CNN 把三個先驗烤得如此之深,深到初學者很少察覺它們其實是一種選擇。

第一個先驗是局部性:一次卷積只看一小窗相鄰像素(例如 3×3),假設關於一個像素最重要的事就在它緊鄰的鄰里——一條邊、一個角、一片紋理。第二個是權重共享:把同一個小濾波器滑過影像的每一個位置。因為到處重複使用完全相同的濾波器,在左上角偵測到的特徵,會被一模一樣的權重在右下角偵測到。這帶來平移等變性:平移輸入,特徵圖就跟著一起平移。再配上池化,它就成了大致的平移不變性——貓不論坐在哪裡都被認成貓。ViT 預設兩個先驗都沒有:每個 patch 位置各自學自己的關係,也沒有任何東西強迫一個在某個角落學到的特徵能轉移到另一個角落。

堆疊局部層:每個神經元只看一小窗,但深度讓最深的神經元的感受野涵蓋整張影像。

一疊層,頂端的輸出神經元透過逐層加寬、由 3 格寬的局部窗構成的三角形向下連接,直到在底部橫跨整列輸入。

第三份禮物是多尺度階層。透過交替使用卷積與池化(池化會把解析度減半),CNN 在淺層看到細小的細節,越往深層看到越粗、越大的結構——正好對應影像被組織的方式,從邊到紋理、到部件、到整個物體。如同這個圖示所呈現的,堆疊局部層會讓每個深層神經元的感受野不斷成長,直到它實質上看見整張影像,但它是逐步而且在地地抵達那裡。這三個先驗——局部性、平移等變性、多尺度階層——正是小資料情境所渴求的,因為它們編碼了關於影像的真實事實,否則模型就得自己重新發現。轉折在這裡:ViT 缺少它們,既是弱點(資料飢渴)也是強項(在巨大規模下,它能自由學到 CNN 僵硬先驗會禁止的關係)。這個取捨就是整篇導引的故事——也正是 混合 CNN–Transformer 這個想法的動機:把一部分 歸納偏置 補回去,又不把 Transformer 的觸及範圍關進籠子。

DeiT:用適量資料訓練 ViT

2021 年,Facebook AI 的一個團隊問了一個犀利的問題:ViT 的資料飢渴,到底真的關乎架構,還是只關乎訓練配方?他們的答案是 資料高效影像 Transformer,也就是 DeiT——一個用 ImageNet-1k、不需要 JFT-300M,在一台機器上花幾天就訓練到具競爭力準確率的 ViT。DeiT 立足於兩根支柱。第一根是好得多的訓練配方。第二根是透過一個全新特殊 token 進行的知識蒸餾。兩者合起來,讓一個 Transformer 能從你真的下載得到的資料集學會良好的視覺習慣。

第一根支柱不起眼但關鍵:好好地訓練它。因為 ViT 內建的假設太少,它在中等規模資料集上很容易過擬合,所以 DeiT 大量倚賴資料增強與正則化,來製造資料本身缺乏的多樣性。配方堆疊了 RandAugment(隨機組合旋轉、色彩偏移、剪切)、Mixup 與 CutMix(混合兩張影像及其標籤,讓模型永遠不會看到一模一樣的範例兩次)、隨機抹除、隨機深度(訓練時隨機跳過整層)、標籤平滑,以及搭配長暖身與餘弦衰減的 AdamW 優化器。這些都沒有改動架構;改動的是飲食。教訓是:表面上的資料飢渴有一大塊其實是調校問題——給 ViT 足夠的正則化與增強,它在 ImageNet-1k 上就不再崩潰。

第二根支柱才是聰明的部分。回想第三篇導引的 類別 token——我們前置到 patch 序列的那個額外可學習 token,把它最終的狀態讀出來當作整張影像的摘要拿去分類。DeiT 在它旁邊再加一個第二個特殊 token,也就是蒸餾 token。類別 token 一如既往,被訓練去預測真實標籤。蒸餾 token 則被訓練去預測另一個已經訓練好的 CNN——也就是老師——的輸出。想像一個學生 Transformer 坐在一位經驗老到的 CNN 家教旁邊:類別 token 研讀課本(真實標籤),而蒸餾 token 則從家教的肩膀後面看過去、抄下家教怎麼作答。因為這位 CNN 家教骨子裡早就帶著局部性與平移的 歸納偏置,學生就間接地吸收了那份偏置——透過家教的答案——而完全不必把任何卷積寫死進去。

\mathcal{L} \;=\; \tfrac{1}{2}\,\mathcal{L}_{\mathrm{CE}}\!\big(\mathrm{softmax}(z_{\mathrm{class}}),\, y\big) \;+\; \tfrac{1}{2}\,\mathcal{L}_{\mathrm{CE}}\!\big(\mathrm{softmax}(z_{\mathrm{distill}}),\, y_{\mathrm{teacher}}\big)

DeiT 的訓練損失:一半來自類別 token 對真實標籤,一半來自蒸餾 token 對老師。

把這條式子讀成『總訓練損失是兩個交叉熵損失的等權平均,每個 token 各一個』。逐個符號來看:L 是我們要最小化的那一個數字。L_CE 是交叉熵損失,當預測出的機率分布與其目標不一致時它會很大。z_class 與 z_distill 分別是從類別 token 與蒸餾 token 讀出的原始輸出分數(logits);softmax 把每一組 logits 變成總和為 1 的機率。y 是真實標籤,y_teacher 是 CNN 老師的預測。兩個二分之一係數表示我們同等信任課本與家教。用一個三類別(貓、山貓、狗)的小例子:假設真實標籤是 = [0, 1, 0]。類別 token 的 softmax 是 [0.2, 0.7, 0.1],所以它的交叉熵是 -ln(0.7) ≈ 0.357。老師也說,而蒸餾 token 的 softmax 是 [0.1, 0.6, 0.3],得到 -ln(0.6) ≈ 0.511。總損失是二分之一 × 0.357 加上二分之一 × 0.511 ≈ 0.434。最小化它,會同時把類別 token 推向真相、把蒸餾 token 推向家教。測試時,DeiT 就把兩個 token 的預測平均起來。

混合 CNN–Transformer 架構

蒸餾是從外部教會一個純 Transformer 好習慣。混合 CNN–Transformer 走的是更直接的路:在前端裝上一個小 CNN。你不再把原始影像切成像素 patch 再嵌入,而是先讓影像通過一個 CNN stem(前端莖部)——少數幾層卷積——產生一張特徵圖,然後把那些特徵攤平成一個序列,當作 Transformer 的 token。這其實是原始 ViT 論文中的一個變體,也是許多現代視覺骨幹背後的精神。

為什麼這有幫助?在第一篇導引中,標準的 patch 嵌入 對每個原始 16×16 像素 patch 套用一次線性投影——單一、淺層的一步,patch 之間沒有任何局部性的概念。CNN stem 用好幾層卷積取代它,這意味著 Transformer 看到的第一樣東西就已經具備局部性意識:每個 token 摘要了一小片鄰里,邊與紋理都已被偵測出來,平移等變性也已經就位。換句話說,stem 幾乎不花成本就把一份 歸納偏置 的領先優勢交到 Transformer 手上。它也平滑了淺層的優化——純 ViT 在最前面幾層出了名地神經質,而卷積 stem 會穩住梯度,讓訓練更可靠地收斂。

CNN stem 萃取出一張特徵圖;它的格點成為餵給 Transformer 的 token。

一張影像通過數個卷積與池化階段,變成一個小的特徵圖格點,其格子接著被攤平成一列 token。

# Hybrid ViT: a CNN stem turns an image into tokens for the Transformer.
# Instead of embedding raw 16x16 pixel patches, a few conv layers extract a
# locality-aware feature map first, then we flatten its grid into tokens.

import torch.nn as nn

class HybridStem(nn.Module):
    def __init__(self, d_model=768):
        super().__init__()
        # Each conv keeps locality + weight sharing -> free inductive bias.
        # Strided convs shrink H and W, so the feature map becomes a token grid.
        self.cnn = nn.Sequential(
            nn.Conv2d(3,    64, 3, stride=2, padding=1), nn.ReLU(),  # 224 -> 112
            nn.Conv2d(64,  128, 3, stride=2, padding=1), nn.ReLU(),  # 112 -> 56
            nn.Conv2d(128, 256, 3, stride=2, padding=1), nn.ReLU(),  # 56  -> 28
            nn.Conv2d(256, 512, 3, stride=2, padding=1), nn.ReLU(),  # 28  -> 14
            nn.Conv2d(512, d_model, 1),                              # 1x1: project to d_model
        )

    def forward(self, x):                       # x: (B, 3, 224, 224)
        f = self.cnn(x)                         # f: (B, d_model, 14, 14)
        B, C, H, W = f.shape                    # H = W = 14  ->  196 cells
        tokens = f.flatten(2).transpose(1, 2)   # (B, 196, d_model): one token per cell
        return tokens                           # hand these to the Transformer encoder
CNN stem 把一張 224×224 影像變成 196 個具局部性意識的 token——數量與 16×16 patch 相同,但已經過卷積處理。

心中的畫面:讓 CNN 去做容易的在地觀看——找出那些明顯最適合用小滑動濾波器偵測的邊與紋理——再讓注意力去處理『這些部件在整張影像上彼此如何關聯』的全域推理。不過,這個取捨是真實存在的。一旦你在前端就決定用卷積,你也重新引入了 ViT 當初設計來逃離的那份僵硬,所以在極端規模下,純 ViT 有可能些微勝過混合架構。但在多數實務工作者真正身處的資料情境裡,那一點彈性的犧牲換來明顯更好的小資料表現——通常是一筆划算的交易。

實用指南:何時選哪一種

那麼你該伸手拿哪一個?四個因素決定它:你有多少標註資料、你的算力預算、是否存在合適的預訓練模型,以及你的部署限制(延遲、記憶體、目標硬體)。好消息是,答案幾乎從來不是『發明新東西』——它幾乎總是『從架上的選項裡挑對的那一個』。

  1. 估計你真正的標註資料集大小——不是有多少張原始影像,而是有多少張對你的任務帶有可用標籤。
  2. 查看是否存在與你的領域相近的預訓練模型;若有,微調它幾乎總會勝過從零訓練任何東西。
  3. 衡量你的算力預算:從零訓練一個 ViT 很昂貴,而微調一個預訓練模型、或訓練一個小型混合架構則便宜得多。
  4. 最後檢查部署限制——所需的延遲、記憶體上限與目標硬體,可能不論準確率高低都把最大的模型排除掉。

具體的經驗法則。資料少又沒有預訓練權重:偏好 CNN、混合架構,或帶蒸餾的 資料高效影像 Transformer——任何能注入歸納偏置的東西。在這裡,從零訓練的普通 ViT 是唯一該避開的組合。混合 CNN–Transformer 在你既想要一些 Transformer 的全域推理、又只能在中等資料集上訓練時,是個甜蜜點。資料充裕,或有一個強大、可下載的預訓練 ViT:就微調那個 ViT,享受它的規模效益。

而這裡就是那個偉大的均衡器,也是讓上述一切對初學者真正可行的東西:遷移學習。你幾乎從不從零訓練一個 視覺 Transformer。相反地,你下載別人在巨大資料集上預訓練好的權重——他們已經替你繳了 JFT-300M 規模的『資料稅』——然後在你那幾千張影像上微調。一個預訓練過的 ViT 已經從那數億個範例學會了局部性與平移不變性,所以在你的小資料集上,它表現得像一個完全不再資料飢渴的模型。預訓練權重把 ViT 最大的弱點,變成日常工作中無關緊要的小事。