「有把握」應該是什麼意思:校準
在這個系列裡,你已經學會把像素變成標籤、誠實地為分類器打分、訓練出能類化的模型,並處理困難的情況。這堂最後的大師課,談的是「只是答對」與「值得信賴」之間的差別。一個上線的分類器回報的不只是標籤,還會附帶一個數字——「貓,0.99」。大師層級的第一個問題是:當模型說 0.99 時,它真的會在 100 次裡答對 99 次嗎?
出乎意料地,答案常常是「不會」。現代深度網路雖然非常準確,卻有系統性的過度自信:當它輸出 99% 時,實際上可能只有約 90% 的時候是對的。這個數字被灌水了。這就是「校準」的問題——讓回報的信心值與真正答對的機率相符。我們可以用很便宜的 溫度縮放 / 校準 來修正它(下一節會談),但首先要看清為什麼校準不良很重要,以及該如何衡量。
只要信心值是用來驅動決策,而不只是裝飾,校準就很重要。在醫療分流中,0.6 與 0.95 的癌症評分,會決定要不要呼叫放射科醫師來看。在自動煞車中,「這團東西是行人」的信心,決定車子要不要停下。在任何「不確定時就交給人類」的系統裡,如果信心值本身就是謊言,交付的門檻就毫無意義。過度自信的模型永遠不會求救——而那正是它最危險的時候。
我們用可靠度圖(reliability diagram)來衡量校準。做法很簡單:把保留集上所有的預測,依信心值排序,丟進一個個區間——例如 0.0–0.1、0.1–0.2、…、0.9–1.0。對每個區間算兩件事:模型宣稱的平均信心,以及它在這些樣本上實際達到的準確率。把準確率(縱軸)對信心(橫軸)畫出來。完美校準就是那條 45 度對角線:在模型說「0.8」的那個區間裡,它真的答對 80%。曲線若低於對角線,就代表過度自信——這正是深度網路常見的毛病。
期望校準誤差:各區間中信心與準確率差距的加權平均。
這條公式就是期望校準誤差(ECE),它把可靠度圖濃縮成單一數字:曲線與對角線之間的平均垂直差距。逐符號來看:我們把總共 N 個預測分進 M 個信心區間;B_m 是落在第 m 個區間的預測集合,|B_m| 是它的數量,所以 |B_m|/N 是該區間佔全部預測的比例(它的權重)。\mathrm{acc}(B_m) 是該區間的真實準確率,\mathrm{conf}(B_m) 是模型在那裡宣稱的平均信心,|\mathrm{acc}-\mathrm{conf}| 則是現實與宣稱的偏離程度。我們以各區間的大小為各差距加權,再加總起來。舉個小例子:假設某區間裝了 1000 個預測中的 100 個,模型宣稱平均信心 0.95,卻只答對 85%——這個區間對 ECE 的貢獻是 (100/1000)\times|0.85-0.95| = 0.1\times0.10 = 0.01。把所有區間的這類項加總;0 代表完美校準,數值越大代表信心越脫離現實。
溫度縮放:一個旋鈕的修正
好消息是,過度自信通常用一個數字就能修好。溫度縮放 / 校準 是最簡單、而且實證上最有效的*事後(post-hoc)*校準法——「事後」是指你在訓練完之後才套用它,不必重新訓練網路。你把模型所有的 logits(softmax 之前的原始分數)都除以一個學到的純量 T,再取 softmax;而 T 的選法,是讓驗證集上的校準誤差最小。一個旋鈕,調一次就好。
溫度縮放後的 softmax:每個 logit 在歸一化前都除以同一個 T。
逐符號來讀:z_i 是類別 i 的 logit(網路在 softmax 之前的原始分數),而 T>0 是所有類別共用的單一溫度。把每個 logit 都除以同一個 T,會均勻地縮小或拉大它們之間的差距。當 T>1 時我們「冷卻」分布:差距縮小,所以 softmax 之後機率更平坦、最高信心下降——模型變得更謙虛。當 T<1 時我們「加熱」/銳化它:差距變大、分布更尖、信心上升。在恰好 T=1 時,就回到一般的 softmax。分母 \sum_j \exp(z_j/T) 一如往常,只是讓所有 p_i 加起來等於 1 的歸一化項。
來實際走一遍。假設三個 logits 是 z=[3,\,1,\,0]。在 T=1 時 softmax 大約是 [0.84,\,0.11,\,0.04]——模型對最高類別宣稱 84%。現在設 T=2,縮放後的 logits 變成 [1.5,\,0.5,\,0],softmax 變成大約 [0.63,\,0.23,\,0.14]——最高類別降到 63%。機率質量散開了,模型變得更謹慎。如果驗證集顯示這個模型在宣稱 84% 時其實只答對約 65%,那麼 T=2 就讓它的信心變誠實了。
import numpy as np
def softmax_T(logits, T):
z = np.asarray(logits) / T # divide every logit by the temperature
z = z - z.max() # numerical stability (does not change result)
e = np.exp(z)
return e / e.sum()
z = [3.0, 1.0, 0.0]
print(softmax_T(z, T=1)) # ~ [0.844, 0.114, 0.042] -> 84% on class 0
print(softmax_T(z, T=2)) # ~ [0.629, 0.231, 0.140] -> 63% on class 0
# argmax is class 0 in BOTH cases -> accuracy is untouched, only confidence changed
print(np.argmax(softmax_T(z, 1)), np.argmax(softmax_T(z, 2))) # 0 0
# Fitting T: scan a small grid (or use gradient descent) to minimise
# validation NLL / ECE while keeping the model's weights frozen.測試時增強:和自己投票
這是榨出額外準確率的第一個技巧。測試時增強(TTA)妙在簡單:在推論時,不要只把單一張原圖丟進模型跑一次,而是為同一張影像造出好幾個增強後的視角——水平翻轉、幾個裁切、也許再加一點點縮放——把每一個都送進網路,然後在取 argmax 之前把它們的預測機率向量平均起來。
TTA:把 A 個增強視角的 softmax 輸出平均,再取 argmax。
逐符號來看:x 是測試影像;t_a 是第 a 個增強(例如翻轉或某個特定裁切),所以 t_a(x) 是一個變換後的視角;f(\cdot) 是網路的 softmax 輸出,也就是各類別上的完整機率向量;A 是你用了幾個視角。我們把 A 個機率向量加起來、除以 A,得到平均 \bar{p},再對它取 argmax。直覺是把「群眾智慧」用在同一個模型上:每個視角都是帶點雜訊的意見,不同視角的雜訊指向不同方向,平均會抵消掉大部分,而真正的訊號——這究竟是哪一類——則保留下來。視角越多,估計的變異越小,但邊際效益遞減:從 1 個增到 4 個幫助很大,從 4 個增到 16 個就小很多。
一張原始影像被變換成數個版本——水平翻轉、兩個裁切、一個色彩抖動——並排呈現以說明增強。
來走一個 3 視角的例子。假設對一張處於邊界的影像,三個視角在類別 A、B、C 上產生 softmax 向量 [0.55,\,0.35,\,0.10]、[0.45,\,0.45,\,0.10]、[0.62,\,0.28,\,0.10]。中間那個視角是 A 與 B 的接近平手,單獨來看可能會翻成錯誤標籤。平均後得到 [(0.55{+}0.45{+}0.62)/3,\,(0.35{+}0.45{+}0.28)/3,\,(0.10{+}0.10{+}0.10)/3]=[0.54,\,0.36,\,0.10]——對 A 是更清楚、更穩定的一票。注意這正是你在第 3 篇看過的同一族變換,卻用在相反的目的:在那裡,訓練時的隨機增強教模型保持不變性;在這裡,推論時的確定性增強則收割那份不變性,以穩定每個預測,並把 top-k 準確率 往上推一點。
模型集成:三個臭皮匠勝過一個諸葛亮
TTA 是對同一個模型的多個視角取平均;分類的模型集成 則是對好幾個不同的模型取平均。你獨立訓練 M 個分類器,讓它們全都跑這張測試影像,然後在 argmax 之前把它們的預測機率平均。集成是最可靠、能拿到最後那零點幾個百分點的方法——這也是為什麼各種競賽排行榜幾乎都被集成模型佔據。
為什麼有效?回想先前機器學習系列裡的偏差–變異(bias–variance)觀點。單一訓練好的模型會犯兩種誤差:偏差部分(模型族本身內建的系統性盲點)與變異部分(來自這一次特定訓練的抖動——隨機種子、資料順序、增強的抽樣)。如果你訓練出好幾個犯下互不相關變異誤差的模型,那麼當你平均它們的預測時,這些誤差指向隨機方向、大致互相抵消;而共有的訊號——它們都同意、通常就是正確答案的那部分——則疊加起來。你無法把偏差平均掉,但你可以把變異壓垮。
一張分解圖,顯示總誤差為偏差分量與變異分量之和,平均會降低變異那一部分。
集成預測:M 個模型機率向量的簡單平均。
逐符號來看:p^{(m)} 是模型 m 的機率向量,M 是模型數量,p_{\text{ens}} 是它們的簡單平均,我們對它取最後的 argmax。一個 3 模型、類別 A、B、C 的例子:模型 1 說 [0.6,\,0.3,\,0.1],模型 2 說 [0.5,\,0.2,\,0.3],模型 3 說 [0.8,\,0.1,\,0.1]。平均是 [0.63,\,0.20,\,0.17]——三者本來都偏好 A,集成既正確又更穩。增益背後的數學:若每個模型的誤差大致獨立、變異為 \sigma^2,則 M 個的平均其變異會縮向 \sigma^2/M。M=3 時就是抖動的三分之一——但只有在誤差互相獨立時才成立。如果你把同一個模型複製三次,誤差完全相關,你什麼也得不到;這正是為什麼多樣性(不同種子、架構、增強配方、資料子集)才能讓增益成真。額外的好處是,集成通常也校準得更好,因為平均會緩和任一模型的過度自信,並傾向把 top-k 準確率 往上推。
線性探測:衡量骨幹網路學到了什麼
暫時從追逐準確率退一步,問一個更深的問題:一個網路學到的特徵有多好,跟它最後那層分類器分開來看?線性探測 回答了這件事。你拿一個預訓練好的特徵抽取器——也就是骨幹(backbone)——把它所有的權重凍結,讓它不能再學,然後只在它產生的特徵之上訓練一個線性分類器。其他什麼都不動。
在凍結的骨幹特徵之上,加一個線性頭。
逐符號來看:x 是輸入影像;g(\cdot) 是凍結的骨幹,把影像映成特徵向量 h=g(x)(例如 2048 個數字,用來摘要這張影像);W 與 b 是線性頭的權重矩陣與偏置——而它們是唯一可訓練的參數;z 是得到的 logits,一如往常送進 softmax。因為 g 被凍結,訓練優化的只有「從特徵到類別」的這個線性映射。這正是重點:如果這麼小的一個線性層就能把類別分得很好,那麼特徵 h 一定早就帶有相關的結構——骨幹做完了苦工,一個超平面就足以收尾。
特徵空間中兩類別的點被一條直線分開,說明線性決策邊界。
線性探測有兩個角色。第一,它是衡量表徵品質的乾淨診斷工具,這正是這個領域評估自監督與基礎模型的方式:一個完全沒有標籤訓練出來的模型,仍可藉由凍結它、再看線性探測能否分開類別來評判——探測準確率高,代表無監督的預訓練學到了真正有用的結構。第二,它是一種便宜又快速、能轉移到新標籤集的遷移方法。想像一個在 ImageNet 1000 類上預訓練的骨幹,你想把它改用於 10 個醫療類別:凍結它,接上一個 10\times2048 的頭(W)加 10 個偏置——約 20{,}500 個可訓練參數,相對於骨幹裡的數百萬個——在一個小資料集上幾分鐘就能訓練好。幾何圖像正是你整個系列建立起來的 影像分類 決策邊界:線性頭只是在凍結的特徵空間裡畫一個超平面,而「好特徵」就是指各類別早已乾淨地分落在這種平面的兩側。把它對照完整微調,後者連 g 也解凍:那有多得多的容量、通常能達到更高的準確率,但需要多很多的資料與計算,還有過度擬合那個小的新資料集、或遺忘骨幹原本所知的風險。先做線性探測,非不得已才微調。
知道自己所不知道的:開放集與分布外偵測
現在我們正面迎戰自第 1 篇以來,內建在一切之中的封閉世界假設。你的分類器是在 K 個已知類別上訓練的,而 softmax 的設計就是要在這 K 個類別上輸出機率——它無法回傳別的東西。但在上線後,模型會遇到從未見過的類別:不在資料集裡的物種、一張壞掉的感測器影格、一張完全離題的照片。模型還是得回答,於是它硬把輸入塞進它的 K 個格子之一——而且因為過度自信,往往還說得很大聲。一個數字分類器被丟一張貓的照片,會很有把握地宣告它是「7」。
解方是給模型一個說「以上皆非」的選項。這就是 開放集 / 分布外分類:在給出最佳猜測之外,系統必須能在輸入看起來不像任何它認識的東西時棄答(abstain)。最簡單的基準是最大 softmax 機率偵測器:只要最高信心低於某個門檻,就拒答(標記為未知)。而此處前面幾節就派上用場了——這只有在信心值值得信賴時才有效,所以 溫度縮放 / 校準 是前提,不是奢侈品。若信心校準不良、灌了水,連真正的分布外輸入都會輕鬆越過門檻。
棄答規則:只有當最高類別的信心越過門檻時,才認定它。
逐符號來看:p_i 是類別 i 的(理想上已校準的)機率;\max_i p_i 是模型的最高信心——它對自己單一最佳猜測有多確定;\tau(tau)是你在驗證資料上調校的拒答門檻。規則是:若最高信心越過 \tau,就照常輸出 argmax 類別;否則輸出「未知」,交給系統處理(交給人類、再要一次感測器讀數、退回安全的預設值)。一個例子:在貓、狗、鳥上訓練,設 \tau=0.7。一張清楚的貓照片給出校準後的 p=[0.93,\,0.05,\,0.02],所以 \max=0.93\ge0.7 → 預測「貓」。現在丟一張車:有了校準的信心值,模型會把機率質量攤開,例如 p=[0.41,\,0.33,\,0.26],所以 \max=0.41<0.7 →「未知」,正確地拒絕把車硬塞進某個已知的動物類別。
上線檢查清單
讓我們把整個系列串起來。上線一個分類器,不是你刷出最高驗證數字的那一刻——而是你能誠實回答一份簡短問題清單的那一刻。底下每一項都是你上線前該問的問題,每一項也都指回這個系列裡的某一篇。把它當成大師級分類器的起飛前檢查清單。
- 我的指標符合真正的目標嗎?重讀第 2、4 篇:在類別不平衡下,單看準確率會騙人——改選精確率/召回率、各類別指標,或反映你應用中真正在乎之事的成本加權分數。
- 我有用一套正則化的配方訓練嗎?來自第 3 篇:增強、權重衰減、提早停止、標籤平滑 / mixup——讓模型能類化,並且一開始就沒那麼過度自信。
- 我的信心值誠實嗎?在驗證集上擬合 溫度縮放,並檢查可靠度圖 / ECE。只要下游有任何環節會讀取信心值,這一步就沒得商量。
- 我的準確率對延遲如何取捨?判斷 測試時增強 與/或 模型集成 是否符合你的延遲與記憶體預算——若不符合,就改用快照集成或權重平均。
- 模型能說「我不知道」嗎?為 開放集 / 分布外 輸入設一個棄答門檻 \tau,並用保留的未知樣本驗證,讓系統在面對意外時交付出去,而不是有把握地亂猜。
- 我要怎麼察覺世界在改變?持續監測輸入的統計量與線上準確率,留意分布隨時間的漂移,並排定重新訓練——一個凍結在過去的模型,會隨著現實逐漸偏離它的訓練資料而悄悄腐壞。
注意貫穿這些檢查的主軸。第 1 至 4 篇讓模型答得對;這一篇讓它對自己的不確定性誠實(校準)、榨出每一分安全的準確率(TTA 與 集成),並對未知保持謙虛(開放集棄答與漂移監測)。對、誠實、謙虛——這三個性質,正是把一個排行榜模型,和一個你真的敢託付它在現實世界做決策的模型,分隔開來的東西。