聊天視窗

洞見數據:用分析思維駕馭數據科學 - 第 7 章

第七章 模型評估與調優

發布於 2026-02-27 02:44

# 第七章 模型評估與調優 在上一章「機器學習基礎」中,我們已經完成了模型的訓練與初步評估。這一章將深入探討如何透過科學、可重複的方式評估模型性能,並利用多種技巧進行調優,確保模型在實務環境中能穩健運作。 ## 1. 評估框架概覽 | 步驟 | 目的 | 主要指標 | |------|------|-----------| | 1. 資料分層 | 防止資料泄露 | 隨機或分層抽樣 | | 2. 交叉驗證 | 減少過擬合風險 | k‑fold、留一法 | | 3. 指標選擇 | 針對任務挑選 | 準確率、召回率、AUC 等 | | 4. 超參數調整 | 找到最佳模型 | 網格搜索、隨機搜尋、貝葉斯優化 | | 5. 模型解釋 | 提升可解釋性 | 特徵重要性、SHAP | ## 2. 交叉驗證(Cross‑Validation) ### 2.1 k‑Fold 交叉驗證 k‑Fold 將資料等分成 k 份,輪流將其中一份作為驗證集,其餘作為訓練集。 python from sklearn.model_selection import KFold, cross_val_score kf = KFold(n_splits=5, shuffle=True, random_state=42) model = LogisticRegression() scores = cross_val_score(model, X, y, cv=kf, scoring='roc_auc') print(f"k‑Fold AUC: {scores.mean():.4f} ± {scores.std():.4f}") ### 2.2 留一法(Leave‑One‑Out, LOO) LOO 是 k‑Fold 的極端形式,k 等於樣本數。適用於小樣本資料,計算成本較高。 ## 3. 混淆矩陣與相關指標 混淆矩陣是二元分類模型評估的基礎。 python from sklearn.metrics import confusion_matrix, classification_report y_pred = model.predict(X_test) cm = confusion_matrix(y_test, y_pred) print(cm) print(classification_report(y_test, y_pred)) | 指標 | 公式 | 直觀意義 | |------|------|-----------| | 準確率 (Accuracy) | (TP+TN)/(TP+TN+FP+FN) | 全部樣本中正確預測比例 | | 召回率 (Recall) | TP/(TP+FN) | 能檢測到的正樣本比例 | | 精確率 (Precision) | TP/(TP+FP) | 正預測中真實正樣本比例 | | F1‑score | 2×(Precision×Recall)/(Precision+Recall) | 精確率與召回率的調和平均 | | ROC‑AUC | ROC 曲線下方面積 | 分類器對不同閾值的整體表現 | ## 4. ROC 曲線與 AUC ROC 曲線展示真陽性率(TPR)對假陽性率(FPR)的變化。AUC 越接近 1,模型越好。 python from sklearn.metrics import roc_curve, auc import matplotlib.pyplot as plt fpr, tpr, thresholds = roc_curve(y_test, y_prob) roc_auc = auc(fpr, tpr) plt.figure() plt.plot(fpr, tpr, label=f'AUC = {roc_auc:.3f}') plt.plot([0,1],[0,1],'--', color='gray') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('ROC Curve') plt.legend() plt.show() ## 5. 特徵重要性與解釋 ### 5.1 基於樹模型的特徵重要性 python model = RandomForestClassifier() model.fit(X_train, y_train) importances = model.feature_importances_ indices = np.argsort(importances)[::-1] for idx in indices[:10]: print(f'{X.columns[idx]}: {importances[idx]:.4f}') ### 5.2 SHAP(SHapley Additive exPlanations) SHAP 以遊戲理論角度量化每個特徵對預測的貢獻。 python import shap explainer = shap.TreeExplainer(model) shap_values = explainer.shap_values(X_test) shap.summary_plot(shap_values, X_test) ## 6. 超參數調整 ### 6.1 網格搜索(Grid Search) python from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [100, 200, 300], 'max_depth': [None, 10, 20, 30], 'min_samples_split': [2, 5, 10] } grid = GridSearchCV(RandomForestClassifier(), param_grid, cv=5, scoring='roc_auc', n_jobs=-1) grid.fit(X_train, y_train) print('Best params:', grid.best_params_) ### 6.2 隨機搜尋(Randomized Search) 更適合大參數空間。 python from sklearn.model_selection import RandomizedSearchCV from scipy.stats import randint param_dist = { 'n_estimators': randint(50, 500), 'max_depth': randint(5, 50), 'min_samples_split': randint(2, 20) } random_search = RandomizedSearchCV(RandomForestClassifier(), param_dist, n_iter=50, cv=5, scoring='roc_auc', random_state=42, n_jobs=-1) random_search.fit(X_train, y_train) print('Best params:', random_search.best_params_) ### 6.3 貝葉斯優化(Optuna) python import optuna def objective(trial): n_estimators = trial.suggest_int('n_estimators', 50, 500) max_depth = trial.suggest_int('max_depth', 5, 50) min_samples_split = trial.suggest_int('min_samples_split', 2, 20) 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_train, y_train, cv=5, scoring='roc_auc').mean() return score study = optuna.create_study(direction='maximize') study.optimize(objective, n_trials=100) print('Best trial:', study.best_trial.params) ## 7. 模型集成與投票 | 方法 | 原理 | 適用情況 | |------|------|-----------| | 投票(Voting) | 多個模型預測結果進行投票 | 不同模型互補時 | | 堆疊(Stacking) | 以基模型預測為特徵,訓練元模型 | 大型數據集、複雜任務 | | 均衡器(Blending) | 內部交叉驗證分層集成 | 需要快速部署時 | python from sklearn.ensemble import VotingClassifier, StackingClassifier estimators = [('rf', RandomForestClassifier()), ('gb', GradientBoostingClassifier())] ensemble = VotingClassifier(estimators=estimators, voting='soft') ensemble.fit(X_train, y_train) ## 8. 檢測模型漂移(Model Drift) 實務部署後,資料分布可能變化,導致性能下降。 - **監測**:定期計算學習曲線、AUC。 - **再訓練**:當 AUC 下滑 > 0.02 時觸發 retrain。 - **版本控制**:每次再訓練均保存模型版本與性能。 ## 8. 案例:醫療診斷 使用「心臟病」Kaggle 資料集,採用 XGBoost 進行分類,並以 AUC 0.94 為目標。以下為完整評估流程: python import xgboost as xgb from sklearn.model_selection import StratifiedKFold xgb_clf = xgb.XGBClassifier(eval_metric='auc', use_label_encoder=False) skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) cv_scores = [] for train_idx, val_idx in skf.split(X, y): X_tr, X_val = X.iloc[train_idx], X.iloc[val_idx] y_tr, y_val = y.iloc[train_idx], y.iloc[val_idx] xgb_clf.fit(X_tr, y_tr) y_prob = xgb_clf.predict_proba(X_val)[:,1] cv_scores.append(auc(*roc_curve(y_val, y_prob)[:2])) print(f"Final AUC: {np.mean(cv_scores):.4f}") ## 9. 練習題 1. 對於一個多類別分類問題,如何計算每個類別的 F1‑score? 2. 在使用 SVM 時,為什麼使用 `cross_val_score` 時應該用 `scoring='roc_auc'` 而不是 `accuracy`? 3. 實作一個 Optuna 超參數搜索,優化 Lasso 回歸的 `alpha` 參數,並比較網格搜索結果。 ## 10. 參考文獻 - Bishop, C. M. *Pattern Recognition and Machine Learning*, 2006. - James, G., et al. *An Introduction to Statistical Learning*, 2013. - Lundberg, S. M., & Lee, S.-I. *Consistent Individualized Feature Attribution for Tree Ensembles*, 2017. - T. K. H. Optuna: A Hyper‑parameter Optimization Framework. - Scikit‑learn Documentation: `model_selection`, `metrics`, `ensemble`. --- > **提示**:在實務部署前,請務必使用與資料分層相同的隨機種子,確保評估結果可重複。