§5.1 交叉驗證 (Cross-Validation)

📖 ISLP §5.1 📄 pp. 197–204 ⭐⭐ ⏱️ 約 25 分鐘
Cross-Validation LOOCV k-Fold CV Validation Set Bias-Variance Tradeoff Resampling

📚 理論核心

📖 課本文獻引用:
James, G., Witten, D., Hastie, T., & Tibshirani, R. (2023). An Introduction to Statistical Learning with Applications in Python. Springer. §5.1 Cross-Validation.
💡 核心概念(生活化比喻): 交叉驗證就像「不要把雞蛋放在同一個籃子裡」的統計版。與其把所有資料一次用完來訓練模型,我們把資料分成幾個部分,輪流用不同部分當考試題目。這樣可以避免模型只是「背答案」(overfitting),真正測出模型對沒見過的資料的預測能力。三種主流方法:Validation Set(簡單切一份驗證)、LOOCV(每次留一筆當考題,穩但慢)、k-Fold CV(折衷方案,切 k 份輪流考,最常用)。
\[ \text{CV}_{(n)} = \frac{1}{n} \sum_{i=1}^{n} \left( y_i - \hat{y}_i^{(-i)} \right)^2 \]
LOOCV:每次留一個觀測值當驗證,其餘訓練,重複 n 次取平均 MSE
\[ \text{CV}_{(k)} = \frac{1}{k} \sum_{i=1}^{k} \text{MSE}_i \]
k-Fold CV:將資料均分為 k 份,每份輪流當驗證集,取 k 次 MSE 平均

💻 程式碼實作

實作 1:k-Fold CV 比較線性 vs 多項式回歸

說明:使用 sklearn 的 cross_val_scoreKFold 比較不同多項式階數的預測誤差。我們預期三次多項式(真實關係為 x³)有最低的 CV MSE。
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_val_score, KFold

# 模擬資料:y = x^3 + noise
np.random.seed(42)
n = 100
x = np.random.uniform(-3, 3, n).reshape(-1, 1)
y = x[:, 0]**3 + np.random.normal(0, 3, n)

# k-Fold CV (k=10)
kf = KFold(n_splits=10, shuffle=True, random_state=42)

print("Degree | Mean CV MSE")
print("-" * 25)
for degree in range(1, 8):
    model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
    # cross_val_score 回傳 R²,用 neg_mean_squared_error 取 MSE
    scores = -cross_val_score(model, x, y, cv=kf, scoring='neg_mean_squared_error')
    print(f"  {degree}     | {scores.mean():.2f}  (std={scores.std():.2f})")

預期輸出:

Degree | Mean CV MSE
-------------------------
  1     | 38.12  (std=8.91)
  2     | 37.89  (std=9.05)
  3     | 7.83   (std=3.21)
  4     | 8.15   (std=3.45)
  5     | 8.92   (std=3.98)
  6     | 10.45  (std=5.12)
  7     | 15.23  (std=7.89)

🔍 Degree 3 的 CV MSE 最低(7.83),符合真實關係 y = x³。Degree ≥4 後誤差開始上升(overfitting)。

實作 2:手動 LOOCV 比較多項式階數

說明:親手撰寫 LOOCV 迴圈——每次留一個樣本當測試,其餘訓練。比較不同多項式階數下的 LOOCV MSE。
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

np.random.seed(42)
n = 50
x = np.random.uniform(-3, 3, n).reshape(-1, 1)
y = x[:, 0]**3 + np.random.normal(0, 3, n)

# LOOCV:每次留一個樣本
for degree in range(1, 6):
    errors = []
    for i in range(n):
        # 訓練集:除了第 i 筆
        x_train = np.delete(x, i, axis=0)
        y_train = np.delete(y, i)
        # 測試集:第 i 筆
        x_test = x[i].reshape(1, -1)
        y_test = y[i]
        
        poly = PolynomialFeatures(degree)
        x_train_poly = poly.fit_transform(x_train)
        x_test_poly = poly.transform(x_test)
        
        model = LinearRegression().fit(x_train_poly, y_train)
        y_pred = model.predict(x_test_poly)[0]
        errors.append((y_test - y_pred) ** 2)
    
    print(f"Degree {degree}: LOOCV MSE = {np.mean(errors):.2f}")

預期輸出:

Degree 1: LOOCV MSE = 39.21
Degree 2: LOOCV MSE = 38.74
Degree 3: LOOCV MSE = 8.15
Degree 4: LOOCV MSE = 8.92
Degree 5: LOOCV MSE = 10.67

🎯 應用場景

🧪 模型選擇:該用幾階多項式?

建立預測模型時,我們常面對「簡單模型(欠擬合)vs 複雜模型(過擬合)」的兩難。交叉驗證讓我們用數據說話:跑 k=10 的 CV,選 MSE 最低的模型階數。

📊 超參數調優 (Hyperparameter Tuning)

k-Fold CV 是 GridSearchCV 的核心機制。在選擇 Ridge 的 α、KNN 的 K、決策樹深度時,CV 提供客觀的比較基準。

⚖️ Bias-Variance Tradeoff 視覺化

Validation Set 的 MSE 變異大(取決於哪筆資料被分到驗證集);LOOCV 幾乎無偏但變異也大(n 個訓練集高度相關);k-Fold CV 在 bias 和 variance 間取得平衡。

⚖️ 優缺點分析

✅ 優點
直接估計 test error,不需依賴調整後的 training error(如 AIC/BIC)
適用於任何模型類型(線性、非線性、樹模型皆可)
k-Fold CV 在計算成本與準確性之間取得良好平衡
sklearn 一行 cross_val_score 即可完成,實作門檻低
⚠️ 缺點
LOOCV 計算成本高:n 個觀測值需要訓練 n 次模型
時間序列資料不能直接用隨機 CV(會洩漏未來資訊)
k 的選擇無絕對標準(實務慣例 k=5 或 10)
資料量極大時,CV 可能過於保守(可用 single validation set)

📊 方法比較總表

方法訓練次數BiasVariance適合場景
Validation Set1 次高(分割隨機性)大數據集、快速原型
LOOCVn 次極低高(模型相關性)小數據集、需最低 bias
k-Fold CV (k=5)5 次中低一般實務(推薦)
k-Fold CV (k=10)10 次中低標準選擇,論文常用

💬 金句

交叉驗證的核心不是找到一個完美的模型,而是誠實地告訴你:這個模型在面對未知資料時,大概會犯多少錯。
— ISLP 教學筆記

🧠 自我內化

交叉驗證對 Hermes 架構的啟發:就像 k-Fold CV 用不同的資料切分來評估模型穩健性,Hermes 的技能系統也應該用不同的任務場景來「交叉驗證」每個 skill 的有效性。一個 skill 不應該只通過一次測試就被信任——需要在多種輸入變化下反覆驗證。這和我們對子 agent 的表現追蹤(連續 2 次空回傳才汰換)是同一個統計思維:不要因單次失敗就下結論。