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

資料集與成績單:我們如何判斷分類器好不好

準確率是個數字——但是哪個數字、在哪份資料上算的?學會把吹噓變成科學的基準與指標。

為什麼這個領域需要共用基準

在上一篇指南中,你建立了一個把像素轉成標籤的分類器。接下來自然會問:它到底好不好?你可以用一個數字回答——「94% 正確!」——但這個數字本身毫無意義。94% 是在哪些影像上算出來的?用什麼方式評分?如果每個研究團隊都用自己私藏的一堆照片測試,那等於每個團隊都在自己出考卷、自己改考卷、自己宣布成績。兩個分數根本無法比較,也沒人能判斷一個新方法是真的比較好,還是只是拿到比較簡單的考卷。

解法和學校採用的一樣:一份大家都要考的標準化測驗。如果上千名學生在相同規則下考完全相同的卷子,那麼分數較高就真的代表實力較強。在電腦視覺裡,對應的東西就是一個共用的 基準影像資料集——一批固定的影像,整個社群都同意在它上面做評估。當大家都在同一份資料上回報數字,排行榜才會變成真正的排名,而不是一堆無法比較的吹噓。

這套紀律帶來兩個好處。第一是可重現性:因為資料與分割都凍結了,任何人都能重跑你的評估、得到相同結果,於是宣稱可以被「驗證」而不是只能「相信」。第二是公平的競技場:當測驗保持不變,要提高分數的唯一辦法就是做出真正更好的模型。正是這一點,把視覺研究從一堆軼事變成可量測的科學——而且,如同我們接下來會看到的,它也讓某一個資料集重新塑造了整個領域。

經典巡禮:MNIST、CIFAR、ImageNet

基準有難度等級,就像電玩一樣。沿著這些經典資料集的階梯往上爬,能讓你對整個領域建立一張心智地圖,所以我們從最簡單爬到最難。每一階都在權衡同樣兩件事:你能多快迭代,以及任務有多貼近真實。

MNIST 是視覺界的「Hello World」:七萬張手寫數字(0–9),每張都是小小的 28×28 灰階影像。只有 10 個類別、幾乎沒有雜亂背景,你在筆電上幾秒鐘就能訓練出一個像樣的模型。這讓 MNIST 非常適合用來檢查程式碼有沒有寫對——但它也簡單到好模型都能超過 99%,幾乎沒有空間能分辨「很棒的方法」和「只是還行的方法」。這個資料集已經飽和了。

CIFAR-10 與 CIFAR-100 把門檻提高了。它們是 32×32 的彩色照片,拍的是真實事物——CIFAR-10 有 10 個日常類別(飛機、貓、船……),CIFAR-100 則把同樣的概念細分成 100 類。影像仍然很小、訓練很快,但現在有了真實世界的變化:光線、姿態、背景。CIFAR 是經典的中型遊樂場,你一天能跑很多實驗,卻仍能學到有意義的東西。

階梯頂端是 ImageNet:超過一百萬張全解析度照片,分布在 1000 個細分類別中——不只是「狗」,而是數十種特定品種;不只是「蛇」,而是許多物種。這樣的規模與難度,使它成為影像分類的黃金標準基準。類別取自真實概念,照片雜亂而自然,而 1000 個類別也讓任務真正變得困難。本篇指南中的每一個指標,我們都會以 ImageNet 作為貫穿全文的範例。

ImageNet 也是一段歷史。多年來,大家在它上面的進步都緩慢而漸進。接著在 2012 年,一個深度卷積網路(「AlexNet」)一舉大幅降低了錯誤率——這個結果遠遠領先其他所有方法,說服了整個領域:深度學習就是未來的方向。現代電腦視覺幾乎整個時代都從那一刻算起,而它之所以能發生在一個基準上,正是因為基準讓這個躍進變得可量測、無法否認。

訓練、驗證、測試:不可違反的鐵則

每個正經的基準都會把影像切成三個互不重疊的池子,而圍繞它們的紀律,是整個領域裡最重要的一個習慣。訓練集是模型用來學習的對象。驗證集是你用來做選擇的——選哪個架構、用多少資料增強、學習率設多少。測試集則只在最後碰一次,用來估計模型在它「真正從沒見過」的資料上會表現得多好。

考試這個比喻完美說明了為什麼這樣有效。訓練集是你的課本和作業——你愛念多認真都行。驗證集是你拿來決定要複習什麼的模擬考。測試集則是期末考:你絕對不能拿它來念書,因為整件事的重點是要預測你在「沒看過的題目」上會考得如何。如果你早就看過期末考卷,你的分數對於真正的理解一點都說明不了。

  1. 在訓練集上訓練:調整模型的參數,直到它學會這個任務。
  2. 在驗證集上調參:嘗試不同的架構、資料增強與學習率;留下在這裡分數最好的設定。想重複幾次都可以。
  3. 在測試集上只測一次:把所有選擇都凍結後,讓模型只跑一次,並把這個數字當作你真正的泛化估計回報出來。
隨著訓練持續,訓練誤差不斷下降,但驗證誤差最終會回升——這個落差就是過度擬合。

兩條對訓練時間作圖的曲線:訓練誤差單調下降,驗證誤差先降後升,逐漸擴大的落差標示為過度擬合。

你在先前的機器學習課程裡已經見過過度擬合:模型可以把訓練資料背起來——在訓練集上幾乎滿分,卻在任何新東西上表現很差。驗證集就是你用來抓住這件事的工具。如圖所示,當訓練誤差持續下降、但驗證誤差開始攀升時,模型就不再學習通用的規律,而是開始死背了。那個落差就是要你停下、簡化或加上正則化的早期警訊。

還有一個更隱微的陷阱。你也可能對測試集過度擬合——不是靠在它上面訓練,而是靠一次又一次地偷看它。每當你調整模型、看一眼測試分數,再根據看到的結果繼續調,你其實就是在悄悄用測試集做決策。偷看幾百次之後,你等於已經對它擬合了,這種習慣叫做基準爬山(benchmark hill-climbing)。公開排行榜讓情況更糟:當上千個團隊都反覆在同一份隱藏測試集上評估,整個社群會慢慢「洩漏」答案,使回報的數字漂得比真正的泛化還高。這就是為什麼守護測試集、把它「只用一次」當成神聖不可侵犯,是不可違反的鐵則。

Top-1 與 Top-5 準確率

先從最簡單的指標開始:純準確率就是模型完全答對的測試影像所佔的比例。回想第 1 篇指南:模型會為每個類別輸出一個機率,而決策規則(argmax 取最大值)會挑出機率最高的那一個類別。當這個最高的猜測等於真實標籤時,這張影像就算答對。因此一般的準確率又叫做 top-1 準確率:top-1 = argmax。它是最嚴格而公平的測驗——唯一的那個猜測必須正確。

我們可以把它推廣。與其只看單一最佳猜測,top-k 準確率問的是:真實類別有沒有落在模型機率最高的 k 個猜測之中?你讀出模型最有信心的 k 個類別,只要正確答案出現在那份短名單裡,就算這張影像答對。定義如下。

\text{Top-}k\text{ accuracy} = \frac{1}{N}\sum_{n=1}^{N} \mathbf{1}\!\left[\, y_n \in \mathrm{TopK}(p_n) \,\right]

真實類別出現在模型機率最高的 k 個預測中的影像所佔比例。

我們逐一拆解每個符號。N 是測試影像的數量,n 是它們的編號(第 1 張、第 2 張……一直到第 N 張)。對第 n 張影像,模型會產生一個機率向量 p_n,每個類別對應一個數值;y_n 是這張影像的真實類別。TopK(p_n) 是 p_n 中數值最高的 k 個類別所構成的集合——也就是模型最好的 k 個猜測。方括號 1[ … ] 是指示函數:當括號內的敘述為真(真實類別在 top-k 集合裡)時等於 1,為假時等於 0。把這些 1 和 0 對全部 N 張影像加總、再除以 N,就得到落在 top-k 內的比例——也就是準確率。注意當 k = 1 時,TopK 就只剩下那個唯一的 argmax 類別,於是公式退回成一般的 top-1 準確率。

為什麼 top-5 會在 ImageNet 上成為標準?因為面對 1000 個細分類別,單一猜測是個殘酷嚴苛的門檻。其中許多類別本來就模稜兩可或彼此重疊:有數十種連人類都會搞混的狗品種,而一張照片裡也可能合理地包含好幾個物體(一個人騎著馬、一張堆滿文具的書桌)。要求模型那唯一的最高猜測,必須對上那唯一被選定的標籤,可能會懲罰到其實很合理的答案。top-5 問的是比較寬鬆、往往也更有意義的問題:正確答案至少還在競爭之列嗎?

一個具體例子能讓差別變得鮮明。假設某張照片的真實類別是「西伯利亞哈士奇」。模型排序後的猜測是:第 1 名「阿拉斯加雪橇犬」(0.30)、第 2 名「愛斯基摩犬」(0.25)、第 3 名「西伯利亞哈士奇」(0.20)、第 4 名「狼」(0.15)、第 5 名「薩摩耶」(0.10)。在 top-1 之下,預測是「雪橇犬」,答錯了——這張影像得 0 分。但真實類別排在第 3,穩穩落在前 5 名之內,所以在 top-5 之下它得 1 分。同一個模型、同一張照片:在一個指標下嚴苛,在另一個指標下卻是正確的。兩個數字都回報,才能說出更完整的故事。

import numpy as np

def top_k_accuracy(probs, labels, k=5):
    # probs:  array of shape (N, C) - predicted probability of each class
    # labels: array of shape (N,)   - the true class index for each image
    # returns the fraction of images whose true class is among the top-k guesses
    correct = 0
    for p, y in zip(probs, labels):
        topk = np.argsort(p)[-k:]   # indices of the k largest probabilities
        if y in topk:               # is the true class in the short list?
            correct += 1
    return correct / len(labels)

# k=1 reproduces ordinary accuracy: the single argmax guess must equal the label.
# k=5 is the classic ImageNet metric.
幾行程式碼就能算出 top-k 準確率;k=1 即為純(top-1)準確率。

讀懂混淆矩陣

單一個準確率數字告訴你模型「有多好」,卻沒告訴你它「擅長什麼、不擅長什麼」。要做出這種診斷,我們會用混淆矩陣。概念很簡單:畫一個方格表,列(橫)是真實類別,行(直)是預測類別。每個格子計算「某個真實類別的影像被預測成某個類別」發生了幾次。把所有格子加起來,就是測試影像的總數。

對角線上的格子——真實類別等於預測類別的地方——就是預測正確的部分。所以一個完美的模型會把所有計數都放在對角線上,其他地方全是零。每一個非對角線的格子都是一個錯誤,而且關鍵在於它精確告訴你是哪一種錯誤:位於「貓」這一列、「猞猁」這一行的計數,代表「被叫成猞猁的貓」。一個明亮的非對角線格子,揭露了一對被模型系統性搞混的類別——而這正是你需要的資訊,告訴你該從哪裡改進。

混淆矩陣:明亮的對角線代表大多答對;明亮的非對角線格子則標示出系統性的類別互換。

一個方形熱圖網格,列為真實類別、行為預測類別;對角線顏色很強,另有幾個被標亮的非對角線格子。

Predicted ->     cat    dog   lynx     (row = true class)
  cat            7      0      3     -> 10 cat images
  dog            0     10      0     -> 10 dog images
  lynx           4      0      6     -> 10 lynx images

# Diagonal (correct): 7 + 10 + 6 = 23  ->  accuracy = 23/30 = 0.77
# Dogs are perfect. The bright off-diagonal cells (cat<->lynx)
# show the model keeps swapping cats and lynxes: 3 cats called lynx,
# 4 lynxes called cat.
30 張測試影像(每類 10 張)的 3x3 混淆矩陣。

把這個小例子當成醫生在看 X 光片來讀。整體準確率是 23/30 ≈ 77%——還行,但單看它沒什麼資訊。矩陣才說出真正的故事:「狗」這一列是完美的(對角線上 10,其餘為零),所以狗不是問題。損害集中在貓—猞猁這一塊:3 隻貓被叫成猞猁、4 隻猞猁被叫成貓。這很合理——貓和猞猁長得像——而且它指向一個具體的修法:取得更多或更好的「貓 vs 猞猁」範例,或是能把兩者分開的特徵。這就是從「整體有多好」通往「擅長什麼、不擅長什麼」的橋樑,是同一個影像分類任務,只是透過診斷的鏡頭來看。

不只一個數字:精確率、召回率與逐類別檢視

準確率有一個危險的盲點:類別不平衡。想像一個醫學篩檢資料集,有 1000 張影像,其中真正生病的只有 50 人,健康的有 950 人。一個懶惰的模型完全不看輸入、永遠喊「健康!」,就能答對 1000 中的 950——95% 準確率——卻一個生病的病人都沒抓到。數字看起來很亮眼,模型卻毫無用處。當某一類佔了絕大多數,連 top-k 準確率都可能美化一個對你真正在乎的案例一無所學的模型。

要看穿這一點,我們把「正確」拆成兩個互補的問題。精確率(precision)問:在我標記為生病的所有人當中,有多少真的生病?召回率(recall)問:在所有真正生病的人當中,我抓到了幾個?假設一個較好的模型標記了 60 人為生病;其中 40 人真的生病(真陽性)、20 人是假警報(假陽性),同時漏掉了 10 個生病的人(假陰性)。那麼精確率 = 40/60 ≈ 0.67(三分之二的警報是真的),召回率 = 40/50 = 0.80(我們抓到了五分之四的病患)。兩個數字、兩件非常不同的事——而且都不是那個誤導人的 95%。

精確率算的是被標記的項目有多少是對的;召回率算的是真正存在的項目被抓到了多少。移動閾值會在兩者之間做取捨。

一張對比精確率與召回率的示意圖,涵蓋「被預測為陽性」與「實際為陽性」兩個集合,並有一個可移動的決策閾值。

精確率與召回率彼此拉扯,而兩者之間的旋鈕就是決策閾值。回想第 1 篇指南:模型會輸出一個機率;當這個機率超過某個閾值時,我們才宣告「生病」。把閾值調低,你會標記更多病人:召回率上升(漏掉的病人變少),但精確率下降(假警報變多)。把閾值調高則相反:精確率上升,召回率下降。天下沒有白吃的午餐——你要根據你的應用裡哪種錯誤代價較高,來決定坐在這個取捨的哪個位置。在癌症篩檢中,漏掉一個病例是災難性的,所以你偏好召回率;對垃圾郵件過濾器來說,一封被誤刪的郵件只是惱人,所以你偏好精確率。

同樣的修法可以擴展到很多類別。與其只算一個整體準確率,不如分別為每個類別算準確率(或精確率與召回率)——也就是逐類別檢視——再把這些逐類別分數平均,得到平衡準確率(balanced accuracy)。在不平衡之下,這個差別是決定性的:一般準確率把每張影像看得一樣重,所以大類別會主宰結果;而平衡準確率把每個類別看得一樣重,於是一個忽略稀有類別的模型再也藏不住了。那個「什麼都不做就 95%」的模型,在平衡準確率下只會拿到難堪的 50%(在健康類完美、在生病類掛零)。