L3.2 16 分鐘閱讀 2026年9月

L3.2 從一百個學生裡挑最好的,他下次會考得更好嗎?

本篇重用M5.0保險為什麼存在:Convex Function 與 Jensen 不等式

起點:從一百個學生裡挑第一名

一百個學生考同一份試卷。你挑出考最高分的那一個,派他去比賽。

他在比賽時會考得比這次好,還是比這次差?

如果一百個學生的實力其實完全一樣,答案很清楚:這次的第一名純粹是那天運氣最好的那個,而運氣不會重現,所以他在比賽時會退回平均。你挑出來的那個「最好」的分數,是實力加上一次特別好的運氣。

麻煩的是,真實情況介於兩者之間:學生有實力差距,也有運氣成分。挑出來的那個第一名,同時是「實力比較好」與「運氣比較好」的混合,而你沒辦法從那張成績單上分開這兩件事。

這正是超參數搜尋結束時的處境。同樣試一百次:排成格子,還是隨便丟? 教你怎麼搜,搜完你有 mm 組設定與 mm 個驗證分數,挑了最低的那個。那個分數可以拿來報告嗎?

挑出來的那一個,分數裡有多少是實力、多少是運氣?
這兩件事分得開嗎?
如果分不開,那個數字還能報告嗎?

課堂提問Q1

把「挑最好的」翻成數學。為什麼「挑出來的那個的分數」是有偏的,而且偏的方向固定?如果候選之間的真實品質完全相同,那個偏差會是多少——它從哪裡來?

先想一想,再展開看整理後的答案

寫下來。 候選 f1,,fmf_1,\dots,f_m,每個的真實風險是 R(fj)R(f_j),驗證分數是 R^val(fj)\widehat R_{\text{val}}(f_j)。每一個分數各自是無偏的:E[R^val(fj)]=R(fj)\mathbb E[\widehat R_{\text{val}}(f_j)]=R(f_j)

我們挑 ȷ^=argminjR^val(fj)\hat\jmath=\arg\min_j \widehat R_{\text{val}}(f_j),然後想用 R^val(fȷ^)\widehat R_{\text{val}}(f_{\hat\jmath}) 當成績。

取最小值不是無偏的。 min\min 是一個凹函數,所以由 Jensen(保險為什麼存在:Convex Function 與 Jensen 不等式

E[minjR^val(fj)]  minj E[R^val(fj)]=minjR(fj).\mathbb E\Big[\min_j \widehat R_{\text{val}}(f_j)\Big]\ \le\ \min_j\ \mathbb E\big[\widehat R_{\text{val}}(f_j)\big]=\min_j R(f_j).

不等號的方向是固定的——回報的數字在期望上不高於候選中最好的真實風險。這不是「有時偏高有時偏低」,是一個系統性的往下漂。

候選品質完全相同時,偏差是純粹的選擇偏差。R(fj)RR(f_j)\equiv R,每個分數是 R+ξjR+\xi_jξj\xi_j 是均值為零、標準差 σ\sigma 的估計誤差。那麼

E[R^val(fȷ^)]=R+E[minjξj]Rσ2logm.\mathbb E\big[\widehat R_{\text{val}}(f_{\hat\jmath})\big]=R+\mathbb E\big[\min_j \xi_j\big]\approx R-\sigma\sqrt{2\log m}.

(最後一步是 mm 個獨立同分布的最小值的標準漸近式。)兩件事要記住:偏差隨 logm\sqrt{\log m} 成長(所以候選從 5 個變 400 個,偏差只成長約兩倍,不是八十倍),而且它正比於 σ\sigma——驗證集越小、估計越抖,同樣的 mm 偏得越多。

而這時 R(fȷ^)=RR(f_{\hat\jmath})=R:你挑出來的那個,真實品質和其他所有候選一模一樣。回報的數字變好看了,拿到的模型完全沒有變好。 下一節把這個極端情形做成一個實驗。

把「實力」拿掉,只留「運氣」

要把選擇偏差量乾淨,最好的辦法是讓候選之間沒有實力差距。這樣任何看起來的「贏」都只能來自運氣,偏差就被孤立出來了。

做法:mm 個候選都是同一個 degree 的多項式擬合,差別只在它們各自看到一份不同的訓練資料。它們在期望上品質相同(同一個演算法、同樣的樣本數),但實際的分數會不同。所有候選共用同一份驗證集(nval=30n_{\text{val}}=30),挑最低的那一個。重複 300 次取中位數:

    m  n_val   勝出者的驗證分數   勝出者的真實風險   全體平均真實風險    樂觀量
    1     30            0.0256            0.0261            0.0261    0.0004
    5     30            0.0225            0.0251            0.0273    0.0025
   20     30            0.0206            0.0251            0.0277    0.0046
  100     30            0.0187            0.0257            0.0280    0.0070
  400     30            0.0185            0.0260            0.0283    0.0075

三欄一起讀才看得出重點。

第一欄(回報的數字)一路變好0.02560.01850.0256\to0.0185mm 從 1 到 400 「改善」了 28%28\%

第二欄(勝出者真正的風險)紋風不動0.02610.02610.02510.02510.02510.02510.02570.02570.02600.0260——在抖動範圍內完全平坦。

你比較得越多,回報的數字越好看,而你拿到的模型沒有變好。

m=1m=1 那一列是對照組:不選擇時樂觀量只有 0.00040.0004,實質為零。這確認了偏差確實來自「選」這個動作,不是實驗設計的某個瑕疵。

樂觀量的成長符合 logm\sqrt{\log m}mm 從 5 到 400 是 80 倍,log400/log51.9\sqrt{\log 400/\log 5}\approx1.9,而實測的樂觀量從 0.00250.00250.00750.0075 是 3 倍——同一個數量級(實測略高,因為候選之間有相關,不完全獨立)。

第三欄還有一個附帶的觀察:全體平均的真實風險隨 mm 略微上升0.02610.02830.0261\to0.0283)。那不是選擇造成的,是這個實驗裡候選數變多時我用了更多不同的訓練子集,抽到較差子集的機會跟著變多——一個提醒:讀這種表時要確認每一欄各自在測什麼。

修法:把「選擇」也包進流程裡

偏差的來源是「用同一份資料做選擇又做評估」。修法因此只有一個形狀:找一份沒有參與選擇的資料來評估。

最簡單的版本是三份切分老師用考古題教學又出題:為什麼要切資料 已經講過):訓練選權重、驗證選超參數、測試只讀一次。這在資料夠多時就夠了。

資料不夠時要用巢狀驗證(nested cross-validation)。 關鍵在於認清一件事:

「挑超參數」是訓練流程的一部分,不是訓練之前的準備工作。

一旦這樣看,做法就自動出來了——把整個「訓練 + 挑超參數」當成一個演算法 A\mathcal A,然後對 A\mathcal A 做交叉驗證:

外層 K 折:
    把第 k 折留出來當「測試折」
    在剩下的 K−1 折裡:
        內層 CV:挑超參數(這一步完全看不到測試折)
        用選出的超參數在那 K−1 折上重訓
    在測試折上評估   ← 這個分數沒有參與過任何選擇
把 K 個外層分數平均

最常見的錯誤是把「挑超參數」放在外層迴圈外面——先用全部資料做一次 CV 挑好超參數,再用同一份資料做 CV 報告分數。那樣做時,超參數已經看過所有資料,整個外層 CV 都被污染了(這和 老師用考古題教學又出題:為什麼要切資料 那個 81%81\% 的特徵選擇洩漏是同一個錯誤,只是換一層)。

注意巢狀驗證估的是「流程」,不是你最後交出去的那個模型

巢狀驗證的每一個外層折,都選出了它自己的超參數——KK 個折可能選到 KK 組不同的設定。所以那 KK 個分數的平均估的是:

這整套流程(含挑超參數)用 n(11/K)n(1-1/K) 筆資料跑出來的期望表現。」

估「我最後用全部資料重訓、用某一組特定超參數的那個模型」的表現。這和 只有三十筆資料:交叉驗證 的結論是同一件事往上推一層:CV 估的永遠是演算法,不是某個模型。

實務上的用法因此是:巢狀驗證用來回報一個誠實的數字、或比較兩套流程;最終要交付的模型則用全部資料、照同樣的流程再跑一次。 那個最終模型的表現沒有被直接估計過——這聽起來不安,但它是小資料下唯一誠實的做法。

另外一個常被忽略的成本:巢狀驗證要跑 K×K×mK_{\text{外}}\times K_{\text{內}}\times m 次訓練。5×5×505\times5\times50 就是 1250 次。這是它在實務上常被跳過的原因,而跳過的代價就是上面那張表。

回到情境:那個 mm 到底是多少

上面所有的算式都依賴 mm——你實際比較過的候選數。而這是實務上最常被低估的量。

mm 不是你寫在論文裡的那個數字。 它包含:

  • 網格或隨機搜尋裡的每一組設定。
  • 你手動試過又丟掉的每一個想法。
  • 每一次「改一下再看看驗證分數」。
  • 每一個你看過驗證分數之後才決定不做的方向。

一個跑了三個月、每天看幾次驗證分數的專案,mm幾百。代入 σ2logm\sigma\sqrt{2\log m}m=3m=31.5σ1.5\sigmam=300m=3003.4σ3.4\sigma——偏差大約翻倍。而 σ\sigma 是驗證集的抖動,該收資料、換模型,還是多訓幾輪:學習曲線 量過,nval=15n_{\text{val}}=15 時它的 95%95\% 區間寬度是被估量的 143%143\%

兩個實務推論。 一,記錄你試了幾組——那個數字是讀懂自己分數的必要資訊,也是 同一個實驗跑三次結果都不同,該報哪一個? 要一起報告的東西。二,驗證集要夠大:偏差正比於 σ\sigma,而 σ1/nval\sigma\propto1/\sqrt{n_{\text{val}}},所以驗證集加大四倍會讓選擇偏差減半——這通常比減少候選數便宜得多(候選數只以 logm\sqrt{\log m} 進來)。

這一切依賴什麼。 一,2logm\sqrt{2\log m} 那條漸近式假設候選的估計誤差獨立;實務上相鄰的設定高度相關,所以真實的偏差比它小(本篇實測略高於漸近式是因為 mm 小時漸近式本身不準)。二,實驗裡候選品質完全相同是刻意造出來的極端;真實情況下偏差與真實改善混在一起,而分開它們的唯一方法就是一份沒參與選擇的資料。三,測試集也只能用一次——用它比較過三個方案之後,它就變成了一個 m=3m=3 的驗證集。

回到情境:一個五分鐘的自我檢查

上面的實驗可以直接搬成一個對自己流程的健康檢查,而且不需要任何新資料:

把標籤打亂,重跑整個超參數搜尋。 標籤打亂之後所有候選的真實品質都是「亂猜」,所以:

  • 誠實的流程會回報一個接近亂猜的分數。
  • 有選擇偏差的流程會回報一個明顯好於亂猜的分數,而且你搜得越用力,那個數字越好看

這是 老師用考古題教學又出題:為什麼要切資料 那個「把標籤打亂」檢查的第二種用法:那裡用來抓洩漏,這裡用來量選擇偏差。兩者的訊號不同——洩漏會給出一個誇張的分數(實測 81%81\% vs 50%50\%),選擇偏差給出的是一個溫和但穩定的高估。

把兩個檢查一起跑,就能回答「我這個 0.920.92 裡面有多少是真的」。

候選數、驗證集大小、與那條漂掉的線

互動 demo:樂觀量隨 √log m 成長、正比於驗證集的抖動——所以「把驗證集加大」比「少試幾組」有效。

先消化一下

想一想

一個團隊比較了 200 組超參數,回報最好那一組的驗證準確率 94.1%94.1\%。根據本篇的實驗,最準確的敘述是:

想一想

在實驗裡,mm 從 1 加到 400 時勝出者的真實風險0.02610.02510.02510.02570.02600.0261\to0.0251\to0.0251\to0.0257\to0.0260。這一欄說明:

想一想

下列哪一種做法會造成本篇所說的偏差?

想一想

下列哪一句不對

參考文獻

  1. Cawley, G., Talbot, N. On Over-fitting in Model Selection and Subsequent Selection Bias in Performance Evaluation. JMLR 2010.(模型選擇造成的偏差有多大、巢狀驗證的正確做法,以及文獻裡常見的錯誤形式。)
  2. Varma, S., Simon, R. Bias in error estimation when using cross-validation for model selection. BMC Bioinformatics 2006.(在小樣本的生醫資料上量出這個偏差,並示範巢狀 CV 把它修掉。)
  3. Dwork, C. et al. The reusable holdout. Science 2015.(把「重複使用同一份留出資料」形式化成適應性資料分析的問題,並給出一個有保證的機制。)