L1.3 只有三十筆資料:交叉驗證
本篇重用M1.0兩台體重計:Gaussian 的線性組合·M1.1從鞋子猜身高:Conditional Expectation
起點:三十筆資料,怎麼切都不對
上一篇的規則很清楚:要估泛化誤差,就得留一部分資料不看。
現在把它用在共用 toy 上:只有 30 筆。留 10 筆當驗證集,訓練就只剩 20 筆——模型變差了,而你估的是這個變差版本的表現,不是你最後要交出去的那個。更麻煩的是,那 10 筆驗證分數本身抖得厲害:換一個切法,選出來的模型就換一個。
這是小資料的兩難:留太多,模型變差;留太少,估計不準。
有一個出路,而且除了計算量之外不必多付什麼:每一筆資料輪流當一次測試,其餘時間當訓練。 把 30 筆分成 5 組,每次留一組出來、用另外 24 筆訓練,做五次,把五個誤差平均。每一筆都被評估過恰好一次,而每次訓練都用了 的資料。
這一篇要回答三個問題:它穩多少(實測十三倍)、 該設多少、以及什麼時候它會說謊(實測:一條純隨機漫步能拿到 的 MSE,而正確的評估是 )。
只有 30 筆資料:切一份出來評估太浪費,
不切又沒有東西可以評估。
有沒有辦法讓每一筆都當過一次測試、也當過訓練?
課堂提問Q1
把交叉驗證翻成數學:-fold 的估計量在估什麼——是「我最後那個模型的風險」嗎?它比單次留出穩,穩在哪個機制?「平均 個數字所以變異數除以 」這個直覺哪裡錯了?
先想一想,再展開看整理後的答案
它在估什麼。 令 是演算法(假設空間+損失+最佳化+超參數), 有 筆。-fold 的估計量是
每一項都是「用 筆訓練出來的模型」在沒看過的 筆上的誤差。所以 估的是演算法在樣本數 下的期望風險,不是你最後用全部 筆訓出來那一個模型的風險。兩者不同,而且 略微悲觀(訓練資料少了一點)—— 越大,這個悲觀越小。
穩在哪。 單次留出只用了 筆去評估,估計的變異數大致 ;交叉驗證讓每一筆都貢獻一次評估,等於用 筆去估同一件事。這是本篇最主要的收益,而且它與「模型變好」無關——它是評估的變異數,不是模型的品質。
除以 為什麼錯。 那個公式要求 個項獨立(兩台體重計:Gaussian 的線性組合:獨立時變異數相加)。但這 個模型的訓練集兩兩重疊 的比例—— 時任兩折的訓練集共用 的資料。它們的誤差高度正相關,所以
第二項不會隨 消失。事實上(參考文獻 2)不存在這個變異數的無偏估計量。
的標準誤不能用那 個數字的標準差除以 算,那會嚴重低估。
一個資源分配問題
把資料看成一筆預算, 是分配比例的旋鈕:
- 小(例如 2):每次訓練只用一半資料,模型明顯比最終模型差 → 估計偏悲觀。但 個模型之間重疊少 → 相依低。計算便宜。
- 大(例如 ,LOO):每次訓練用 筆,幾乎就是最終模型 → 偏誤最小。但任兩個訓練集只差一筆, 個誤差幾乎完全相關 → 相依高。計算貴 倍。
所以 是在「偏誤(訓練資料少了多少)」與「相依(估計彼此多像)」之間權衡,加上計算成本。 或 是常見的折衷,理由是經驗而非定理:訓練資料只少 –,而重疊還沒到 LOO 那麼極端。
還有兩個實務變體值得知道:
- 分層抽樣(stratified):分類任務中讓每一折的類別比例與整體一致。類別不平衡時這不是精緻化,是必需——否則某一折可能一個少數類樣本都沒有。
- 重複交叉驗證:把整個 -fold 用不同的隨機分組重跑幾次再平均。它壓的是「分組方式」帶來的變異,代價是線性的計算量。
CV 在估什麼、抖多少、以及它不能估什麼
估計目標。 記 :演算法用 筆資料訓練後的期望風險。那麼
這是一個關於演算法的量,不是關於某個模型的量。 你想要的是 ——你手上這一份資料訓出來的那個模型的風險。CV 估的是它的期望值(而且是在稍小的樣本數下)。當你只是要比較兩個演算法時,這個區別無關緊要;當你要回報一個數字時,它很重要。
變異數。 如上, 個項的相關係數 讓
所以可以用 CV 的折內標準差感覺穩定度,但不要用它算 值或信賴區間。要比較兩個演算法,用配對比較(同一組 fold 上兩者的差),並且用重複 CV 或多個種子看差值的散佈——這是 同一個實驗跑三次結果都不同,該報哪一個? 的內容。
它不能估什麼。 CV 的每一折都假設「訓練折與驗證折獨立同分佈」。這個假設在三種情況下直接錯:樣本之間有相依(時間、空間、同一個個體的多筆)、資料被增強或去重不完全、以及切分之前做過任何用到全部資料的前處理(老師用考古題教學又出題:為什麼要切資料 的洩漏)。
補充留一法為什麼不是「最好的 CV」
LOO 的偏誤最小(訓練用 筆),所以直覺上它應該是最準的。但它有兩個代價。
變異數不一定小。 個訓練集兩兩只差一筆,所以 個誤差幾乎完全相關,,平均它們幾乎沒有降變異數的效果。實測(下一節)LOO 的估計標準差確實比 5-fold 小一些( 對 ),但差距遠小於「 折對 折」的直覺—— 倍的改善並沒有出現。
不穩定的演算法會讓 LOO 崩壞。 對「換一筆資料就換一個模型」的演算法(決策樹、 的最近鄰、高次多項式),LOO 的每一折都在評估一個不同的模型,平均起來未必代表任何東西。
實務結論: 或 幾乎總是夠好,而且便宜 – 倍。LOO 值得用的場合是資料極少()且模型穩定,或者模型有 LOO 的解析捷徑(線性模型的 hat matrix 讓 LOO 幾乎免費)。
回到情境:什麼時候不能隨機切
規則:fold 的邊界要沿著「相依的邊界」畫。
- 時間序列:訓練折必須全部早於驗證折。標準做法是前向鏈(forward chaining):用前 訓練、預測接下來的 ;用前 訓練、預測接下來的 ;以此類推。它同時模擬了真實部署。
- 群組結構:同一位病人、同一個使用者、同一份原始文件的所有列整組落在同一折(
GroupKFold的用途)。 - 空間資料:鄰近的地點高度相關,隨機切等於用鄰居預測鄰居;要用區塊切分。
這一切依賴什麼。 一,除了上述相依之外,樣本是 iid。二,你的整條流程(含前處理)在每一折內部重跑——把標準化或特徵選擇放在 CV 迴圈外面,就是上一篇那個 的錯誤。三, 折的訓練規模與最終訓練規模相差不大,否則 CV 的悲觀偏誤不能忽略。
回到情境:穩多少,以及什麼時候說謊
第一段:三種切法的穩定度。 共用 toy(),用三種方式從 degree – 裡選一個,重複 200 次不同的資料:
選中 degree 的標準差 | 選出模型的真實風險(中位數) | 對 d=5 的估計標準差
單次留出 (10/30) 2.21 | 0.0311 | 0.4069
5-fold 1.55 | 0.0302 | 0.0304
LOO (30-fold) 1.70 | 0.0306 | 0.0190
最右欄是重點:對同一個模型()的誤差估計,單次留出的標準差是 ,5-fold 是 ——穩了十三倍。 而真實風險大約是 ,也就是說單次留出的估計值本身的抖動比它要估的量還大十五倍。用這樣一個數字去比較兩個模型,等於擲骰子。
中間那欄說明這個穩定度確實買到了東西,但買到的比想像中少:選出模型的真實風險從 改善到 (約 )。原因是這個 toy 的 U 形谷底很平( 到 的母體風險都在 –),選錯 degree 的代價本來就不大;在谷底陡峭的問題上,兩者才會同時改善。
交叉驗證主要買到的是「你對自己的估計有多少信心」,不是「模型好多少」。
最左欄則印證了 Details 裡的說法:LOO()並沒有比 5-fold()更穩定地選出同一個 degree,儘管它折數多了六倍。折與折之間的高度相依讓多出來的折幾乎沒有帶來新資訊。
第二段:刻意違反「樣本獨立」。 造一條純隨機漫步——沒有任何可預測的結構,每一步是上一步加獨立噪聲。模型是「用時間上最近的 5 個鄰居的平均去預測」。任何誠實的評估都應該說「這個模型沒有用」:
import numpy as np
n = 200; idx = np.arange(n)
r = np.random.default_rng(0)
y = np.zeros(n)
for i in range(1, n):
y[i] = 0.97*y[i-1] + 0.1*r.standard_normal() # 高度自相關,沒有可學的訊號
def predict(tr, te): # 用時間上最近的 5 個鄰居平均
return np.array([y[tr[np.argsort(np.abs(tr - i))[:5]]].mean() for i in te])
folds = np.array_split(r.permutation(n), 5) # A) 隨機 5-fold
rand = np.mean([((predict(np.setdiff1d(idx, f), f) - y[f])**2).mean() for f in folds])
fwd = np.mean([((predict(idx[:k*40], idx[k*40:(k+1)*40]) # B) 前向鏈
- y[k*40:(k+1)*40])**2).mean() for k in range(1, 5)])
print(f"隨機 5-fold MSE {rand:.4f} 前向鏈 MSE {fwd:.4f}")
200 次重複的平均:
隨機 5-fold 交叉驗證 MSE 0.0095
前向鏈(只用過去) MSE 0.1392
(對照:前向鏈下「用訓練期平均去猜」的 MSE 0.1559)
隨機 5-fold 說這個模型近乎完美(),前向鏈說它幾乎和「用歷史平均去猜」一樣爛( 對 )。 差了十五倍,而資料裡沒有任何可學的東西。
機制:隨機切分之後,被留出的每個時點的前一秒與後一秒都在訓練集裡。序列高度自相關,所以「用鄰居的平均」幾乎等於直接讀答案。這不是模型作弊——是評估方式讓它得以內插而不是外推,而真實部署永遠是外推。
這個錯誤在真實專案裡的樣子:股價預測、需求預測、感測器異常偵測、使用者流失預測——只要資料有時間戳而評估用了隨機切分,你看到的分數就與部署後的表現無關。一個可以馬上做的檢查:把評估換成前向鏈,看分數掉多少。掉很多,表示原本的分數大部分來自時間洩漏。
切法、K 值與相依:三個旋鈕
互動 demo:拉 K 看估計的抖動變小;把 fold 的邊界放到相關的資料中間,CV 就開始說謊。
先消化一下
參考文獻
- Hastie, T., Tibshirani, R., Friedman, J. The Elements of Statistical Learning, 2nd ed. Springer 2009, Ch. 7.10.(-fold 在估什麼、 的偏誤–變異數權衡、以及正確與錯誤的 CV 流程。)
- Bengio, Y., Grandvalet, Y. No Unbiased Estimator of the Variance of K-Fold Cross-Validation. JMLR 2004.(為什麼折間相依讓 CV 的標準誤無法無偏估計,以及這對模型比較的意義。)
- Bergmeir, C., Benítez, J. M. On the use of cross-validation for time series predictor evaluation. Information Sciences 2012.(時間序列上哪些 CV 變體可用、前向鏈的性質與例外。)