返回目錄
A
數據科學的藝術與科學:從基礎到實踐 - 第 3 章
第三章:模型建構與評估
發布於 2026-02-25 14:41
# 第三章:模型建構與評估
> **章節目標**:本章將以剛剛整理好的銷售資料為基礎,帶領讀者完成兩類典型機器學習任務——迴歸預測與分類判斷,並探討評估指標、交叉驗證、超參數調整與模型可解釋性。
## 3.1 先備工作:資料確認與切分
在進入建模之前,我們先把資料重新整理一次,確保所有特徵都已經轉為機器學習友好的格式。
python
# 讀取剛整理好的 CSV
import pandas as pd
df = pd.read_csv('sales_cleaned.csv')
print(df.head())
> 這裡的 `TotalPrice` 是我們的目標變數,作為迴歸任務。若將其二元化(>50元為高消費,≤50元為低消費),則可作為分類任務。
### 資料切分
python
from sklearn.model_selection import train_test_split
X = df.drop(columns=['TotalPrice'])
y = df['TotalPrice']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
> **備註**:若要同時處理迴歸與分類,建議先定義兩個目標變數:`y_reg` 與 `y_cls`。分類目標可透過 `y_cls = (y > 50).astype(int)` 產生。
## 3.2 迴歸模型:線性迴歸與隨機森林
### 3.2.1 直觀解釋:線性迴歸
python
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X_train, y_train)
print('截距:', lin_reg.intercept_)
print('係數:', dict(zip(X.columns, lin_reg.coef_)))
> **解釋**:係數的正負直接說明該特徵對消費金額的影響方向;數值越大,影響越顯著。
### 3.2.2 性能評估
python
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
pred_lin = lin_reg.predict(X_test)
print('MAE:', mean_absolute_error(y_test, pred_lin))
print('RMSE:', mean_squared_error(y_test, pred_lin, squared=False))
print('R²:', r2_score(y_test, pred_lin))
> **常見陷阱**:若 `R²` 接近 0 或負值,表示模型無法捕捉資料變異,需考慮非線性模型或特徵工程。
### 3.2.3 非線性補強:隨機森林迴歸
python
from sklearn.ensemble import RandomForestRegressor
rf_reg = RandomForestRegressor(n_estimators=500, random_state=42)
rf_reg.fit(X_train, y_train)
pred_rf = rf_reg.predict(X_test)
print('MAE (RF):', mean_absolute_error(y_test, pred_rf))
print('RMSE (RF):', mean_squared_error(y_test, pred_rf, squared=False))
print('R² (RF):', r2_score(y_test, pred_rf))
> **說明**:隨機森林能自動處理非線性關係,且不易過擬合。若仍想提升,可調整 `max_depth`、`min_samples_leaf` 等參數。
## 3.3 分類模型:邏輯回歸與XGBoost
### 3.3.1 目標設定
python
y_cls = (y > 50).astype(int)
X_train_cls, X_test_cls, y_train_cls, y_test_cls = train_test_split(X, y_cls, test_size=0.2, random_state=42)
### 3.3.2 基礎模型:邏輯回歸
python
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression(max_iter=1000)
log_reg.fit(X_train_cls, y_train_cls)
### 3.3.3 評估指標
python
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
pred_cls = log_reg.predict(X_test_cls)
print('Accuracy:', accuracy_score(y_test_cls, pred_cls))
print('Precision:', precision_score(y_test_cls, pred_cls))
print('Recall:', recall_score(y_test_cls, pred_cls))
print('F1-Score:', f1_score(y_test_cls, pred_cls))
print('ROC AUC:', roc_auc_score(y_test_cls, log_reg.predict_proba(X_test_cls)[:,1]))
> **小提醒**:若正負樣本比例失衡,建議使用 `class_weight='balanced'` 或調整 `threshold`。
### 3.3.4 強化模型:XGBoost
python
import xgboost as xgb
xgb_clf = xgb.XGBClassifier(n_estimators=400, learning_rate=0.05, max_depth=4, subsample=0.8, colsample_bytree=0.8, random_state=42)
xgb_clf.fit(X_train_cls, y_train_cls)
pred_xgb = xgb_clf.predict(X_test_cls)
print('XGB Accuracy:', accuracy_score(y_test_cls, pred_xgb))
print('XGB ROC AUC:', roc_auc_score(y_test_cls, xgb_clf.predict_proba(X_test_cls)[:,1]))
> **為什麼選擇 XGBoost**:它在處理高維度特徵、缺失值、非線性關係上表現優異,且易於調參。
## 3.4 超參數調整:網格搜尋與隨機搜尋
### 3.4.1 網格搜尋(GridSearchCV)
python
from sklearn.model_selection import GridSearchCV
param_grid = {
'n_estimators': [200, 400, 600],
'max_depth': [3, 4, 5],
'learning_rate': [0.1, 0.05, 0.01]
}
gs = GridSearchCV(xgb.XGBClassifier(random_state=42), param_grid, cv=5, scoring='roc_auc', verbose=1)
gs.fit(X_train_cls, y_train_cls)
print('Best Params:', gs.best_params_)
print('Best ROC AUC:', gs.best_score_)
### 3.4.2 隨機搜尋(RandomizedSearchCV)
python
from sklearn.model_selection import RandomizedSearchCV
import scipy.stats as st
param_dist = {
'n_estimators': st.randint(100, 1000),
'max_depth': st.randint(3, 10),
'learning_rate': st.uniform(0.01, 0.3),
'subsample': st.uniform(0.5, 1.0),
'colsample_bytree': st.uniform(0.5, 1.0)
}
rs = RandomizedSearchCV(xgb.XGBClassifier(random_state=42), param_distributions=param_dist, n_iter=50, cv=5, scoring='roc_auc', random_state=42)
rs.fit(X_train_cls, y_train_cls)
print('Best Params (Randomized):', rs.best_params_)
print('Best ROC AUC (Randomized):', rs.best_score_)
> **建議**:對於資源有限的情況,先用 RandomizedSearchCV 快速定位,再細化使用 GridSearchCV。
## 3.5 交叉驗證與模型選擇
> **目的**:確保模型在不同資料子集上皆具備穩健性,避免過擬合。
python
from sklearn.model_selection import cross_val_score
cv_scores = cross_val_score(rf_reg, X, y, cv=10, scoring='neg_mean_squared_error')
print('Cross-validated RMSE:', (-cv_scores.mean())**0.5)
> **注意**:對於時間序列資料,請使用 `TimeSeriesSplit`;本例為非時間序列,採用 k‑fold。
## 3.6 模型可解釋性
### 3.6.1 SHAP 值
python
import shap
explainer = shap.TreeExplainer(rf_reg)
shap_values = explainer.shap_values(X_test)
shap.summary_plot(shap_values, X_test)
> **解釋**:SHAP 值能量化每個特徵對預測的貢獻,讓非技術決策者也能理解模型。
### 3.6.2 LIME(Local Interpretable Model‑agnostic Explanations)
python
from lime import lime_tabular
explainer = lime_tabular.LimeTabularExplainer(X_train.values, feature_names=X.columns, class_names=['Low', 'High'], mode='classification')
exp = explainer.explain_instance(X_test.values[0], rf_reg.predict_proba, num_features=5)
exp.show_in_notebook(show_table=True, show_all=False)
> **使用場景**:當模型複雜且無法一次性解釋所有輸入時,LIME 可針對單一樣本提供「本地」解釋。
## 3.7 部署前的最後檢查
| 步驟 | 內容 | 目的 |
|------|------|------|
| 1 | **資料完整性檢查** | 確保無遺失值、極端值已處理 |
| 2 | **版本控制** | 以 Git 或 DVC 紀錄資料、模型、程式碼版本 |
| 3 | **模型序列化** | 使用 `joblib` / `pickle` / `onnx` 將模型儲存 |
| 4 | **測試案例** | 寫單元測試確認輸入→輸出流程無誤 |
| 5 | **監控指標** | 設定 `MLflow`、Prometheus 監控 `latency`、`accuracy` |
python
import joblib
joblib.dump(rf_reg, 'models/rf_reg.pkl')
> **小結**:上述流程將確保模型在實際環境中能穩定運行,並具備回溯與更新機制。
## 3.8 小結
本章從資料準備開始,透過線性迴歸與隨機森林、邏輯回歸與 XGBoost,展現不同類型模型的建構與評估。進一步說明了交叉驗證、超參數調整、模型可解釋性與部署前檢查,為讀者提供了從「理論」到「實務」的完整流程。下一章將進一步探討模型監控、漂移偵測以及如何將模型落地於雲端服務。