重抽樣:用手上的資料造出更多資料

ISLP 第 5 章 — 對應講義 05
驗證集|LOOCV|k-fold CV|偏差–變異取捨|Bootstrap|1−(1−1/n)ⁿ → 0.632
向下捲動開始互動
📌 本頁使用方式(ISLP Ch.5|講義 05)

照節次讀:每節先讀說明,動手玩互動元件——先預測結果,再按按鈕驗證。 ② 對照講義:每個 §徽章都標了 ISLP 節號與講義頁碼,細節與完整推導請回講義與課本。 ③ 每節做 quiz:答錯就回到該節重讀,不要往下跳;錯的選項也寫了「錯在哪」。 ④ 最後翻關鍵詞彙卡自測術語,並用 REF 總覽當速查表。

CONTENTS · 內容目錄
PROLOGUE · 開場

為什麼要重抽樣:訓練誤差不是測試誤差 ISLP §5 開頭講義 05 · p.2–6

你已經會配模型了。現在的問題換成:這個模型在沒見過的資料上會有多準? 最直覺的做法是拿訓練資料算誤差——但那個數字幾乎一定太樂觀,因為模型是照著那批資料調出來的。 極端一點想:一棵長到每個葉子只剩一個樣本的樹,訓練誤差是 0,可是它什麼都沒學到。

如果手上有一大筆獨立的測試資料,事情很簡單。但真實情況通常是資料就這麼多, 再切出去一塊當測試集就不夠訓練了。重抽樣(resampling)的想法是: 反覆從同一批資料裡切出「假的」訓練/測試組合,用它們的平均表現去猜真正的測試誤差。

本章你會學到的三件事 1. 驗證集法(validation set):切兩半,最簡單,但答案會隨切法跳動。
2. 交叉驗證(cross-validation):LOOCV 與 k-fold,讓每個樣本都輪到當一次測試資料。
3. Bootstrap:有放回地重抽,不靠公式就能算出任何估計量的標準誤。

這三個工具的分工可以先記住:交叉驗證是用來估「預測誤差」並藉此選模型bootstrap 是用來估「某個估計量的不確定性」。兩者都在重抽樣,但問的問題不同。

切幾次每次訓練用多少有隨機性嗎主要用途
驗證集法1約 n/2有,換 seed 就變快速粗估
LOOCVnn − 1沒有(分割唯一)小資料、要穩定
k-fold CVk(通常 5 或 10)n(k−1)/k有,但比驗證集小很多選模型的標準做法
BootstrapB(通常 1000)n(有放回)有,B 大就穩估標準誤與信賴區間
QUIZ · 為什麼不能用訓練誤差

為什麼「訓練誤差」不能拿來當測試誤差的估計?

(A) 模型的參數是照著這批資料挑出來的,誤差已經被最小化過,所以會系統性偏低
(B) 訓練資料的樣本數太少,誤差的變異太大
(C) 訓練誤差用的是 MSE,測試誤差用的是別的指標,兩者不能比
PART 01 · 驗證集法

隨手一切兩半:簡單,但答案會跳 ISLP §5.1.1講義 05 · p.7–9

最直接的做法:把資料隨機切成兩半,一半訓練、一半當驗證集(validation set, 也叫 hold-out set)。模型完全沒看過驗證集,所以驗證集上的 MSE 就是一個誠實的測試誤差估計:

$$\mathrm{MSE}_{\text{valid}} = \frac{1}{|V|} \sum_{i \in V} \left(y_i - \hat f(x_i)\right)^2$$

講義用 Auto 資料(n = 392)示範:把 196 筆拿去訓練、196 筆當驗證集, 用 horsepower 預測 mpg。看起來很乾淨——問題是換一個切法,答案就變了

圖表需要連網載入 Chart.js。此圖的重點:十種不同的隨機切分,畫出來是十條差很多的曲線——驗證集法的答案取決於你剛好怎麼切。
按「顯示十次切分」看同一份資料在不同切法下的驗證 MSE。
怎麼看這張圖 圖 5.2 右
每條淡線是一次隨機切分(test_size=196)算出的驗證 MSE,x 軸是 horsepower 多項式的次數。粗線是十次的平均。十條線的高低差距,就是驗證集法的變異。
十次切分的落點(degree 2)
最低
最高
全距
平均
結論
每一條都會告訴你「二次比一次好」——這個結論很穩。但如果你想引用一個具體數字當測試 MSE,那個數字非常不可靠。

講義完整實作:切一半、配模型、算驗證 MSE

講義 05 · 驗證集法(Auto,degree 1)
Auto_train, Auto_valid = train_test_split(Auto, test_size=196, random_state=rng) X_valid = hp_mm.transform(Auto_valid) y_valid = Auto_valid['mpg'] valid_pred = results.predict(X_valid) np.mean((y_valid - valid_pred)**2)
預期輸出
np.float64(25.57387818968441)

這個 25.57 就是課本 §5.1.1 引用的數字。注意 random_state=rng——換掉它,下面的數字就會不一樣。

來源:Ch05-resample-lab-zh.ipynb · 儲存格 16、20
講義 05 · 換一個切法(random_state=3)
Auto_train, Auto_valid = train_test_split(Auto, test_size=196, random_state=3) MSE = np.zeros(3) for idx, degree in enumerate(range(1, 4)): MSE[idx] = evalMSE([poly('horsepower', degree)], 'mpg', Auto_train, Auto_valid) MSE
預期輸出
array([20.75540796, 16.94510676, 16.97437833])

degree 1/2/3 的驗證 MSE 從 [25.57, 22.22, 22.67] 變成 [20.76, 16.95, 16.97]同一份資料、同一個模型,只是切法不同,數字差了快 5。但兩次都指向同一個結論:二次比一次好,三次沒有再更好。

來源:Ch05-resample-lab-zh.ipynb · 儲存格 26、28
觀念釐清
Q:驗證集法為什麼會「高估」測試誤差?

因為它只用了一半的資料訓練。統計模型通常餵愈多資料表現愈好,所以用 n/2 筆訓練出來的模型,本來就比用全部 n 筆訓練出來的差。你量到的是「一個比較弱的模型」的誤差,因此系統性偏高。

這是驗證集法的兩個缺點之一。另一個是變異太大(就是上面那張圖)。交叉驗證同時改善了這兩點:每一輪都用 n(k−1)/k 筆訓練(比 n/2 多),而且做 k 輪再平均(變異變小)。

Q:那 random_state 到底該不該固定?

該固定,但理由是可重現,不是「這個 seed 比較好」。固定 seed 讓你(和讀你程式的人)能重跑出同樣的數字。

危險的做法是「試幾個 seed,挑數字最漂亮的那個報出來」——那等於用驗證集調參,報出來的誤差就不再誠實了。要降低這種隨機性的影響,正解是改用 k-fold CV,不是換 seed。

QUIZ · 驗證集法

驗證集法有兩個公認的缺點。下面哪一組說對了?

(A) ① 估計值變異大(換切法就變)② 只用一半資料訓練,所以高估測試誤差
(B) ① 計算量太大 ② 只能用在迴歸,不能用在分類
(C) ① 會用到測試資料的資訊 ② 需要假設誤差是常態分佈
PART 02 · LOOCV

一次只留一個:沒有隨機性,而且有捷徑 ISLP §5.1.2講義 05 · p.10–12

驗證集法浪費了一半的資料,而且答案會跳。把它推到極端: 每次只留一個樣本當驗證集,其餘 n − 1 筆全部拿去訓練,做 n 次再平均。 這就是留一交叉驗證(leave-one-out cross-validation, LOOCV):

$$\mathrm{CV}_{(n)} = \frac{1}{n} \sum_{i=1}^{n} \mathrm{MSE}_i, \qquad \mathrm{MSE}_i = \left(y_i - \hat f^{(-i)}(x_i)\right)^2$$

其中 $\hat f^{(-i)}$ 是「拿掉第 i 筆」配出來的模型。這樣做有兩個好處: 每一輪都用了幾乎全部的資料(偏差很小),而且分割方式唯一——沒有隨機性, 跑一百次都是同一個數字。

按「開始」逐筆留一:橘色是這一輪被留下來驗證的點,虛線是用其餘 11 筆配出的迴歸線。
虛擬碼 CODE
total = 0 for i in range(n): 留下第 i 筆,其餘訓練 total += (y[i] - 預測)**2 CV = total / n
目前進度
第幾輪0 / 12
這輪的平方誤差
累計平均 CV
為什麼沒有隨機性
「留第 1 筆」「留第 2 筆」…「留第 n 筆」——這 n 種分割是枚舉出來的,不是抽出來的。所以 LOOCV 沒有 random_state 可以調。

講義完整實作:用 cross_validate 跑 LOOCV

講義 05 · LOOCV(Auto,degree 1)
hp_model = sklearn_sm(sm.OLS, MS(['horsepower'])) X, Y = Auto.drop(columns=['mpg']), Auto['mpg'] # 交叉驗證迭代器 cv = LeaveOneOut() cv_results = cross_validate(hp_model, X, Y, cv=cv ) cv_err = np.mean(cv_results['test_score']) cv_err
預期輸出
np.float64(24.23151351792922)

24.2315 就是 degree 1 的 LOOCV 估計。跟上面驗證集法的 25.57/20.76 比一比:LOOCV 只有一個答案,不會因為切法而跳。

來源:Ch05-resample-lab-zh.ipynb · 儲存格 32
最小平方法有捷徑,不必真的配 n 次 對線性或多項式的最小平方擬合,LOOCV 有封閉解(ISLP 式 5.2):
$$\mathrm{CV}_{(n)} = \frac{1}{n}\sum_{i=1}^{n}\left(\frac{y_i - \hat y_i}{1 - h_i}\right)^2$$ 只要配一次模型,拿殘差跟槓桿值 $h_i$ 就算得出來。
但注意:scikit-learn 的通用 cross_validate() 沒有用這個公式, 所以 lab 裡跑 LOOCV 還是慢——這是實作的事,不是理論的事。
觀念釐清
Q:LOOCV 的偏差最小,為什麼還不是首選?

因為它的變異偏大。LOOCV 平均的那 n 個 $\mathrm{MSE}_i$ 彼此高度相關——任兩輪的訓練集有 n − 2 筆是重疊的,所以配出來的模型幾乎一樣。把 n 個高度相關的數字平均,並不會像平均 n 個獨立數字那樣有效地降低變異。

k-fold(k = 5 或 10)的訓練集重疊程度低得多,各折之間比較不相關,所以平均起來變異較小。代價是每輪少用一點資料,偏差稍微大一些——這就是 k 的取捨。

Q:LOOCV 算出來的 24.23,是在估「哪個」模型的測試誤差?

嚴格說,它估的是「用 n − 1 筆資料訓練出來的模型」的期望測試誤差,但因為 n − 1 跟 n 幾乎一樣,實務上直接把它當成「用全部 n 筆訓練出來的那個模型」的測試誤差。

這也是為什麼 CV 的最後一步通常是:用 CV 選出超參數(例如次數 = 2),然後用全部資料重配一次,交出那個模型。CV 的角色是選,不是產出最終模型。

QUIZ · LOOCV

同一份資料上,把 LOOCV 跑兩次,會得到一樣的答案嗎?

(A) 會,因為 n 種「留一」分割是枚舉出來的,沒有隨機性
(B) 不會,因為每輪的訓練集不同,隨機性會累積
(C) 不一定,取決於有沒有設 random_state
PART 03 · k-fold CV

折成 k 份輪流當驗證集 ISLP §5.1.3講義 05 · p.13–15

LOOCV 要配 n 次模型,n 大就吃不消。折衷方案:把資料隨機平分成 k 份, 每次拿一份當驗證集、其餘 k − 1 份訓練,做 k 輪再平均。這就是 k-fold 交叉驗證:

$$\mathrm{CV}_{(k)} = \frac{1}{k} \sum_{j=1}^{k} \mathrm{MSE}_j$$

LOOCV 其實就是 k = n 的特例。實務上 k 取 5 或 10——原因下一節講。

選 k 之後按「開始」,看每一折輪流當驗證集。
怎麼看
左邊用 20 顆球示意切法:橘色是這一輪被留下來驗證的那一折,深藍是訓練用的部分。右邊是同一個 k 在 Auto(n = 392, degree 2)上真的跑出來的每折 MSE。
這一輪
k5
第幾折0 / 5
訓練 / 驗證筆數
這折的 MSE(Auto)
累計平均
每折 MSE 差很多,這正常嗎
正常。上面五折的 MSE 從 13 跳到 24——每折只有 78 筆,本來就不穩。CV 的價值在平均,不在單一折。

講義完整實作:KFold 跑 10-fold

講義 05 · 10-fold CV(degree 1–5)
cv_error = np.zeros(5) cv = KFold(n_splits=10, shuffle=True, random_state=0) # use same splits for each degree not rng here, see https://scikit-learn.org/stable/common_pitfalls.html#robustness-of-cross-validation-results for i, d in enumerate(trange(1,6)): X = np.power.outer(H, np.arange(d+1)) M_CV = cross_validate(M, X, Y, cv=cv) cv_error[i] = np.mean(M_CV['test_score']) cv_error

shuffle=True 很重要:如果原始資料有排序(例如 Auto 按年份),不打亂就會讓每一折的分佈完全不同。random_state=0 是為了讓不同 degree 用同一組分割——這樣比較才公平。這格 lab 沒存下輸出,下一節的圖用同樣設定重算了一次。

來源:Ch05-resample-lab-zh.ipynb · 儲存格 41
QUIZ · k-fold

比較不同模型(例如 degree 1 到 10)的 CV 誤差時,為什麼要讓每個模型用同一組折分割?

(A) 否則模型之間的差異會混進「分割不同」造成的雜訊,可能比模型本身的差異還大
(B) 因為 scikit-learn 要求 cv 物件必須重複使用
(C) 因為用不同分割會讓 CV 誤差變成有偏估計
PART 04 · k 該取多少

k 的偏差–變異取捨:為什麼實務上取 5 或 10 ISLP §5.1.4講義 05 · p.16

k 該取多少?兩個方向在拉扯:

ISLP §5.1.4 的結論是:k = 5 或 k = 10 是偏差與變異都可接受的折衷, 而且計算量只有 LOOCV 的 5/n 或 10/n。下面這張圖是 Auto 上的實測:

圖表需要連網載入 Chart.js。此圖的重點:從 degree 1 到 2,CV 誤差從 24.2 掉到 19.2;之後就平了。LOOCV 與 10-fold 的曲線幾乎重疊。
LOOCV 與 10-fold CV 在同一份 Auto 資料上的比較。
怎麼看這張圖 圖 5.4
x 軸是 horsepower 多項式的次數,y 軸是 CV 估的測試 MSE。兩條線分別是 LOOCV 與 10-fold CV。兩條幾乎重疊——這在實務上很常見,也是大家願意用便宜的 10-fold 取代 LOOCV 的理由。
最低點
LOOCV 最小
10-fold 最小
兩者最大差距
讀圖的重點
不要只看「哪個 degree 的數字最小」。degree 5 的 CV 誤差是 19.03、degree 2 是 19.25,差 0.2——這遠在雜訊範圍內。看的是曲線在哪裡「拉平」,拉平之後就選最簡單的那個。
k每輪訓練用偏差變異配模型次數評語
2n/2最大(高估)最小2太粗,很少用
50.8 n5實務常用
100.9 n更小略大10實務最常用
n(LOOCV)n − 1最小最大n小資料、或有式 5.2 捷徑時
觀念釐清
Q:「k 愈大變異愈大」聽起來很反直覺——平均更多項不是應該更穩嗎?

平均更多項會更穩,前提是那些項彼此獨立。這裡不是。

算式上看:$\mathrm{Var}(\bar X) = \frac{\sigma^2}{k} + \frac{k-1}{k}\rho\sigma^2$。第一項隨 k 變大而變小,但第二項隨 $\rho$(各折之間的相關)變大而變大。LOOCV 的 $\rho$ 接近 1,第二項就吃掉了第一項的好處。

直覺版:LOOCV 的 n 個模型幾乎是同一個模型,所以你其實只有「一個」意見被重複算了 n 次。

Q:跑 CV 之前要不要標準化?在哪一步做?

要,而且必須在每一折裡面做:用該折的訓練部分算平均與標準差,再套到驗證部分。

如果先用全部資料標準化再切折,驗證資料的平均與標準差就洩漏進訓練流程了,CV 誤差會偏低。scikit-learn 的正解是把標準化包進 Pipeline,再把整個 pipeline 丟給 cross_validate——它會自動在每折內重新 fit。這跟下一節的錯誤是同一個病。

QUIZ · k 的取捨

為什麼實務上不直接用偏差最小的 LOOCV,而多半選 k = 5 或 10?

(A) LOOCV 的各折高度相關,平均後變異較大;而且要配 n 次模型
(B) 因為 LOOCV 會用到驗證資料的資訊,估計不誠實
(C) 因為 LOOCV 只能用在線性模型上
PART 05 · 分類問題上的 CV

把 MSE 換成錯誤率,其餘一樣 ISLP §5.1.5講義 05 · p.17–18

整套邏輯搬到分類問題只要換一件事:把 MSE 換成錯誤率

$$\mathrm{CV}_{(k)} = \frac{1}{k} \sum_{j=1}^{k} \mathrm{Err}_j, \qquad \mathrm{Err}_j = \frac{1}{|C_j|} \sum_{i \in C_j} I(y_i \neq \hat y_i)$$

$I(\cdot)$ 是指示函數:預測錯就是 1、對就是 0。其餘完全一樣:切 k 折、輪流當驗證集、平均。 ISLP 圖 5.7–5.8 用邏輯斯迴歸加上不同次數的多項式項示範, CV 誤差率確實會在「真正的邊界複雜度」附近最低。

實務上要注意的三件事 1. 類別不平衡就要分層:StratifiedKFold,讓每一折的類別比例跟整體一致。類別很少的時候, 普通 KFold 可能切出「某一折完全沒有正例」的荒謬情況。
2. 錯誤率不一定是你要的指標:正例只佔 1% 時,全部猜負例就有 99% 正確率。 改用 AUC、F1 或 recall,作法完全相同——換掉 scoring 參數就好。
3. 資料有群組結構就要用 GroupKFold:同一個病人的多次就診、 同一個使用者的多筆紀錄,不能一部分在訓練、一部分在驗證。
情況該用的切分器為什麼
一般迴歸/分類KFold(shuffle=True)最基本
類別不平衡StratifiedKFold保住每折的類別比例
同一實體有多筆資料GroupKFold同組資料不可跨訓練/驗證
時間序列TimeSeriesSplit不能用未來預測過去
只想快速看一眼ShuffleSplit(n_splits=1)等於驗證集法
觀念釐清
Q:時間序列為什麼不能用普通的 k-fold?

因為隨機切折會讓「未來」的資料進到訓練集、「過去」的資料留在驗證集。模型於是可以用 2026 年的資訊去預測 2025 年——這在部署時根本不可能發生,所以 CV 誤差會嚴重低估真實表現。

正解是只往前切TimeSeriesSplit):用前 100 筆訓練、預測第 101–120 筆;再用前 120 筆訓練、預測第 121–140 筆…訓練集永遠在驗證集之前。

QUIZ · 分類上的 CV

一份資料裡正例只佔 2%。用普通的 KFold(n_splits=10) 會有什麼風險?

(A) 某些折可能幾乎(或完全)沒有正例,那一折的錯誤率就沒有意義
(B) 錯誤率的公式在不平衡資料上不成立,要改用 MSE
(C) k 必須小於少數類別的樣本數,否則 CV 無法執行
PART 06 · CV 的對與錯

先用全資料選特徵再 CV:一個很常見的致命錯誤 ISLP §5.1.4講義 05 · p.19–21

這一節是整章最容易在實務上出錯、而且錯了不會有任何錯誤訊息的地方。

常見情境:手上有 p = 500 個特徵、n = 50 筆資料。你先算每個特徵跟 y 的相關係數, 挑出最相關的 10 個,然後對這 10 個特徵做 5-fold CV,得到一個漂亮的誤差率。 這個數字是假的。

問題在於「挑特徵」這一步看過了全部的 y,包含後來被當成驗證資料的那些。 篩選本身就是模型訓練的一部分,它必須關在每一折裡面做。

圖表需要連網載入 Chart.js。此圖的重點:資料是純噪音(y 與 X 完全獨立),正確做法給出約 0.5 的錯誤率,錯誤做法卻只有 0.26。
同一份純噪音資料,兩種 CV 流程的結果。
這個模擬在做什麼 ISLP §5.1.4
n = 50、p = 500 的純噪音資料:y 是丟硬幣決定的,跟每一個 X 都完全無關。所以任何誠實的方法都該回報「錯誤率約 50%,這些特徵沒用」。
兩種做法
先選特徵再 CV(錯)
在每折內選特徵(對)
誠實的答案應該是0.50
為什麼差這麼多
從 500 個純噪音特徵裡挑「最相關的 10 個」,一定挑得到幾個剛好跟這 50 筆 y 對得上的。那個「剛好」也包含了驗證折的 y——模型於是在驗證資料上作弊。
一句話原則 凡是用到 y 的步驟——特徵篩選、標準化的平均與標準差、 缺失值填補、過抽樣、目標編碼、PCA 降維——都必須關在每一折的訓練部分裡面做。 在 scikit-learn 裡,把它們串成 Pipeline 再交給 cross_validate,這件事就自動對了。
觀念釐清
Q:只用 X 不看 y 的前處理(例如 PCA、去除零變異特徵),也要關在折裡嗎?

嚴格說要,實務上影響小得多。

不看 y 的前處理不會直接把答案洩漏進來,所以偏差通常很輕微。但它仍然用到了驗證資料的分佈資訊(PCA 的方向、特徵的變異數),而在部署時你不可能先看過未來資料的分佈。既然包進 Pipeline 幾乎沒有額外成本,就一律包進去,不必去分辨哪種洩漏比較嚴重。

Q:我用 CV 選超參數,那 CV 誤差可以當成最終模型的測試誤差報出來嗎?

不太行,它會偏低。你已經拿 CV 誤差當目標挑過超參數了——被挑中的那組本來就是「在這組折上運氣最好」的那組,這叫做選擇偏差。

要誠實的估計,用嵌套交叉驗證(nested CV):外層折只負責評估、完全不參與調參;內層折在外層的訓練部分裡調參。或者更簡單:一開始就切出一份從頭到尾沒碰過的測試集。

QUIZ · CV 的錯用

在 5-fold CV 之前先用全部資料把特徵標準化(減平均、除標準差)。這樣有問題嗎?

(A) 有問題:平均與標準差用到了驗證折的資料,屬於洩漏,CV 誤差會偏低
(B) 沒問題:標準化只是線性變換,不改變模型的預測能力
(C) 沒問題:標準化沒有用到 y,所以不算洩漏
PART 07 · Bootstrap

有放回地重抽:直接把標準誤算出來 ISLP §5.2講義 05 · p.22–35

換一個問題。前面都在問「模型的預測有多準」;現在問「我算出來的這個數字有多不確定」

ISLP §5.2 的例子:把錢分成比例 α 投資 X、1 − α 投資 Y,要讓報酬的變異最小,最佳比例是

$$\alpha = \frac{\sigma_Y^2 - \sigma_{XY}}{\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}}$$

把樣本變異數代進去就得到 $\hat\alpha$。但 $\hat\alpha$ 有多可靠? 它的標準誤沒有簡單的公式可查。理想上我們會重新蒐集 1000 份新資料、算 1000 個 $\hat\alpha$、 看它們的標準差——但我們只有一份資料。

Bootstrap 的把戲:把手上這份資料當成母體,從裡面有放回地抽 n 筆, 當成一份「新」資料集,重算 $\hat\alpha^*$。重複 B 次,那 B 個 $\hat\alpha^*$ 的標準差 就是 $\mathrm{SE}(\hat\alpha)$ 的估計。不需要任何分佈假設,也不需要推導。

圖表需要連網載入 Chart.js。此圖的重點:1000 次 bootstrap 重抽算出的 α̂* 分佈,它的標準差 0.0912 就是 SE(α̂) 的估計。
按「單步」看一次有放回重抽:實心=被抽到(可能重複),空心虛線=這次沒被抽到。
虛擬碼 CODE
for b in range(B): idx = 有放回抽 n 個 θ*[b] = f(資料[idx]) SE = std(θ*)
這一次重抽
抽到的樣本
沒被抽到(OOB)
這次的樣本平均 θ̂*
累計 B0
累計 SE
Portfolio 的真實結果 ISLP §5.2
用全部 100 筆算:α̂ = 0.5758。跑 B = 1000 次 bootstrap 後,SE(α̂) = 0.0912。下面的 lab 卡片有逐字輸出。

講義完整實作:boot_SE()

講義 05 · α̂ 用全部 100 筆
alpha_func(Portfolio, range(100))
預期輸出
np.float64(0.57583207459283)

這就是 ISLP 書上的 α̂ = 0.5758。

來源:Ch05-resample-lab-zh.ipynb · 儲存格 53、55
講義 05 · 一次 bootstrap 重抽
rng = np.random.default_rng(0) alpha_func(Portfolio, rng.choice(100, 100, replace=True))
預期輸出
np.float64(0.6074452469619004)

replace=True 是關鍵:有放回,所以同一筆可能被抽到好幾次,也會有一些完全沒被抽到。這一次抽出來的 α̂* = 0.6074,跟 0.5758 差了不少——這個「差」正是我們要量化的東西。

來源:Ch05-resample-lab-zh.ipynb · 儲存格 57
講義 05 · 通用的 boot_SE
def boot_SE(func, D, n=None, B=1000, seed=0): rng = np.random.default_rng(seed) first_, second_ = 0, 0 n = n or D.shape[0] for _ in trange(B): idx = rng.choice(D.index, n, replace=True) value = func(D, idx) first_ += value second_ += value**2 return np.sqrt(second_ / B - (first_ / B)**2)

lab 的第 62 格寫著:SE(α̂) 的 bootstrap 估計是 0.0912。注意這支函式用 $E[\theta^2] - (E[\theta])^2$ 累加,不必存下 1000 個值。

來源:Ch05-resample-lab-zh.ipynb · 儲存格 59、61

一個漂亮的事實:bootstrap 樣本只含約 63.2% 的原始樣本

有放回地抽 n 次,某一筆始終沒被抽到的機率是 $\left(1 - \frac{1}{n}\right)^n$。 n 一大,這個數趨近 $e^{-1} \approx 0.368$。所以

$$P(\text{第 } i \text{ 筆有進 bootstrap 樣本}) = 1 - \left(1-\frac{1}{n}\right)^n \;\xrightarrow[n \to \infty]{}\; 1 - e^{-1} \approx 0.632$$

剩下那 36.8% 沒被抽到的樣本叫做 out-of-bag(OOB)。 它們對這一輪的模型來說是天然的驗證集——第 9 章的 bagging 與 random forest 就靠這招 免費拿到測試誤差估計。

圖表需要連網載入 Chart.js。此圖的重點:理論曲線 1−(1−1/n)ⁿ 從 n=2 的 0.75 很快收斂到 0.632,模擬點落在曲線上。
有放回抽 n 次,某一筆至少被抽到一次的機率。
怎麼看
藍線是理論值 $1-(1-1/n)^n$,紅點是每個 n 各跑 2000 次模擬的實測比例。收斂得非常快:n = 20 就已經是 0.642 了。
查表
n = 5
n = 20
n = 100
極限 1 − 1/e0.6321
這個 0.632 會再出現
第 9 章的 OOB 誤差、以及文獻裡的 .632 bootstrap 估計量,都是從這個事實長出來的。
觀念釐清
Q:Bootstrap 可以用來估「預測誤差」嗎?

可以,但要很小心,而且通常比不上交叉驗證。

問題出在重疊:bootstrap 樣本平均含有原始資料的 63.2%,所以如果你用 bootstrap 樣本訓練、用原始全部資料當測試,那個「測試集」裡有三分之二的資料模型已經看過了,誤差會嚴重低估。

補救方式是只用 OOB 的那 36.8% 來評估,這就接近 k-fold 的精神了。所以講義第 36 頁的答案是:估標準誤用 bootstrap,估預測誤差用交叉驗證。

Q:B 要取多少?

估標準誤時 B = 1000 通常就很夠;要估信賴區間的尾端分位數(例如 2.5% 與 97.5%),B 建議拉到 2000 以上,因為尾端需要更多樣本才穩。

注意 B 大只會讓「bootstrap 對真實 SE 的估計」更穩定,不會讓原始資料變多。n 小的時候 bootstrap 本身就不可靠,拉高 B 也救不了——它只是把同一份薄薄的資訊算得更精確而已。

Q:bootstrap 什麼時候會失效?

幾個典型的坑:

  • 估計量依賴極值(最大值、最小值、全距):bootstrap 樣本的最大值永遠不會超過原始資料的最大值,所以分佈會被截斷。
  • 資料不獨立(時間序列、空間資料、群組結構):直接對單筆有放回重抽會破壞相關結構。要改用 block bootstrap。
  • n 很小:把 10 筆資料當母體,本來就沒什麼可抽的。
QUIZ · Bootstrap

n = 100 時,一筆特定的觀測值完全沒有出現在某個 bootstrap 樣本裡的機率約為多少?

(A) 約 0.366
(B) 約 0.01
(C) 0,因為有放回抽 n 次一定會抽到每一筆
EXERCISES · 練習

動手驗證:ISLP 第 5 章精選題 ISLP §5.4 習題

下面幾題取自 ISLP §5.4 的課後習題,題號都對得回課本。先自己想過再點選項; 每個選項——包含錯的——都寫了為什麼。想看完整解答再對照下面3個站。

EXERCISE 1 · ISLP 5.4 第 2 題(a)(b)

從 n 筆資料中有放回地抽第一筆時,抽到的不是第 j 筆觀測值的機率是多少?第二筆呢?

(A) 兩次都是 (n−1)/n
(B) 第一次 (n−1)/n,第二次 (n−2)/(n−1)
(C) 第一次 1/n,第二次 1/n
EXERCISE 2 · ISLP 5.4 第 3 題(b)

相對於驗證集法,k-fold CV 的優點是什麼?

(A) 偏差較小(每輪用 n(k−1)/k 筆訓練,多於 n/2),而且做 k 輪平均後變異也較小
(B) k-fold CV 完全沒有隨機性,驗證集法有
(C) k-fold CV 不需要把資料切開,所以能用到全部資料訓練
EXERCISE 3 · ISLP 5.4 第 8 題

課本第 8 題在模擬資料上跑 degree 1 到 4 的 LOOCV,真實模型是二次的。預期會看到什麼?

(A) degree 1 到 2 誤差大幅下降,之後幾乎不再改善(甚至微幅上升)
(B) 誤差隨 degree 單調下降,degree 4 最低
(C) 四個 degree 的 LOOCV 誤差幾乎相同,因為 LOOCV 對模型複雜度不敏感
EXERCISE 4 · ISLP 5.4 第 9 題

課本第 9 題要對 Bostonmedv 平均值做 bootstrap,並跟公式解 $\mathrm{{SE}}(\bar\mu) = s/\sqrt{{n}}$ 比較。預期結果是?

(A) 兩個數字會很接近,因為 bootstrap 不需要公式也能重現公式給的答案
(B) bootstrap 的 SE 會明顯較小,因為它重複用了同一份資料
(C) 無法比較,因為 bootstrap 只能用在迴歸係數上
REFERENCE · 總覽

重抽樣方法速查表 ISLP Ch.5

考前把這一頁掃過去就好。

四種方法對照

方法在估什麼偏差變異算力隨機性典型用途
驗證集法測試誤差大(高估)有,很大快速看一眼
LOOCV測試誤差最小小資料、線性有捷徑
k-fold CV測試誤差有,較小選模型的標準做法
Bootstrap估計量的 SE有,B 大就穩估不確定性

Auto 資料上的實測數字

degree123510
LOOCV24.2319.2519.3419.0319.91
10-fold CV24.2119.1919.2819.1419.87
驗證集(seed 0)23.6218.7618.8018.4519.07

degree 1 的 LOOCV = 24.2315,跟 lab 儲存格 32 的 np.float64(24.23151351792922) 相符。三列都在 degree 2 之後就拉平了。

公式速查

名稱式子備註
LOOCV$\mathrm{CV}_{(n)} = \frac1n\sum_i \mathrm{MSE}_i$式 5.1
LOOCV 捷徑(最小平方)$\frac1n\sum_i\left(\frac{y_i-\hat y_i}{1-h_i}\right)^2$式 5.2,只配一次模型
k-fold$\mathrm{CV}_{(k)} = \frac1k\sum_j \mathrm{MSE}_j$式 5.3
分類版$\mathrm{Err}_j = \frac{1}{|C_j|}\sum_{i\in C_j} I(y_i \ne \hat y_i)$式 5.4
最小變異配置$\alpha = \frac{\sigma_Y^2-\sigma_{XY}}{\sigma_X^2+\sigma_Y^2-2\sigma_{XY}}$式 5.7
在 bootstrap 樣本裡的機率$1-(1-1/n)^n \to 1-e^{-1} \approx 0.632$OOB 的來源
三個一定要記住的觀念 1. 交叉驗證估「預測誤差」,bootstrap 估「估計量的不確定性」。 兩者都在重抽樣,但問的問題不同,不要混用。
2. k = 5 或 10 是偏差、變異、算力三方的折衷。 LOOCV 偏差最小但各折高度相關、變異大。
3. 任何用到 y 的步驟都要關在折裡面。 特徵篩選、標準化、填補、過抽樣、目標編碼——包進 Pipeline 就對了。

本頁「預期輸出」逐字取自課程 lab notebook(老師在課程環境實跑);圖表用的烘焙資料由 tools/frames/ 在固定種子下產生,環境為 numpy 1.24.4 · pandas 2.3.2 · scikit-learn 1.6.1 · scipy 1.13.1 · statsmodels 0.14.2 · ISLP 0.4.0 · pygam 0.10.1。每張卡下方的「來源」標了 lab 的儲存格編號,可以直接回去對。

CARDS · 關鍵詞彙卡

關鍵詞彙卡:點卡片翻面 課程題庫 · 23 張

詞彙卡取自本章講義與 ISLP 第 5 章,正面是中文術語(附英文原名)。 先看正面、心裡默想定義,再翻面對答案;洗牌後再過一輪,直到每張都能不看答案講出來。