聊天視窗

從資料到決策:系統化資料科學實踐手冊 - 第 6 章

第六章 模型評估與調參

發布於 2026-03-05 17:27

# 第六章 模型評估與調參 > **一句話提醒**:評估與調參是模型工程師與數據科學家的「品質保證站」。不只是在實驗室中做一次驗證,而是為了確保模型在真實環境中持續穩定地提供決策價值。 ## 6.1 評估指標總覽 | 指標 | 定義 | 適用場景 | 取值範圍 | 典型解讀 | |------|------|----------|----------|----------| | Accuracy | | (TP+TN)/(TP+TN+FP+FN) | 普通二分類 | 0–1 | 取值越高越好 | | Precision | TP/(TP+FP) | 當預測為正類時,真的正類比例 | 0–1 | 重要於成本高的誤判 (如醫療) | | Recall (Sensitivity) | TP/(TP+FN) | 捕捉所有正類 | 0–1 | 重要於漏判成本高 (如欺詐偵測) | | F1‑Score | 2·Precision·Recall/(Precision+Recall) | 平衡精度與召回 | 0–1 | 兩者皆重要時使用 | | ROC‑AUC | | Area under ROC Curve | 描述 TP‑率 vs FP‑率 | 檢測分數模型 | 0–1 | 1 完美, 0.5 隨機 | | PR‑AUC | Area under Precision‑Recall Curve | 不平衡資料 | 0–1 | 召回率高時更能反映真實表現 | | Log‑Loss | 交叉熵損失 | 需要分數預測 | ≥0 | 0 越低越好 | | MAE / MSE / R² | 回歸指標 | 回歸模型 | 依據 | 取值越低/高越好 | > **選擇指標的關鍵**:先考量「失誤成本」與「業務價值」。例如信用卡欺詐,漏判成本遠高於誤判,則召回率與 PR‑AUC 更具決策價值。 | | | | | | ## 6.2 交叉驗證 (Cross‑Validation) ### 6.2.1 為什麼要交叉驗證? - **避免過擬合**:僅靠一次拆分可能得到偶然的好表現。 - **穩健性評估**:透過多次重複,得到表現的分布。 - **資料不平衡**:配合 StratifiedKFold 確保每折樣本比例相近。 ### 6.2.2 常用交叉驗證類型 | 方法 | 何時適用 | |------|----------| | K‑Fold | 一般分布均勻資料 | | StratifiedKFold | 分類問題,樣本不平衡 | | LeaveOneOut (LOO) | 資料極少,想盡可能多訓練 | | TimeSeriesSplit | 時間序列資料,避免未來資訊泄露 | ### 6.2.3 範例程式碼 python import numpy as np import pandas as pd from sklearn.model_selection import StratifiedKFold, cross_val_score from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import make_classification X, y = make_classification(n_samples=10000, n_features=20, n_informative=10, n_repeated=0, n_classes=2, weights=[0.995, 0.005], flip_y=0, random_state=42) model = RandomForestClassifier(n_estimators=200, random_state=42) skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(model, X, y, cv=skf, scoring='roc_auc') print('ROC‑AUC scores per fold:', scores) print('Mean ROC‑AUC:', scores.mean()) ### 6.2.4 交叉驗證的常見陷阱 | 陷阱 | 解決方式 | |------|----------| | 1. **資料泄露** | 對資料做「先切再處理」的順序;不在整體資料上做特徵工程。 | | 2. **不當抽樣** | StratifiedKFold 或自定義樣本比例。 | | 3. **過度訓練** | 在交叉驗證內部再做 hyper‑parameter search 時,使用內部的 CV 或自訂分層。 | ## 6.3 ROC 與 PR 曲線 ### 6.3.1 何時用 ROC,何時用 PR? - **ROC**:當 FP 與 FN 成本相近時。 - **PR**:當正類稀少且誤判成本高時。<br>例如信用卡欺詐、疾病篩檢。 ### 6.3.2 繪圖範例 python from sklearn.metrics import roc_curve, precision_recall_curve, auc import matplotlib.pyplot as plt # 模型預測 y_proba = model.predict_proba(X)[:, 1] # ROC fpr, tpr, _ = roc_curve(y, y_proba) roc_auc = auc(fpr, tpr) # PR precision, recall, _ = precision_recall_curve(y, y_proba) pr_auc = auc(recall, precision) plt.figure(figsize=(12, 5)) # ROC plt.subplot(1, 2, 1) plt.plot(fpr, tpr, label=f'ROC AUC = {roc_auc:.3f}') plt.plot([0, 1], [0, 1], 'k--') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('ROC Curve') plt.legend() # PR plt.subplot(1, 2, 2) plt.plot(recall, precision, label=f'PR AUC = {pr_auc:.3f}') plt.xlabel('Recall') plt.ylabel('Precision') plt.title('Precision‑Recall Curve') plt.legend() plt.tight_layout() plt.show() ### 6.3.3 判斷閾值的技巧 - **Youden’s J statistic**:最大化 `tpr - fpr`。 - **F1‑max**:在 PR 曲線上最大化 F1。 - **業務指標**:例如「成本最低」或「召回率達 95%」作為閾值。 ## 6.4 參數調整策略 ### 6.4.1 Grid Search - **全搜尋**:穩健但計算成本高。 - **使用 `GridSearchCV`**:自動交叉驗證並輸出最佳參數。 python from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [100, 200, 300], 'max_depth': [None, 5, 10], 'min_samples_split': [2, 5] } gs = GridSearchCV(RandomForestClassifier(random_state=42), param_grid, cv=5, scoring='roc_auc', n_jobs=-1) gs.fit(X, y) print('Best params:', gs.best_params_) print('Best ROC‑AUC:', gs.best_score_) ### 6.4.2 Randomized Search - **樣本化**:在大參數空間中隨機抽樣,速度快。 - **適用**:參數範圍廣,搜尋成本高。 python from sklearn.model_selection import RandomizedSearchCV from scipy.stats import randint param_dist = { 'n_estimators': randint(50, 500), 'max_depth': [None] + list(range(5, 25, 5)), 'min_samples_split': randint(2, 10) } rs = RandomizedSearchCV(RandomForestClassifier(random_state=42), param_dist, n_iter=50, cv=5, scoring='roc_auc', n_jobs=-1, random_state=42) rs.fit(X, y) print('Best params:', rs.best_params_) ### 6.4.3 Bayesian Optimization (Optuna) - **基於先前結果**:使用貝葉斯模型預測未測試參數的表現。 - **自動調整步驟**:可用於連續型參數或大參數空間。 python import optuna from optuna.samplers import TPESampler def objective(trial): n_estimators = trial.suggest_int('n_estimators', 50, 500) max_depth = trial.suggest_int('max_depth', 5, 30) min_samples_split = trial.suggest_int('min_samples_split', 2, 10) clf = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth, min_samples_split=min_samples_split, random_state=42) score = cross_val_score(clf, X, y, cv=5, scoring='roc_auc').mean() return score study = optuna.create_study(direction='maximize', sampler=TPESampler()) study.optimize(objective, n_trials=100) print('Best params:', study.best_params) print('Best ROC‑AUC:', study.best_value) ### 6.4.4 早停(Early Stopping) - **對於迭代模型**(如 XGBoost, LightGBM):當驗證集表現不再提升時自動終止。 - **實作範例**: python import xgboost as xgb params = { 'objective': 'binary:logistic', 'eval_metric': 'auc', 'learning_rate': 0.05, 'max_depth': 6, 'subsample': 0.8, 'colsample_bytree': 0.8 } dtrain = xgb.DMatrix(X_train, label=y_train) # 以驗證集作為 early stopping watchlist = [(dtrain, 'train'), (xgb.DMatrix(X_val, label=y_val), 'eval')] model = xgb.train(params, dtrain, num_boost_round=1000, evals=watchlist, early_stopping_rounds=50, verbose_eval=False) print('Best iteration:', model.best_iteration) ## 6.5 監測模型表現 (Model Monitoring) | 監測項目 | 目的 | 工具 / 實作 | |----------|------|-------------| | **漂移檢測 (Concept Drift)** | 確保資料分布或目標分布未發生改變 | River、Alibi Detect、漂移指標 (KL Divergence) | | **性能退化** | 監控關鍵指標 (Accuracy, F1 等) 變化 | MLflow、Prometheus + Grafana | | **異常檢測** | 識別輸入資料或模型輸出是否異常 | Isolation Forest、LSTM‑Autoencoder | | **成本跟蹤** | 以實際業務成本評估模型效益 | 自訂指標、Cost‑Sensitive learning | > **實作示例**:使用 `river` 進行連續漂移監測。 python import river from river import drift model = river.linear_model.LogisticRegression() drift_detector = drift.ADWIN() for x, y in data_stream: y_pred = model.predict_one(x) model.learn_one(x, y) drift_detector.update(float(y_pred == y)) if drift_detector.change_detected: print('Drift detected! Re‑training model.') model = river.linear_model.LogisticRegression() # or retrain on recent batch ## 6.6 案例:信用卡欺詐模型評估(延伸) | 指標 | 目標 | 目前值 | |------|------|--------| | Precision | 0.80 | 0.78 | | Recall | 0.95 | 0.92 | | F1‑Score | 0.87 | 0.85 | | PR‑AUC | 0.70 | 0.68 | | ROC‑AUC | 0.95 | 0.94 | **改進建議** 1. **採用更多不平衡學習策略**:如 `SMOTE + undersampling` 或 `class_weight='balanced'`。 2. **調整閾值**:將 `F1‑max` 或 `Recall‑90%` 位置作為預測閾值。 3. **引入新特徵**:交易頻率、客戶風險等。 4. **使用 Bayesian Optimization 進一步調參**:優化 XGBoost 連續參數。 ## 6.7 小結 - **評估指標**:不僅是數值,更關聯業務邊際。選擇正確的指標,才能做出有意義的調整。 - **交叉驗證**:提供穩健的性能估計,避免過擬合與資料泄露。 - **參數調整**:從 Grid Search、Randomized Search 到 Bayesian Optimization,依據資料規模與模型複雜度選擇合適工具。 - **模型監測**:在部署後持續追蹤表現與漂移,確保模型長期可用。 - **實務洞見**:在每一次模型迭代中,都要把「成本‑效益」視角放在首位,才能真正將資料科學價值落地。 > **一句話提醒**:模型的「表現」是量化的,而「價值」則取決於你能否將這些數字轉化為可衡量的業務改進。