兩套 API、兩種目的:statsmodels 與 scikit-learn

先備知識 P6 — 選讀,不列入評分
MS 設計矩陣|sm.OLS(y, X).fit()|summarize 的四欄|fit-predict-score|train_test_split|cross_validate|先切分再標準化
向下捲動開始互動↓
📌 本頁使用方式(先備 · 建模 API)

① 依序閱讀:每節先讀說明;遇到互動元件時,先預測結果,再操作驗證。 ② 對照程式範例:程式碼卡下方的「來源」標了課程練習筆記本(lab);需要實作時,再打開原始筆記本對照。 ③ 檢查理解:答錯時先看回饋,再回到相關說明;標為延伸的內容可留待第二輪。 ④ 最後翻關鍵詞彙卡自測術語,並用 重點速查與來源查閱。

教科書:An Introduction to Statistical Learning with Applications in Python(ISLP)。章節旁的來源標記可點到本頁書目。

CONTENTS · 內容目錄
PROLOGUE · 開場

要看係數用 statsmodels,要做預測用 scikit-learn 課程 Lab Ch3 · lab 範例

Python 做統計建模有兩套主流工具,這門課兩套都會用到。它們算的東西常常一模一樣, 但回答的問題不同:statsmodels 給你係數、標準誤與 p 值, 是為了讓你解釋;scikit-learn 給你 fit/predict/ score,用來預測並核對模型表現。

比較同一模型的兩套輸出 同一個模型在兩套 API 底下係數完全一樣——課程 lab 裡 statsmodels 給 34.5538 與 −0.9500,sklearn 給 34.5538408793831 與 −0.95004935,對 lstat = 5、10、15 的預測值逐位相同。因此,這裡可以依需要的輸出選擇工具。
›四個步驟,看兩套 API 各自怎麼寫。
課程/lab 資料
按按鈕比較
同一件事在兩套 API 的寫法。注意 statsmodels 要你自己把截距欄放進 X,sklearn 則是用 fit_intercept=True 幫你處理。
這一步
statsmodels—
scikit-learn—
差在哪—
什麼時候用哪一套
要報告係數與顯著性(第 3、4 章)→ statsmodels。
要比較模型、做交叉驗證、串前處理(第 5、6 章之後)→ scikit-learn。
兩者可以混用:課程 lab 就用 sklearn_sm 把 statsmodels 的模型包成 sklearn 的估計器,好丟進 cross_validate。
statsmodels:截距要自己放進 X
X = pd.DataFrame({'intercept': np.ones(Boston.shape[0]), 'lstat': Boston['lstat']}) X[:4]
預期輸出
   intercept  lstat
0        1.0   4.98
1        1.0   9.14
2        1.0   4.03
3        1.0   2.94

全是 1 的 intercept 欄用來表示截距。sm.OLS 不會自動加截距,忘了就是擬合一條過原點的線。

來源:Ch03-linreg-lab-zh.ipynb
擬合之後的係數表
summarize(results)
預期輸出
              coef  std err       t  P>|t|
intercept  34.5538    0.563  61.415    0.0
lstat      -0.9500    0.039 -24.528    0.0

四個欄位:係數、標準誤、t 值、p 值。下一節逐欄拆開講。

來源:Ch03-linreg-lab-zh.ipynb
PART 00 · 自我檢測

你要在論文裡報告「每增加一個百分點的 lstat,medv 平均下降多少,這個估計有多確定」。該用哪一套?

(A) statsmodels
(B) scikit-learn,因為它比較新
(C) 兩套都可以,反正係數一樣
PART 01 · 設計矩陣

把資料整理成設計矩陣 X 課程 Lab Ch3 · lab 範例

先將資料整理成模型使用的設計矩陣 X: 第一欄通常是全 1 的截距,後面每一欄是一個預測變數。 類別變數要先展開成 0/1 的欄,交互作用要先乘起來變成新的一欄。 MS()(ModelSpec)就是幫你做這件事的。

›先猜加了交互作用之後 X 有幾欄,再按按鈕。
課程/lab 資料
按按鈕看它怎麼展開
從一個資料框開始,看 MS([...]) 一步步把它變成 X。類別變數與交互作用都是在這一層處理掉的。
目前
設定MS(['lstat'])
X 的欄intercept, lstat
X 的 shape(506, 2)
fit_transform 與 transform
fit_transform 在訓練資料上學會「有哪些欄、類別有哪幾種」;之後對新資料一律用 transform,不要再 fit 一次——不然兩邊的欄可能對不起來。最後一節會用這個區分說明資料洩漏。
MS 幫你把資料框變成 X
design = MS(['lstat']) design = design.fit(Boston) X = design.transform(Boston) X[:4] design = MS(['lstat']) X = design.fit_transform(Boston) X[:4]
預期輸出
   intercept  lstat
0        1.0   4.98
1        1.0   9.14
2        1.0   4.03
3        1.0   2.94
   intercept  lstat
0        1.0   4.98
1        1.0   9.14
2        1.0   4.03
3        1.0   2.94

輸出跟上一節手工做的 X 完全一樣,差別是 MS 會記得規格,之後可以對新資料套用相同轉換。

來源:Ch03-linreg-lab-zh.ipynb
對新資料要用 transform
new_df = pd.DataFrame({'lstat':[5, 10, 15]}) newX = design.transform(new_df) newX
預期輸出
   intercept  lstat
0        1.0      5
1        1.0     10
2        1.0     15

三筆新的 lstat 值,經過同一個 design 之後自動加入 intercept 欄。保留這個物件,就能對新資料套用相同規格。

來源:Ch03-linreg-lab-zh.ipynb
預測值
new_predictions = results.get_prediction(newX) new_predictions.predicted_mean
預期輸出
array([29.80359411, 25.05334734, 20.30310057])

lstat = 5、10、15 對應的預測 medv。記住這三個數字,下一節 sklearn 會算出逐位相同的結果。

來源:Ch03-linreg-lab-zh.ipynb
PART 02 · 讀 summary 表

四個欄位:係數、標準誤、t 值、p 值 課程 Lab Ch3 · lab 範例

summarize(results) 給你四個欄位。可以依序閱讀:係數告訴你效果多大,標準誤告訴你這個估計多穩, p 值只是這兩者的比值換算出來的。

›四個欄位,一個一個看。
課程/lab 資料
點欄位看它的意思
按按鈕切換,被選到的欄會亮起來,右邊解釋它在講什麼、以及看到什麼樣的值要警覺。
這一欄
欄位coef
在講什麼效果的大小與方向
警訊符號跟預期相反
t 值怎麼來的
t = coef / std err。lstat 那一列是 −0.9500 ÷ 0.039 ≈ −24.5,跟表上的 −24.528 對得起來。所以四個欄位其實只有前兩個是獨立的資訊。
只要係數的話
results.params
預期輸出
intercept    34.553841
lstat        -0.950049
dtype: float64

results.params 回傳一個 Series,索引是欄名。要拿單一個係數就 results.params['lstat']。

來源:Ch03-linreg-lab-zh.ipynb
查閱完整模型報表
完整的 summary
print(results.summary())

這一格印出來很長(含 R²、F 統計量、殘差診斷),這裡不列,自己在 lab 裡跑一次看。四個核心欄位就是上面那張表。

來源:Ch03-linreg-lab-zh.ipynb
觀念釐清
p 值 0.000 是不是代表這個變數很重要?
不是。在模型假設與零係數假設成立時,p 值衡量出現目前或更極端統計量的機率。小 p 值提供反對零係數假設的證據,跟效果大不大無關。樣本夠大時,一個實務上可以忽略的效果也會有很小的 p 值。要談重要性要看係數本身的大小,以及它在你的問題裡代表什麼。
標準誤跟標準差差在哪?
標準差量的是個別觀測的分散程度;標準誤量的是估計量(例如這個係數)的分散程度。樣本變大時標準差不會變小,標準誤會。第 5 章的自助法就是在估標準誤。
PART 03 · scikit-learn 的三個動詞

fit、predict、score:每個模型都長一樣 課程 Lab Ch3 · lab 範例

scikit-learn 的預測模型使用一致的介面。 線性迴歸、隨機森林、支持向量機,都是 fit(X, y) 學參數、predict(X) 給預測、score 算分數。 換模型只要換建構子那一行,後面完全不用改。

›按「單步」走一次 fit → predict → score。
課程/lab 資料
三個動詞
按「單步」看資料怎麼流過去。注意 fit 用訓練資料的 y 學參數;predict 只吃 X,score 則需要 y 作為評分答案。
目前
步驟0 / 3
這一步做什麼—
學到的東西存在哪—
結尾有底線的屬性
coef_、intercept_、classes_——sklearn 的慣例是「擬合之後才存在」的屬性結尾加底線。看到 NotFittedError 就是你還沒 fit 就想用它們。
同一個模型的 sklearn 版本
# ols model with intercept ols_sl = linear_model.LinearRegression(fit_intercept=True) # fitted ols model (.values.reshape(-1, 1) is required for single predictor?) x_train = Boston.lstat.values.reshape(-1, 1) y_true = Boston.medv ols_sl.fit(x_train, y_true) y_pred = ols_sl.predict(x_train) # summary ols_sl.intercept_, ols_sl.coef_
預期輸出
(np.float64(34.5538408793831), array([-0.95004935]))

輸出是 (intercept_, coef_):34.5538408793831 與 −0.95004935。跟 statsmodels 的 34.5538 與 −0.9500 是同一組數字。另外注意 reshape(-1, 1)。sklearn 要的 X 一律是二維。

來源:Ch03-linreg-lab-zh.ipynb
完整輸出:多種評分指標
評分
ols_sl_summary = {'R2': r2_score(y_true, y_pred), 'Ex. Var': explained_variance_score(y_true, y_pred), 'MSE': mean_squared_error(y_true, y_pred)} for k, v in ols_sl_summary.items(): print(k, ':', v)
預期輸出
R2 : 0.5441462975864797
Ex. Var : 0.5441462975864798
MSE : 38.48296722989415

R² 0.544 與 MSE 38.48。這裡算的是訓練資料上的分數,所以一定偏樂觀——下一節就在講這件事。

來源:Ch03-linreg-lab-zh.ipynb
預測新資料
# out-of-sample 預測s ols_sl.predict(np.array([5, 10, 15]).reshape(-1, 1))
預期輸出
array([29.80359411, 25.05334734, 20.30310057])

跟 statsmodels 的 get_prediction 給出逐位相同的三個數字。兩套 API 算的是同一件事。

來源:Ch03-linreg-lab-zh.ipynb
PART 03 · 自我檢測

model.fit(X_train, y_train) 之後要在測試集評估,正確的寫法是?

(A) model.fit(X_test, y_test) 再看分數
(B) model.score(X_test, y_test)
(C) model.fit_transform(X_test)
PART 04 · 切分訓練與測試

分開訓練與測試資料,評估對新資料的預測 課程 Lab Ch5 · lab 範例

上一節的 R² 0.544 是拿訓練資料自己考自己算出來的, 一定偏樂觀。模型可以把訓練資料背下來,那個分數不代表它對沒看過的資料有用。 解法是把資料切成兩份:一份學、一份考。

用未見資料評估模型 第 2 章的訓練 MSE 對測試 MSE、第 5 章的交叉驗證、第 6 章的模型選擇,都會使用同一項原則:評估要用模型沒看過的資料。
切一刀
Auto_train, Auto_valid = train_test_split(Auto, test_size=196, random_state=rng)

random_state=0 固定切法。不固定的話你今天的 MSE 跟明天的不一樣,就沒辦法比較模型了。

來源:Ch05-resample-lab-zh.ipynb
在驗證集上算 MSE
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 是驗證資料上的誤差;後面會繼續用這個數字比較模型。

來源:Ch05-resample-lab-zh.ipynb
完整比較:誤差指標與多項式次數
三種常見的誤差指標
print("The MAE is: {:.5}".format(metrics.mean_absolute_error(y_valid, valid_pred))) # (True, Predict) print("The MSE is: {:.5}".format(metrics.mean_squared_error(y_valid, valid_pred))) print("The RMSE is: {:.5}".format(np.sqrt( metrics.mean_squared_error(y_valid, valid_pred))))
預期輸出
The MAE is: 3.9907
The MSE is: 25.574
The RMSE is: 5.0571

MAE 3.99、MSE 25.57、RMSE 5.06。RMSE 跟 y 同單位(每加侖英里),比 MSE 好解釋;MAE 對離群值比較不敏感。

來源:Ch05-resample-lab-zh.ipynb
三個次數的驗證誤差
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([25.57387819, 22.21802005, 22.66767544])

上面那張圖的三個數字就是這一格的輸出,逐字取用。

來源:Ch05-resample-lab-zh.ipynb
跨章例子:三種多項式的驗證誤差
圖表需要連網載入 Chart.js:驗證集的 MSE 從一次式的 25.57 降到二次式的 22.22,三次式反而略升到 22.67,複雜度不是越高越好。
›驗證集 MSE:二次式最低,三次式沒有更好。
課程/lab 資料
三個多項式次數
同一份資料、同一個切法,只改多項式的次數。二次式明顯比一次式好,三次式沒有再進步——這就是第 5、6 章在找的那個轉折點。
這些數字
degree 125.5739
degree 222.2180
degree 322.6677
單一次切分的問題
換一個 random_state,這三個數字都會變。lab 範例 就示範了換切法之後結果不同——所以才需要交叉驗證,下一節講。
PART 05 · 交叉驗證與資料洩漏

在每次切分的訓練資料內估計前處理參數 課程 Lab Ch5 · lab 範例

比較兩個模型,先讓它們使用相同切分,再看每次分數的配對差異。各自的平均和標準差可以摘要結果,但不足以單獨判定哪個模型較好。

單切一刀有兩個問題:只用了一部分資料訓練,而且結果會隨著切法跳動。 交叉驗證把資料輪流當驗證集,每一筆都當過一次,結果穩定得多。 但用它之前得先弄清楚一件事——前處理要在切分的哪一邊做。

›先找出哪條路在切分前看過測試集資訊。
概念示意
比較前處理的順序
左邊:先標準化整份資料、再切分。右邊:先切分、只用訓練集學標準化的參數。按「單步」看差別在哪一步發生。
目前
路線—
標準化用到了誰的資訊—
評估可否解讀—
為什麼這叫洩漏
先標準化的話,訓練集的每一筆都間接知道了測試集的平均與標準差。評估已經不再是乾淨的 out-of-sample 比較;洩漏可能影響分數;影響方向與大小需要依模型及資料檢查。Pipeline 可以把這些前處理放進每一折,但仍要檢查管線外的處理與切分方式。
完整實作:留一法與重複切分的分數
留一交叉驗證
reg = LinearRegression() # Use MSE as the scoring metric scoring = metrics.make_scorer(metrics.mean_squared_error) # Cross validation iterators cv = LeaveOneOut() cv_results = cross_validate(reg, X.horsepower.to_numpy().reshape(-1,1), Y.to_numpy(), scoring=scoring, cv=cv ) cv_err = np.mean(cv_results['test_score']) cv_err
預期輸出
np.float64(24.231513517929226)

24.2315 是各留一折誤差的平均。固定資料下的留一切法唯一,仍有各筆誤差與抽樣不確定性;但要擬合 n 次,資料大的時候很貴。

來源:Ch05-resample-lab-zh.ipynb
重複切分:看平均,也看標準差
validation = ShuffleSplit(n_splits=10, test_size=196, random_state=0) results = cross_validate(hp_model, Auto.drop(['mpg'], axis=1), Auto['mpg'], cv=validation) results['test_score'].mean(), results['test_score'].std()
預期輸出
(np.float64(23.802232661034168), np.float64(1.4218450941091842))

平均 23.80、標準差 1.42。1.42 描述不同切分下的分數變動。比較兩個模型時,需要查看相同切分下的配對差異,再判斷差距是否穩定。

來源:Ch05-resample-lab-zh.ipynb
順序標準化參數從哪裡估評估能否解讀判定
先標準化 → 再切分全部資料(含之後的測試資料)不能當成乾淨的 out-of-sample 表現✗ 洩漏
先切分 → 訓練集 fit;測試集只 transform只有訓練集可作為這次切分的測試表現✓
Pipeline + cross_validate每一折各自的訓練部分可彙整各折的驗證表現✓ 最推薦
用 Pipeline 固定前處理流程 把 StandardScaler 與模型包成 Pipeline 之後丟進 cross_validate,sklearn 會在每一折內部重新 fit 那個 scaler。但放在 Pipeline 外的補值、選欄位或不當切分仍可能洩漏。第 6 章的收縮方法通常要標準化,練習那些模型時,也要維持相同的前處理順序。
觀念釐清
交叉驗證跟測試集是不是同一件事?
兩者的用途不同。交叉驗證是在訓練資料內部切來切去,用來選模型與調參數;測試集是最後才動一次的、完全獨立的一份,用來報告最終效能。拿測試集去選模型,等於把它變成訓練資料的一部分。
留一交叉驗證為什麼只列一個數字?
這個 lab 把每一個留一折的損失取平均,所以只顯示平均值;不是所有損失都一樣。固定資料與擬合程序下,LOOCV 沒有切分的隨機性,仍有資料抽樣的不確定性。上面的 ShuffleSplit 是十次隨機留出一半資料,並非把資料分成十個互斥的折。
PART 05 · 自我檢測

你先對整份資料做 StandardScaler().fit_transform(X),再 train_test_split,最後回報測試 MSE。這個數字有什麼問題?

(A) 沒問題,標準化不會改變模型的排序
(B) 它不再是嚴格獨立的評估;偏差方向與大小不能只靠『有洩漏』判定
(C) 應該改用 MinMaxScaler
🔗 這在本站哪一章會用到 第 5 章整章都在講交叉驗證與自助法,第 6 章用交叉驗證挑收縮的強度 λ,第 8 章調樹的深度也是同一套。→ 重抽樣方法 · k 折交叉驗證 → 同一章的「先用全資料選特徵再 CV」
EXERCISES · 練習

動手驗證:概念自測 先備 · 自我檢測

回顧本頁觀念與原 Lab 的程式,確認各個操作的意義與使用條件。

EXERCISE 1 · 選工具

你要比較五個模型並挑一個,中間需要標準化與多項式展開。該用哪一套?

(A) statsmodels,因為它給的統計量比較完整
(B) 手寫迴圈,最有彈性
(C) scikit-learn,把前處理與模型包成 Pipeline 再交叉驗證
EXERCISE 2 · 截距

sm.OLS(y, Boston[['lstat']]).fit() 少做了一件事。少了什麼?

(A) 沒有標準化 lstat
(B) y 應該放後面
(C) 沒有截距欄,擬合出來的是一條過原點的線
EXERCISE 4 · 洩漏

下列哪一個做法不會造成資料洩漏?

(A) 先切分,再用訓練集的平均與標準差去 transform 測試集
(B) 用全部資料算相關係數,挑出前十個變數,再切分做交叉驗證
(C) 用全部資料的中位數填補遺漏值,再切分
重點速查與來源

建模 API速查表 先備 · 建模 API

來源:完整書目與章節定位

本頁書目與章節定位

  • 教科書:An Introduction to Statistical Learning with Applications in Python。James、Witten、Hastie、Tibshirani、Taylor,2023。原書與下載資訊

需要核對完整公式、比較表或實作時,再展開下方速查。

查閱完整速查與延伸提醒

下表整理兩套 API 的用法與前處理順序。

你要做的事statsmodelsscikit-learn
準備 XMS([...]).fit_transform(df)(截距要自己有)X.values.reshape(-1,1);截距用 fit_intercept=True
擬合sm.OLS(y, X).fit()model.fit(X, y)
看係數summarize(results)、results.paramsmodel.coef_、model.intercept_
標準誤與 p 值有沒有
預測results.get_prediction(newX)model.predict(X)
評分results.rsquaredmodel.score(X, y)
交叉驗證包成 sklearn_sm 才有cross_validate
串前處理沒有Pipeline
估計器(Estimator)轉換器(Transformer)
fit(X, y) — 學參數fit(X) — 學轉換的參數
predict(X) — 給預測transform(X) — 套用轉換
score(X, y) — 算分數fit_transform(X) — 兩步合一
例:LinearRegression、LogisticRegression例:StandardScaler、PCA、SimpleImputer
誤差指標公式的意思什麼時候用
MAE平均絕對誤差對離群值不敏感;跟 y 同單位
MSE平均平方誤差數學上好處理,整門課的預設
RMSEMSE 開根號跟 y 同單位,最好解釋
R²被解釋的變異比例跟其他模型比較時方便,但會隨變數增加而虛高
三個閱讀重點 1. 兩套 API 算的是同一件事,差別在輸出。要係數與 p 值找 statsmodels,要預測與模型比較找 scikit-learn。
2. sklearn 的一切都是 fit / predict / score。換模型只要換建構子那一行;結尾有底線的屬性是擬合之後才存在的。
3. 先切分,再轉換。順序反了,那個分數就不再是乾淨的 out-of-sample 評估;用 Pipeline 固定正確順序。
CARDS · 關鍵詞彙卡

關鍵詞彙卡:點卡片翻面 課程詞彙

詞彙卡整理本頁引用的課程 lab中常用的統計或機器學習專有名詞,正面附英文名稱。 先看正面的術語,試著說出定義,再翻面核對。可以洗牌複習,也可以搭配本章例子練習解釋。

複習:展開關鍵詞彙卡