5.4.5 ハイパーパラメータチューニング

この節では次を確認します。
- parameters と hyperparameters の違い;
GridSearchCVの使い方;- 探索空間が大きいときの
RandomizedSearchCV; - final holdout を調整に使わず残す方法;
- 過剰調整を避ける方法。


| 用語 | 実用上の意味 |
|---|---|
| parameter | モデルが fit() 中にデータから学ぶ値 |
| hyperparameter | 学習前に人が選ぶ設定。たとえば木の深さ |
| search space | 探索に試させる候補値 |
| CV score | 設定を選ぶためのクロスバリデーションスコア |
| final holdout | 調整後に 1 回だけ使う、触っていないデータ |
| budget | 試せる組み合わせ数や trial 数 |
セットアップ
Section titled “セットアップ”python -m pip install -U scikit-learn完全な実験を実行する
Section titled “完全な実験を実行する”tuning_lab.py を作成します。
from sklearn.datasets import load_breast_cancerfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.metrics import accuracy_score, f1_score, recall_scorefrom sklearn.model_selection import GridSearchCV, RandomizedSearchCV, StratifiedKFold, train_test_split
X, y = load_breast_cancer(return_X_y=True)X_train, X_final, y_train, y_final = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y)cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
print("grid_search_lab")grid = GridSearchCV( RandomForestClassifier(random_state=42), param_grid={ "n_estimators": [80, 160], "max_depth": [3, 5, None], "min_samples_leaf": [1, 3], }, scoring="f1", cv=cv, n_jobs=-1,)grid.fit(X_train, y_train)print("best_params=", grid.best_params_)print(f"best_cv_f1={grid.best_score_:.3f}")final_pred = grid.best_estimator_.predict(X_final)print( f"final accuracy={accuracy_score(y_final, final_pred):.3f} " f"recall={recall_score(y_final, final_pred):.3f} " f"f1={f1_score(y_final, final_pred):.3f}")
print("random_search_lab")random_search = RandomizedSearchCV( RandomForestClassifier(random_state=42), param_distributions={ "n_estimators": [60, 100, 160, 220], "max_depth": [3, 5, 8, None], "min_samples_leaf": [1, 2, 3, 5], "max_features": ["sqrt", "log2", None], }, n_iter=8, scoring="f1", cv=cv, random_state=42, n_jobs=-1,)random_search.fit(X_train, y_train)print("best_params=", random_search.best_params_)print(f"best_cv_f1={random_search.best_score_:.3f}")
print("top_3_grid_results")rows = sorted( zip(grid.cv_results_["mean_test_score"], grid.cv_results_["params"]), key=lambda item: item[0], reverse=True,)[:3]for score, params in rows: print(f"score={score:.3f} params={params}")実行します。
python tuning_lab.py期待される出力:
grid_search_labbest_params= {'max_depth': 5, 'min_samples_leaf': 3, 'n_estimators': 80}best_cv_f1=0.968final accuracy=0.956 recall=0.972 f1=0.966random_search_labbest_params= {'n_estimators': 100, 'min_samples_leaf': 1, 'max_features': 'log2', 'max_depth': 8}best_cv_f1=0.972top_3_grid_resultsscore=0.968 params={'max_depth': 5, 'min_samples_leaf': 3, 'n_estimators': 80}score=0.968 params={'max_depth': 5, 'min_samples_leaf': 3, 'n_estimators': 160}score=0.968 params={'max_depth': None, 'min_samples_leaf': 3, 'n_estimators': 160}
パラメータとハイパーパラメータ
Section titled “パラメータとハイパーパラメータ”ランダムフォレストは、データから分岐ルールを学びます。学習された分岐ルールは parameters です。
人が事前に選ぶ設定には次があります。
n_estimators;max_depth;min_samples_leaf;max_features。
これらが hyperparameters です。学習の進み方を形作ります。
Grid Search
Section titled “Grid Search”Grid search は、列挙したすべての組み合わせを試します。
param_grid={ "n_estimators": [80, 160], "max_depth": [3, 5, None], "min_samples_leaf": [1, 3],}この grid は 2 x 3 x 2 = 12 通りです。5-fold CV なら、60 回モデルを fit します。
Grid search が向いている場面:
- 探索空間が小さい;
- 妥当な候補値がある程度わかっている;
- 単純で再現しやすい基線がほしい。
Random Search
Section titled “Random Search”Random search は、大きな空間から限られた回数だけ組み合わせを抽出します。
n_iter=8実験では、8 通りだけ試しながらより広い空間を探索し、少し高い CV F1 を見つけました。
best_cv_f1=0.972Random search が向いている場面:
- ハイパーパラメータが多い;
- 学習コストが高い;
- 狭い grid を作る前に広く探索したい。
Final Holdout
Section titled “Final Holdout”final holdout は、CV 探索に使っていない部分です。
X_train, X_final, y_train, y_final = train_test_split(...)探索が最良設定を選んだ後、1 回だけ評価します。
final accuracy=0.956 recall=0.972 f1=0.966final holdout を見たあとに grid を変え続けないでください。そうすると final holdout ではなく、調整の一部になります。
探索結果を読む
Section titled “探索結果を読む”Grid の上位結果はかなり近いです。
score=0.968 params={'max_depth': 5, 'min_samples_leaf': 3, 'n_estimators': 80}score=0.968 params={'max_depth': 5, 'min_samples_leaf': 3, 'n_estimators': 160}スコアが同じくらいなら、より単純または安いモデルを選びます。木が多い、深い、というだけでは良いモデルとは限りません。
実用的な調整戦略
Section titled “実用的な調整戦略”| 段階 | 行動 |
|---|---|
| 開始 | まず既定設定で簡単な基線を作る |
| 診断 | bias/variance と指標選択を確認する |
| 最初の探索 | 重要パラメータの小さな grid を試す |
| 広い探索 | 組み合わせが爆発したら random search |
| 最終確認 | untouched holdout で 1 回だけ評価 |
| 本番 | ドリフトと再学習方針を監視する |
経験者向け:Optuna などのベイズ最適化ツールは、1 回の trial が高価なときや探索空間が大きいときに便利です。ただし、きれいな検証設計の代わりにはなりません。
このページを終えたら、この evidence card を残します。
- 評価設定
- 分割、交差検証、指標、ベースライン、比較対象
- 結果
- スコア表、曲線、confusion matrix、検証結果、または検索結果
- 判断
- データ、特徴量、モデル、閾値、またはハイパーパラメータを変えるかどうか
- 失敗確認
- リーク、不安定な検証、誤った指標、またはテストセットでのチューニング
- 期待される成果
- 次のモデリング判断を支える評価記録
よくあるトラブル
Section titled “よくあるトラブル”| 症状 | よくある原因 | 修正 |
|---|---|---|
| 探索が遅すぎる | grid が大きすぎる | 候補値を減らし、random search を使う |
| CV スコアは上がるが final holdout が下がる | 過剰調整 | 探索を単純にし、新しい holdout を残す |
| 最良モデルがかなり複雑 | 指標差が小さい | 安い/単純なモデルを選ぶ |
| 実行ごとに違う params が選ばれる | データが不安定、fold が小さい | repeated CV や分散確認を行う |
| 調整しても改善しない | モデルクラスや特徴量が制限 | 先に特徴量やモデルファミリーを改善する |
- scoring を
"f1"から"recall"に変えてください。最良パラメータは変わりますか? - grid に
max_depth=10を追加してください。CV スコアは改善しますか? n_iterを8から16に増やしてください。追加コストに見合う改善がありますか?cv_results_からmean_fit_timeを表示し、スコアが近いときは安いモデルを選んでください。- 以前の CV だけの実験に、最後まで触らない test set を追加してください。
参考実装と解説
- recall を最適化すると、より多くの positive を拾う攻めたパラメータが選ばれることがあります。その代わり precision や F1 が下がる場合があります。
max_depth=10は前の grid が underfitting していた場合だけ役立ちます。CV スコアが伸びない、またはばらつくなら深いモデルは選びません。n_iterを倍にする価値は、スコア改善が実行時間増に見合うときだけです。ノイズ範囲の小さな改善なら、軽い探索を選びます。mean_fit_timeは同点に近い候補を選ぶときに役立ちます。スコア差が小さいなら、通常は速くて単純なモデルを優先します。- untouched test set はチューニング後に一度だけ使います。モデルやハイパーパラメータ選択に関わっていないデータで最終性能を見積もるためです。
合格チェック
Section titled “合格チェック”次を説明できれば、この節はクリアです。
- ハイパーパラメータは学習前に選ぶ;
- grid search は小さな候補空間を全探索する;
- random search は大きな探索空間に向いている;
- final holdout は繰り返し調整に使ってはいけない;
- 調整では、悪い特徴量や誤った検証設計は救えない。