① 照節次讀:每節先讀說明,動手玩互動元件——先預測結果,再按按鈕驗證。 ② 對照講義:每個 §徽章都標了 ISLP 節號與講義頁碼,細節與完整推導請回講義與課本。 ③ 每節做 quiz:答錯就回到該節重讀,不要往下跳;錯的選項也寫了「錯在哪」。 ④ 最後翻關鍵詞彙卡自測術語,並用 REF 總覽當速查表。
第 3 章的最小平方法很好用:沒有調整參數、有封閉解、係數可以直接解讀。 這一章不是要換掉它,而是要問一句話:當變數很多的時候,「把全部變數丟進去做最小平方」 還是最好的做法嗎?答案通常是不。
問題出在兩個地方。第一是預測準確度:當 p 逼近 n,最小平方的估計會非常不穩定 (變異很大);當 p > n,它連唯一解都沒有——你可以找到無限多組係數把訓練誤差壓到 0, 而它們在新資料上都爛得一樣。第二是可解讀性:一堆跟 y 沒關係的變數留在模型裡, 最小平方幾乎不可能把它們的係數估成剛好 0,於是你得到一個沒人看得懂的模型。
三類方法都在做同一件事:用一點偏差換一大塊變異。最小平方是無偏的, 但在 p 大的時候變異大到讓它毫無用處;只要願意接受一點偏差,測試誤差往往降得很明顯。 整章的節奏都是這個交換。
$$\text{RSS} = \sum_{i=1}^{n}\left(y_i - \beta_0 - \sum_{j=1}^{p}\beta_j x_{ij}\right)^2$$下面這張表先放在這裡,讀完整章再回來看一次會比較有感覺。
| 留幾個變數 | 係數會剛好是 0 嗎 | 有調整參數嗎 | 座標系 | 主要弱點 | |
|---|---|---|---|---|---|
| 最小平方 | 全部 p 個 | 幾乎不會 | 沒有 | 原始變數 | p 大時變異爆炸 |
| 子集選擇 | k 個(自己選) | 沒被選的就是 0 | k | 原始變數 | 搜尋空間太大、不穩定 |
| Ridge(L2) | 全部 p 個 | 不會 | λ | 原始變數 | 不做變數選擇 |
| Lasso(L1) | 由 λ 決定 | 會 | λ | 原始變數 | 相關變數之間的選擇很不穩 |
| PCR / PLS | 全部 p 個都參與 | 不會 | M | 換過的座標 | 新座標不好解讀 |
為什麼 p 逼近 n 的時候,「把全部變數丟進去做最小平方」是個壞主意?
最直白的想法:每一種變數組合都試一次,挑最好的。這就是最佳子集選擇 (best subset selection)。p 個變數有 2p 種組合(含空模型), 演算法分兩階段——先在每個大小 k 裡挑出訓練 RSS 最小的 Mk, 再從 M0, …, Mp 裡挑一個。
$$\text{總共要配} \; \sum_{k=0}^{p} \binom{p}{k} = 2^{p} \; \text{個模型}$$2p 長得太快,p 超過 40 就算不完了。前向逐步選擇 (forward stepwise selection)改成貪婪地走:從空模型開始,每一步加一個「讓 RSS 降最多」的變數。 後向逐步選擇(backward stepwise selection)反過來,從全模型開始每次砍一個。 兩者都只要配
$$1 + \sum_{k=0}^{p-1} (p-k) = 1 + \frac{p(p+1)}{2} \; \text{個模型}$$p = 20 時這是 211 個,而最佳子集是 1,048,576 個。
代價是:貪婪走法不保證找到最佳子集。下面第二張圖用真實的 Credit
資料把這件事畫出來。
Balance ~ Limit + Rating + Cards + Student 的 16 種組合),x 是變數個數、y 是訓練 RSS。紅線是每個大小的最佳子集(ISLP 圖 6.1 的紅色前緣),綠線是 forward 走出來的路。
Credit 上,forward 第一步選 Rating、
第二步加 Student——到這裡都跟最佳子集一樣。但三變數的最佳子集是
Limit + Cards + Student(RSS 15.23 百萬),forward 卻只能給
Rating + Cards + Student(15.51 百萬)。原因是 forward 一旦選了
Rating 就不能再把它丟掉,而 Limit 與
Rating 幾乎共線,最佳子集會拿 Limit 換掉 Rating。
ISLP 表 6.1 在完整的 11 個變數上是同一個現象(第四個模型開始分歧)。
(263, 20)
59 名球員的 Salary 是缺失的,dropna() 之後剩 263 列、20 欄(19 個預測變數+Salary)。整章的 Hitters 結果都建立在這 263 筆上。
Ch06-varselect-lab-zh.ipynb · 儲存格 12、13、15
1 ['CRBI'] 2 ['CRBI', 'Hits'] 3 ['CRBI', 'Hits', 'PutOuts'] 4 ['CRBI', 'Hits', 'PutOuts', 'Division[W]'] 5 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat'] 6 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks'] 7 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks'] 8 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns'] 9 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat'] 10 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists'] 11 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]'] 12 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs'] 13 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs', 'Errors'] 14 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs', 'Errors', 'HmRun'] 15 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs', 'Errors', 'HmRun', 'CHits'] 16 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs', 'Errors', 'HmRun', 'CHits', 'RBI'] 17 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs', 'Errors', 'HmRun', 'CHits', 'RBI', 'NewLeague[N]'] 18 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs', 'Errors', 'HmRun', 'CHits', 'RBI', 'NewLeague[N]', 'Years'] 19 ['CRBI', 'Hits', 'PutOuts', 'Division[W]', 'AtBat', 'Walks', 'CWalks', 'CRuns', 'CAtBat', 'Assists', 'League[N]', 'Runs', 'Errors', 'HmRun', 'CHits', 'RBI', 'NewLeague[N]', 'Years', 'CHmRun']
讀這段輸出的正確方式是看每一行是不是前一行的超集合:CRBI → 加 Hits → 加 PutOuts…一路只加不減。這正是前向逐步的定義,也正是它可能錯過最佳子集的原因。
Ch06-varselect-lab-zh.ipynb · 儲存格 33、34
np.int64(7)
bic_b 是照 models2 的索引順序(1、2、…、19)收集的,0-based 的 argmin() = 7 指的是第 8 個元素,也就是 8 變數模型;而前向那邊(下一節)是 6 變數。同一份資料、同一個準則,兩個搜尋方向給出不同大小的模型——講義第 42 頁講的就是這件事。
Ch06-varselect-lab-zh.ipynb · 儲存格 40、41
| 搜尋法 | 要配幾個模型 | p = 20 時 | n < p 能用嗎 | 保證找到最佳子集嗎 |
|---|---|---|---|---|
| 最佳子集 | 2p | 1,048,576 | 不能(最多到 n−1 個變數) | 會 |
| 前向逐步 | 1 + p(p+1)/2 | 211 | 可以 | 不保證 |
| 後向逐步 | 1 + p(p+1)/2 | 211 | 不能(要先配全模型) | 不保證 |
| 混合逐步 | 略多於逐步 | 約 211+ | 看實作 | 不保證(但比較接近) |
同一份資料上做最佳子集、前向逐步、後向逐步。大小同樣是 k 的三個模型裡,哪一個的訓練 RSS 最小?
第二階段的問題是:怎麼比較「大小不同」的模型?訓練 RSS 與 R² 不能用, 因為它們單調偏好大模型。兩條路——間接(把訓練誤差加一個懲罰項,修掉它的樂觀偏差) 與直接(用驗證集或交叉驗證真的去估測試誤差,下一節講)。
間接法的四個常客。設模型有 d 個預測變數、σ̂² 是用全模型估出來的誤差變異數:
$$C_p = \frac{1}{n}\left(\mathrm{RSS} + 2 d \hat\sigma^2\right), \qquad \mathrm{BIC} = \frac{1}{n}\left(\mathrm{RSS} + \log(n)\, d \hat\sigma^2\right)$$ $$\text{Adjusted } R^2 = 1 - \frac{\mathrm{RSS}/(n-d-1)}{\mathrm{TSS}/(n-1)}$$兩個公式長得幾乎一樣,只差在每多一個變數要罰多少:Cp 罰 2σ̂²,
BIC 罰 log(n)·σ̂²。因為 n > 7 就有 log n > 2,BIC 罰得比 Cp 重,所以偏好更小的模型。
n 愈大這個差距愈誇張——Credit 的 n = 400,log 400 ≈ 5.99,BIC 的懲罰是 Cp 的三倍。
statsmodels 的 .aic)用的是 log-likelihood 版
−2·log L + 2k,它跟 Cp 不是單調對應——所以下圖把它畫成獨立一條線。
兩個版本都叫 AIC,看到數字差很多不要慌,先問是哪一個公式。
| 準則 | 式子(最小平方) | 每多一個變數罰多少 | n=400 時的懲罰 | Credit 選出 |
|---|---|---|---|---|
| Cp | (RSS + 2dσ̂²)/n | 2σ̂² | 2σ̂² | 6 個 |
| AIC(課本) | RSS + 2dσ̂² | 2σ̂² | 與 Cp 成正比 | 同 Cp |
| BIC | (RSS + log(n)·dσ̂²)/n | log(n)·σ̂² | 5.99σ̂² | 4 個 |
| 調整後 R² | 1 − [RSS/(n−d−1)] / [TSS/(n−1)] | 分母的 n−d−1 變小 | 很輕 | 7 個 |
| 交叉驗證 | 直接估測試誤差 | 不需要 σ̂² | — | 6 個 |
三個都是「訓練 RSS + 一個隨模型變大而變大的懲罰」。差別只在懲罰的係數:Cp 與課本版 AIC 都是 $2d\hat\sigma^2$,BIC 是 $\log(n)\, d\hat\sigma^2$。$\log n > 2$ 對任何 $n > 7$ 都成立($e^2 \approx 7.39$),所以 BIC 一律罰得比較重,選出來的模型一律比較小或一樣大。n = 400 時 $\log n \approx 5.99$,懲罰差三倍。
背後的動機不同。Cp 是估測試 MSE:可以證明只要 $\hat\sigma^2$ 是 $\sigma^2$ 的無偏估計,Cp 就是測試 MSE 的無偏估計。BIC 是從貝氏觀點來的,它在近似「這個模型是真模型的後驗機率」。所以兩者的目標本來就不一樣:Cp/AIC 想預測得準,BIC 想找出正確的模型。如果真模型確實在候選清單裡,n 夠大時 BIC 會挑中它(一致性);AIC 不保證,它會傾向多留幾個變數。
實務上的建議:目的是預測就用 CV(或 AIC/Cp),目的是「哪些變數真的有關」就參考 BIC。而且兩者都要求你估得出 $\hat\sigma^2$——這在 p > n 的時候直接破功(最後一節會講)。
Cp、AIC、BIC 都有明確的推導:Cp 是測試 MSE 的無偏估計,AIC 來自 Kullback–Leibler 距離的漸近論證,BIC 來自後驗機率的 Laplace 近似。調整後 R² 沒有這種東西——它就是「把 RSS 除以 $n-d-1$ 而不是 n」這個直覺上合理的修正。
它的直覺是對的:真正有用的變數都進來以後,再加雜訊變數只會讓 RSS 降一點點,但 $d$ 變大會讓分母 $n-d-1$ 變小,兩者相抵之下 $\mathrm{RSS}/(n-d-1)$ 會變大、調整後 R² 會下降。問題是「一點點」有多點沒有理論刻度,所以它的懲罰強度是任意的——在 Credit 上它比 Cp 還鬆(選 7 個 vs 6 個)。
順便記一件事:調整後 R² 在 p > n 時可以輕易做到 1,所以高維度下它完全不能用。
np.float64(3812.2130777982)
argmin() 回傳 5,而 bic_f 是 0-based 的 list,所以指的是清單的第 6 個元素——6 變數模型,BIC = 3812.21。注意下一格的 models1.model[5] 用的是 pandas 的標籤索引,取到的是 5 變數模型(輸出列出 5 個特徵)。同一個 5,兩種索引意義完全不同,讀別人的程式碼時要分清楚。
Ch06-varselect-lab-zh.ipynb · 儲存格 36、37
('AtBat',
'Hits',
'Walks',
'CAtBat',
'CRuns',
'CRBI',
'CWalks',
'League[N]',
'Division[W]',
'PutOuts',
'Assists')這是 adjust_r2() 的定義(就是式 6.4)加上在最佳子集前緣上取最大值。Hitters 上調整後 R² 選出 11 個變數;BIC 選 6 個。兩個準則差了 5 個變數——這不是誰算錯,是兩個準則的懲罰強度本來就不同。
Ch06-varselect-lab-zh.ipynb · 儲存格 97、98、101
同一份資料上,BIC 選出的模型大小,跟 Cp 選出的比起來?
Cp、AIC、BIC 都需要估 $\hat\sigma^2$,而在 p 接近 n 的時候那個估計本身就不可靠。 交叉驗證不需要 $\hat\sigma^2$、不需要知道自由度、也不需要假設模型是對的—— 它直接估測試誤差。所以只要算得動,CV 是首選。
做法就是第 5 章那一套,只是把「模型」換成「模型大小」:對每個大小 k, 在每一折的訓練部分做子集選擇、在驗證折上算誤差。 注意選變數這件事必須關在折裡面——這正是第 5 章 P06 講的那個錯誤。
Pipeline(steps=[('scaler', StandardScaler()),
('ridge', Ridge(alpha=np.float64(2.494832928915928)))])Pipeline 是關鍵:把標準化包進去,GridSearchCV 就會在每一折的訓練部分重新 fit scaler,而不是用全部資料的平均與標準差。這一步做錯,CV 誤差會偏低而且不會報錯。
Ch06-varselect-lab-zh.ipynb · 儲存格 134
三個理由。第一,算力:最佳子集有 $2^p$ 個模型,每個都跑 k-fold 是 $k \cdot 2^p$ 次配適;而 Cp 只要一次配適加一個修正項。第二,這三個準則會出現在別人的論文與報表裡,你得看得懂。第三,它們解釋了「懲罰複雜度」這個想法的來源——AIC 從 KL 散度來、BIC 從後驗機率來,各有各的目標。
但如果你只是要挑一個模型來預測,而且資料量算得動:用 CV。
會,而且那是刻意的。它的立場是:在預測能力分不出差別時,偏好簡單。
這個偏好有實際理由——變數少的模型更好解釋、需要蒐集的資料更少、上線後更不容易因為某個變數的定義改變而壞掉。如果你的目標純粹是最小化預測誤差而完全不在意這些,那就選最低點;但要記得那個「最低」在下一份資料上很可能換人。
CV 誤差在模型大小 4、5、6、7 上分別是 54100、53900、53800、53850,而最小值的標準誤是 6000。one-SE 規則會選哪一個?
子集選擇是離散的:一個變數要嘛在、要嘛不在。這讓它變異很大—— 資料動一點,選出來的變數就換一批。另一條路是留下全部變數,但把係數往零壓。
Ridge 在原本的 RSS 上加一個 L2 懲罰:
$$\sum_{i=1}^{n}\left(y_i - \beta_0 - \sum_{j=1}^{p}\beta_j x_{ij}\right)^2 + \lambda \sum_{j=1}^{p} \beta_j^2 \;=\; \mathrm{RSS} + \lambda \|\beta\|_2^2$$$\lambda = 0$ 就是最小平方;$\lambda \to \infty$ 所有係數趨近 0。 注意截距 $\beta_0$ 不罰——罰它等於在乎你把 y 的原點放在哪裡。
np.float64(160.42371017725918)
ElasticNet 的 l1_ratio=0 就是純 Ridge。係數的 L2 範數會隨 λ 單調下降——這個數字就是上圖 x 軸的分子。
Ch06-varselect-lab-zh.ipynb · 儲存格 122、124
Pipeline(steps=[('scaler', StandardScaler()),
('ridge',
RidgeCV(alphas=array([2.22093791e+05, 1.76005531e+05, 1.39481373e+05, 1.10536603e+05,
8.75983676e+04, 6.94202082e+04, 5.50143278e+04, 4.35979140e+04,
3.45506012e+04, 2.73807606e+04, 2.16987845e+04, 1.71959156e+04,
1.36274691e+04, 1.07995362e+04, 8.55844774e+03, 6.78242347e+03,
5.37495461e+03, 4.25955961e+03, 3.375...
1.84386167e-03, 1.46122884e-03, 1.15799887e-03, 9.17694298e-04,
7.27257037e-04, 5.76338765e-04, 4.56738615e-04, 3.61957541e-04,
2.86845161e-04, 2.27319885e-04, 1.80147121e-04, 1.42763513e-04,
1.13137642e-04, 8.96596467e-05, 7.10537367e-05, 5.63088712e-05,
4.46238174e-05, 3.53636122e-05, 2.80250579e-05, 2.22093791e-05]),
cv=KFold(n_splits=5, random_state=0, shuffle=True)))])RidgeCV 把「掃 λ + 交叉驗證」包成一步。注意 alphas 要給一整排值,而且照慣例是由大到小掃,因為暖啟動(warm start)從收縮較重的解開始比較快收斂。
Ch06-varselect-lab-zh.ipynb · 儲存格 144、145
scikit-learn 的解法是
Pipeline([('scaler', StandardScaler()), ('ridge', Ridge())]),
順便解決了 CV 的洩漏問題。
把某個預測變數的單位從「元」改成「千元」(數值除以 1000)。Ridge 的預測會變嗎?
Ridge 的缺點是:它留下全部 p 個變數。p = 500 的時候你得到 500 個很小但不為零的係數, 模型一點也不好解釋。Lasso 把 L2 換成 L1:
$$\mathrm{RSS} + \lambda \sum_{j=1}^{p} |\beta_j| \;=\; \mathrm{RSS} + \lambda \|\beta\|_1$$只差一個平方,行為完全不同:λ 夠大時 Lasso 會把一部分係數壓成剛好 0, 於是它同時做了收縮與變數選擇。這種解叫做稀疏(sparse)。
把兩者寫成等價的約束型式($s$ 是預算,跟 $\lambda$ 一對一對應):
$$\min_{\beta} \mathrm{RSS} \quad \text{s.t.} \quad \|\beta\|_1 \le s \;\;(\text{Lasso,菱形}) \qquad\text{或}\qquad \|\beta\|_2^2 \le s \;\;(\text{Ridge,圓})$$RSS 的等高線是以 $\hat\beta^{\mathrm{OLS}}$ 為中心的同心橢圓。解就是 橢圓第一次碰到約束區域的那一點。菱形有尖角而且尖角剛好落在座標軸上, 所以碰撞很容易發生在尖角——那裡有一個座標是 0。圓沒有尖角,碰到座標軸是機率 0 的事。
array([-210.01008773, 243.4550306 , 0. , 0. ,
0. , 97.69397357, -41.52283116, -0. ,
0. , 39.62298193, 205.75273856, 124.55456561,
-126.29986768, 15.70262427, -59.50157967, 75.24590036,
21.62698014, -12.04423675, -0. ])注意輸出裡有剛好等於 0 的係數——那不是「很小」,是真的 0。這是 Lasso 跟 Ridge 最實際的差別:你可以直接說「這些變數被模型丟掉了」。
來源:Ch06-varselect-lab-zh.ipynb · 儲存格 162、172
不一定,取決於真實的係數結構。
如果真的只有少數幾個變數有用(真實係數稀疏),Lasso 佔優勢——它能把沒用的丟掉,省下估計它們所花的變異。如果很多變數都有小小的貢獻(係數密集),Ridge 通常較好——Lasso 會硬把一些真的有用的變數歸零,付出偏差的代價。
而你事前並不知道是哪一種。實務作法是兩個都跑、用 CV 比較,或直接用 elastic net(同時放 L1 與 L2 懲罰)讓資料自己決定混合比例。
不能。Lasso 的變數選擇沒有附帶 p 值,而且「先用資料選變數、再對選出來的變數做 t 檢定」會嚴重高估顯著性——那是同一份資料用了兩次,跟第 5 章的 CV 誤用是同一個病。
要做選後推論(post-selection inference)需要專門的方法(selective inference、debiased lasso 等),已經超出本課範圍。實務上的誠實說法是:「在這個 λ 之下,Lasso 保留了這些變數」,而不是「這些變數顯著」。
傾向隨機留一個、丟另一個——因為留一個就夠解釋,留兩個要多付一份 L1 懲罰。
問題是「留哪一個」很不穩定,資料動一點就換人。這在解釋上很危險:你可能報告「基因 A 重要、基因 B 不重要」,而重跑一次結論就反過來。
Ridge 相反,它會讓相關的變數平分係數。想要「同進同出」的行為,elastic net 或 group lasso 是更合適的工具。
為什麼 L1 約束區域(菱形)會讓解落在座標軸上,而 L2(圓)不會?
λ 是調整參數(tuning parameter),不是從資料估出來的參數。 選它的方法跟第 5 章選多項式次數完全一樣:掃一排候選值,用交叉驗證比較。
Credit 資料上 Lasso 的 CV 選出 λ = 364.76, 在那個 λ 之下 11 個變數全部還活著——這份資料的係數並不稀疏, 所以 Lasso 沒有真的丟掉東西。這是個誠實的結果,不是失敗: Lasso 沒有義務一定要歸零。
np.logspace(-2, 5, 100) 或 10**np.linspace(a, b, 100)
才是對的做法。
np.float64(114690.73118253677)
mse_path_ 的形狀是(λ 個數 × 折數),.mean(1) 先對折取平均、再取最小值,就是 CV 曲線的最低點。
Ch06-varselect-lab-zh.ipynb · 儲存格 162、168
用 CV 選好 λ 之後,最終交出的模型應該是?
第三條路:不動變數,改換座標。先把 p 個相關的預測變數壓成 M 個互不相關的方向(M ≪ p),再對這 M 個方向做最小平方。
主成分迴歸(principal components regression, PCR)用的方向就是第 12 章的主成分: 第一主成分是 X 變異最大的方向,第二個是在跟第一個正交的條件下變異最大的方向,依此類推。
$$Z_m = \sum_{j=1}^{p} \phi_{jm} X_j, \qquad y_i = \theta_0 + \sum_{m=1}^{M} \theta_m z_{im} + \varepsilon_i$$M 是調整參數,同樣用 CV 選。M = p 時 PCR 就退回最小平方(只是換了個座標); M 小的時候,被丟掉的那些低變異方向就是被收縮掉的部分。
GridSearchCV(cv=KFold(n_splits=5, random_state=0, shuffle=True),
estimator=Pipeline(steps=[('scaler', StandardScaler()),
('pca', PCA(n_components=2)),
('linreg', LinearRegression())]),
param_grid={'pca__n_components': range(1, 20)},
scoring='neg_mean_squared_error')pca__n_components 這種雙底線寫法是 Pipeline 的參數命名慣例:步驟名 + __ + 該步驟的參數名。標準化同樣包在 pipeline 裡,PCA 必須在標準化之後做——不然變異數大的變數會主宰第一主成分。
Ch06-varselect-lab-zh.ipynb · 儲存格 180、182
array([0.3831424 , 0.21841076])
explained_variance_ratio_ 就是第 12 章的 PVE。累加起來可以回答「留幾個主成分才夠」。
Ch06-varselect-lab-zh.ipynb · 儲存格 188
不算。每一個主成分都是全部 p 個原始變數的線性組合,所以就算你只留 M = 2 個主成分,最終模型仍然用到了每一個原始變數。
這是 PCR 跟 Lasso 最重要的差別:Lasso 給你「這 5 個變數有用、其餘丟掉」;PCR 給你「這 2 個方向有用」,而每個方向都攪拌了所有變數。後者在解釋上通常更難——你得去看 loadings 才知道那個方向大致代表什麼。
因為主成分是「變異最大的方向」,而變異數的大小完全取決於單位。
Credit 資料裡 Limit 的數值是幾千、Cards 是個位數。不標準化的話,第一主成分幾乎就等於 Limit 本身——不是因為它最重要,而是因為它的單位最大。第 12 章的 USArrests biplot 有更戲劇性的例子。
PCR 用 M = 2 個主成分。最終模型用到了幾個原始預測變數?
PCR 挑方向時完全沒看 y。這有點浪費——我們明明知道 y 是什麼。
偏最小平方(partial least squares, PLS)修掉這一點:第一個方向的權重直接取 $\phi_{j1} \propto$ 每個 $X_j$ 與 $y$ 的簡單線性迴歸係數, 也就是跟 y 相關性愈強的變數,權重愈大。 取完第一個方向後,把各變數對它迴歸取殘差,再在殘差上重複同樣的步驟得到第二個方向。
上一節那個元件就是在演這件事:拖滑桿改變 y 的方向,PLS1 跟著轉、PC1 不動。
| 方向怎麼挑 | 有沒有用到 y | 做變數選擇嗎 | 何時較好 | |
|---|---|---|---|---|
| PCR | X 變異最大的方向 | 沒有 | 沒有 | X 的主要變異方向剛好跟 y 有關時 |
| PLS | 跟 y 相關性最強的方向 | 有 | 沒有 | X 的大變異方向跟 y 無關時 |
| Ridge | 不換座標,全部收縮 | 有(配適時) | 沒有 | 很多變數都有小貢獻 |
| Lasso | 不換座標,部分歸零 | 有(配適時) | 有 | 真實係數稀疏 |
實務上 PLS 的表現常常跟 PCR 差不多,有時還略差。原因是: 它雖然降低了偏差(方向跟 y 有關),但也增加了變異(方向是估出來的,而且用到了 y)。 ISLP §6.3.2 的結論就是這樣:PLS 沒有一致地贏過 PCR 或 Ridge。
GridSearchCV(cv=KFold(n_splits=5, random_state=0, shuffle=True),
estimator=PLSRegression(),
param_grid={'n_components': range(1, 20)},
scoring='neg_mean_squared_error')PLSRegression 的 n_components 跟 PCR 的 n_components 是同一個角色的調整參數,一樣用 CV 選。注意它預設 scale=True,已經幫你標準化了。
Ch06-varselect-lab-zh.ipynb · 儲存格 192、194
PLS 相對於 PCR 的差別是什麼?
基因資料、文字資料、感測器資料常常是 p 遠大於 n: 幾萬個變數、幾百個樣本。這時候最小平方不只是「表現不好」,而是徹底失效。
當 p ≥ n 時,最小平方能找到一組係數完美通過每一個訓練點—— 殘差全部是 0、R² 剛好等於 1。這不是模型好,而是自由度用完了: n 個方程式、p ≥ n 個未知數,解不唯一而且必然過度配適。
詛咒的是額外的變數不是免費的。
直覺上多一個變數只會提供更多資訊,最壞也就是沒用。但實際上每個變數都要花自由度去估它的係數,而估計本身帶進變異。一個跟 y 完全無關的變數,它的係數估計值不會剛好是 0,而是一個隨機的小數字——那個隨機性直接進到預測裡。
下面那張圖就是這件事:真正有用的變數固定是 20 個,把候選池從 20 擴到 2000 之後測試 MSE 從 1.34 升到 7.54。雜訊不會互相抵消,它會累加。
這一章的三類工具全部都是為此設計的:子集/逐步選擇(明確減少變數)、收縮(Ridge 與 Lasso,Lasso 還順便選變數)、降維(PCR 與 PLS)。它們的共同點是降低變異,代價是接受一點偏差。
在 p ≫ n 的場景,Lasso 與 elastic net 特別受歡迎,因為稀疏假設常常合理(幾萬個基因裡真的有關的通常只有少數幾個),而且結果可以直接列出「這幾個變數」。
n = 50、p = 500 的資料,用最小平方配適。訓練 R² 大約會是多少?
下面幾題取自 ISLP §6.6 的課後習題,題號都對得回課本。先自己想過再點選項; 每個選項——包含錯的——都寫了為什麼。想看完整解答再對照下面3個站。
最佳子集、forward stepwise、backward stepwise 各自選出的「k 個變數的最佳模型」中,測試誤差最低的會是哪一個?
相對於最小平方,Lasso 的特性是?
Ridge 的懲罰參數 λ 從 0 開始往上增加。訓練 RSS 會怎麼變化?
課本第 9 題在 College 資料上比較最小平方、Ridge、Lasso、PCR、PLS 的測試誤差。預期會看到什麼?
考前把這一頁掃過去就好。
| 做什麼 | 調整參數 | 會歸零嗎 | 算力 | 何時最適合 | |
|---|---|---|---|---|---|
| 最佳子集 | 搜遍 2^p 個模型 | 模型大小 k | 會(沒選就是 0) | 2^p,p > 40 不可行 | p 很小 |
| Forward/backward | 貪婪地一次加/減一個 | 模型大小 k | 會 | 1 + p(p+1)/2 | p 中等 |
| Ridge(L2) | 全部係數一起收縮 | λ | 不會 | 很快,有封閉解 | 很多變數都有小貢獻 |
| Lasso(L1) | 收縮 + 部分歸零 | λ | 會 | 很快(座標下降) | 真實係數稀疏 |
| PCR | 換成 M 個非監督式方向 | M | 不會(用到全部變數) | 很快 | X 的大變異方向跟 y 有關 |
| PLS | 換成 M 個監督式方向 | M | 不會 | 很快 | X 的大變異方向跟 y 無關 |
| 準則 | 選出的大小 | 懲罰項 | 備註 |
|---|---|---|---|
| Cp | 6 | $+2d\hat\sigma^2$ | 測試 MSE 的無偏估計 |
| AIC | 6 | 常態誤差下與 Cp 等價 | 從 KL 散度來 |
| BIC | 4 | $+\log(n)\,d\hat\sigma^2$ | n > 7 時罰得比 Cp 重,偏好小模型 |
| 調整後 R² | 7 | 分母帶 $n-d-1$ | 罰得最輕,選最大的模型 |
| 10-fold CV | 6 | 無(直接估測試誤差) | 不需要 $\hat\sigma^2$ |
| CV + one-SE | 4 | 同上,再取門檻內最簡單的 | 跟 BIC 一致 |
n = 400、p = 11、$\hat\sigma^2$ = 9760。 Cp 選 6 個、BIC 選 4 個、調整後 R² 選 7 個,與 ISLP 圖 6.2 的數字相符。 最佳子集與 forward stepwise 在大小 3 之後就開始選出不同的變數組合。
| 名稱 | 式子 | 備註 |
|---|---|---|
| Cp | $\frac{1}{n}(\mathrm{RSS} + 2d\hat\sigma^2)$ | 式 6.2 |
| AIC | $\frac{1}{n\hat\sigma^2}(\mathrm{RSS} + 2d\hat\sigma^2)$ | 式 6.3,常態誤差下與 Cp 等價 |
| BIC | $\frac{1}{n}(\mathrm{RSS} + \log(n)\,d\hat\sigma^2)$ | 式 6.3,$\log n > 2$ 時罰得比 Cp 重 |
| 調整後 R² | $1 - \frac{\mathrm{RSS}/(n-d-1)}{\mathrm{TSS}/(n-1)}$ | 式 6.4 |
| Ridge | $\mathrm{RSS} + \lambda\sum_j \beta_j^2$ | 式 6.5,等價於 $\|\beta\|_2^2 \le s$ |
| Lasso | $\mathrm{RSS} + \lambda\sum_j |\beta_j|$ | 式 6.7,等價於 $\|\beta\|_1 \le s$ |
| 主成分方向 | $Z_m = \sum_j \phi_{jm} X_j$ | 式 6.16,$\phi$ 由 X 的變異決定 |
本頁「預期輸出」逐字取自課程 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 的儲存格編號,可以直接回去對。
詞彙卡取自本章講義與 ISLP 第 6 章,正面是中文術語(附英文原名)。 先看正面、心裡默想定義,再翻面對答案;洗牌後再過一輪,直到每張都能不看答案講出來。