返回目錄
A
洞見數據:用分析思維駕馭數據科學 - 第 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`.
---
> **提示**:在實務部署前,請務必使用與資料分層相同的隨機種子,確保評估結果可重複。