電腦視覺:Vision Transformer (ViT) - 廠房裂縫影像分類,CNN 王座保衛戰

電腦視覺:Vision Transformer (ViT) - 廠房裂縫影像分類,CNN 王座保衛戰

把影像切成 16×16 的拼圖、當成一句話丟進 Transformer——ViT 用這個粗暴的想法挑戰了 CNN 三十年的視覺霸權。本篇用廠房混凝土裂縫巡檢資料實測四路對決:從頭訓練的 CNN vs ViT、預訓練探測的 ResNet vs ViT-Tiny,親眼見證「歸納偏置」與「大規模預訓練」的世紀交鋒。


核心貢獻者

Alexey Dosovitskiy 領銜的 Google Brain 團隊在 2020 年 10 月發表**《An Image is Worth 16x16 Words》**,標題本身就是整篇論文:一張圖值 196 個「單字」。當時 Transformer (Day 18) 已經統一了 NLP,但視覺領域仍是 CNN (Day 13) 的天下——這篇論文證明:只要預訓練資料夠大,不需要任何卷積,純 Transformer 就能打贏 CNN。它是後來 CLIP、SAM、多模態大模型的視覺骨幹,引用數已超過 5 萬次。


為什麼要挑戰 CNN?

Day 13 我們學過,CNN 的成功來自兩個內建假設——局部性 (相鄰像素相關) 與平移不變性 (裂縫在左上角和右下角是同一種東西)。這些假設叫做歸納偏置 (Inductive Bias),是 CNN 與生俱來的「圖像常識」。

但常識也是枷鎖:卷積核一次只看 3×3 的鄰居,要看懂「整張圖的兩端有關聯」得靠一層層堆疊慢慢擴大感受野。Transformer 的 Self-Attention 天生就是全域的——第 1 個 patch 和第 196 個 patch 在第一層就能直接對話。ViT 的賭注是:拋棄圖像常識,用海量資料把常識重新學回來,還能學得更好


1. 資料集來源

資料集:Concrete Crack Images for Classification

備註:本系列第一個真實影像資料集!4 萬張 227×227 的混凝土表面照片,一半有裂縫、一半沒有。廠房結構的裂縫巡檢正是職業安全的基本功——牆面、樑柱、地坪的裂縫是坍塌與設備基礎失效的前兆。

資料集特色與欄位介紹:

  • 類別:crack (有裂縫) / normal (無裂縫),各 2 萬張。
  • 本篇抽樣:固定隨機種子抽 3,000 張——訓練 2,000 (每類 1,000)、測試 1,000 (每類 500)。刻意用「小資料」情境,因為這才是工廠的現實:沒有人幫你標 100 萬張。

資料清理

  1. 從頭訓練組:縮到 64×64,直接轉張量。
  2. 預訓練探測組:依各模型的 ImageNet 前處理 (224×224 + 標準化),由 timm 自動配置。

2. 原理

2.1 一張圖,196 個單字

ViT 對影像的處理粗暴而優雅,總共三步:

Patch 切分示意
  1. 切拼圖 (Patch Embedding):224×224 的裂縫照片切成 16×16 的小方塊,得到 14×14 = 196 個 patch。每個 patch 攤平後乘上投影矩陣,變成一個向量——從此它就是一個「單字 (token)」。
  2. 加位置 (Position Embedding):攤成序列後「第幾格」的資訊沒了,所以每個 token 加上一個可學習的位置向量——不然模型分不清裂縫在樑上還是在地上。
  3. 前面插一個 [CLS] token:借用 BERT (Day 38) 的招式,這個特殊 token 負責在層層 attention 中「聽完全場」,最後拿它的輸出做分類。

z0=[xclass;  xp1E;  xp2E;  ;  xp196E]+Eposz_0 = [x_{class};\; x_p^1 E;\; x_p^2 E;\; \dots;\; x_p^{196} E] + E_{pos}

之後就是 Day 18 學過的標準 Transformer Encoder——Multi-Head Self-Attention 加 MLP,一字不改。

2.2 CNN vs ViT:兩種世界觀

面向CNNViT
感受野局部起步,逐層擴大第一層就是全域
歸納偏置局部性+平移不變性 (內建常識)幾乎為零 (常識要用資料買)
小資料表現穩,常識兜底不穩,容易學歪
大規模預訓練後提升有限反超,規模越大越強
計算複雜度對解析度線性Attention 對 token 數平方

一句話總結論文結論:資料低於千萬張等級,CNN 的常識是資產;資料到了億級,常識變成天花板。


3. 實戰

自己動手寫一個迷你 ViT

ViT 的架構簡單到可以自己寫——patch embedding 其實就是一個「stride = kernel size」的卷積:

class TinyViT(nn.Module):
    """64x64 影像 -> 8x8 patches -> 64 tokens, dim 128, depth 4"""
    def __init__(self, dim=128, depth=4, heads=4):
        super().__init__()
        self.patch = nn.Conv2d(3, dim, kernel_size=8, stride=8)  # 切拼圖+投影一次完成
        self.cls = nn.Parameter(torch.zeros(1, 1, dim))           # [CLS] token
        self.pos = nn.Parameter(torch.zeros(1, 65, dim))          # 64 patches + CLS
        layer = nn.TransformerEncoderLayer(d_model=dim, nhead=heads,
                                           dim_feedforward=dim * 2,
                                           batch_first=True, norm_first=True)
        self.encoder = nn.TransformerEncoder(layer, num_layers=depth)
        self.head = nn.Linear(dim, 2)

    def forward(self, x):
        x = self.patch(x).flatten(2).transpose(1, 2)              # B, 64, dim
        x = torch.cat([self.cls.expand(len(x), -1, -1), x], 1) + self.pos
        return self.head(self.encoder(x)[:, 0])                  # 只取 CLS 輸出

預訓練線性探測 (Linear Probe)

凍結預訓練模型當「特徵提取器」,只訓練最後一層邏輯回歸——CPU 幾十秒就能完成:

import timm
model = timm.create_model('vit_tiny_patch16_224.augreg_in21k_ft_in1k',
                          pretrained=True, num_classes=0)   # num_classes=0: 只出特徵
# 萃取特徵後: LogisticRegression().fit(features_train, y_train)

4. 模型評估

四路對決成績單 (測試集 1,000 張)

選手訓練方式測試準確率
SimpleCNN從頭訓練 (2,000 張, 64px)0.9800
TinyViT從頭訓練 (2,000 張, 64px)0.9760
ResNet18ImageNet 預訓練 + 線性探測0.9960
ViT-TinyImageNet-21k 預訓練 + 線性探測0.9970

第一戰場:小資料從頭訓練——常識的價值

從頭訓練學習曲線
  • CNN 穩定爬升不回頭:藍線一路向上,收在 0.980——卷積的內建常識讓它每一步都踩得穩。
  • ViT 有天分但情緒化:紅線在第 3~4 輪一度反超 (0.975),卻在第 6 輪突然崩到 0.931,最終以 0.976 惜敗。沒有歸納偏置兜底,它對訓練的隨機性更敏感。
  • 誠實註記:裂縫辨識是「局部紋理」任務、又只有二分類,對從頭訓練相對仁慈;ViT 原論文在更難的任務上,小資料的差距比這裡大得多。

第二戰場:預訓練探測——常識可以用錢買

四路對決總表
  • 兩個預訓練模型雙雙輾壓從頭訓練 (錯誤率從 2% 壓到 0.3~0.4%),而 ViT-Tiny (0.9970) 險勝 ResNet18 (0.9960) 登頂——在 ImageNet-21k 的千萬張圖上,ViT 已經把「圖像常識」學好了,而且學得比卷積更通用。
  • 效率彩蛋:凍結特徵 + 邏輯回歸,ViT-Tiny 在純 CPU 上 53 秒完成訓練——沒有 GPU 的工廠,這就是最務實的部署路線

工安視角的解讀

  • 裂縫巡檢自動化:廠房定檢拍照 → 預訓練 ViT 線性探測,千分之三的錯誤率已經超過肉眼疲勞時的表現;可疑照片再交人工複核。
  • 選型建議:廠內小樣本 (幾千張) 情境,別從頭訓練任何東西——預訓練 + 線性探測起步,不夠再微調。這也是 Day 14 遷移學習的精神在 Transformer 時代的延續。

5. 總結

我們學習了 Vision Transformer:

  • 核心思想:影像切成 16×16 的 patch 序列 + 位置編碼 + [CLS] token,之後就是標準 Transformer——「一張圖值 196 個單字」。
  • 歸納偏置的交易:ViT 拋棄 CNN 的內建常識,換取全域感受野與規模紅利;小資料吃虧 (且訓練較震盪),大規模預訓練後反超。
  • 實驗結論:從頭訓練 CNN 0.980 小勝 ViT 0.976;預訓練探測 ViT-Tiny 0.997 登頂——與論文的世界觀完全一致。
  • 工安啟示:結構裂縫巡檢這類任務,預訓練 ViT + 線性探測在 CPU 上一分鐘可訓練、千分之三錯誤率——AI 工安的入場券比想像中便宜。

下一篇繼續視覺新世代:如果每個員工的臉只有一張註冊照,模型要怎麼「認人」?Siamese 網路與度量學習——不學分類,學「像不像」。