コンテンツにスキップ

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

ハイパーパラメータ探索手法の比較図

この節では次を確認します。

  • parameters と hyperparameters の違い;
  • GridSearchCV の使い方;
  • 探索空間が大きいときの RandomizedSearchCV
  • final holdout を調整に使わず残す方法;
  • 過剰調整を避ける方法。

ハイパーパラメータチューニングの検証フロー図

ハイパーパラメータ探索空間と予算の図

用語実用上の意味
parameterモデルが fit() 中にデータから学ぶ値
hyperparameter学習前に人が選ぶ設定。たとえば木の深さ
search space探索に試させる候補値
CV score設定を選ぶためのクロスバリデーションスコア
final holdout調整後に 1 回だけ使う、触っていないデータ
budget試せる組み合わせ数や trial 数
Terminal window
python -m pip install -U scikit-learn

tuning_lab.py を作成します。

from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score, recall_score
from 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}")

実行します。

Terminal window
python tuning_lab.py

期待される出力:

Terminal window
grid_search_lab
best_params= {'max_depth': 5, 'min_samples_leaf': 3, 'n_estimators': 80}
best_cv_f1=0.968
final accuracy=0.956 recall=0.972 f1=0.966
random_search_lab
best_params= {'n_estimators': 100, 'min_samples_leaf': 1, 'max_features': 'log2', 'max_depth': 8}
best_cv_f1=0.972
top_3_grid_results
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}
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 は、列挙したすべての組み合わせを試します。

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 は、大きな空間から限られた回数だけ組み合わせを抽出します。

n_iter=8

実験では、8 通りだけ試しながらより広い空間を探索し、少し高い CV F1 を見つけました。

best_cv_f1=0.972

Random search が向いている場面:

  • ハイパーパラメータが多い;
  • 学習コストが高い;
  • 狭い grid を作る前に広く探索したい。

final holdout は、CV 探索に使っていない部分です。

X_train, X_final, y_train, y_final = train_test_split(...)

探索が最良設定を選んだ後、1 回だけ評価します。

final accuracy=0.956 recall=0.972 f1=0.966

final holdout を見たあとに grid を変え続けないでください。そうすると final holdout ではなく、調整の一部になります。

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}

スコアが同じくらいなら、より単純または安いモデルを選びます。木が多い、深い、というだけでは良いモデルとは限りません。

段階行動
開始まず既定設定で簡単な基線を作る
診断bias/variance と指標選択を確認する
最初の探索重要パラメータの小さな grid を試す
広い探索組み合わせが爆発したら random search
最終確認untouched holdout で 1 回だけ評価
本番ドリフトと再学習方針を監視する

経験者向け:Optuna などのベイズ最適化ツールは、1 回の trial が高価なときや探索空間が大きいときに便利です。ただし、きれいな検証設計の代わりにはなりません。

このページを終えたら、この evidence card を残します。

評価設定
分割、交差検証、指標、ベースライン、比較対象
結果
スコア表、曲線、confusion matrix、検証結果、または検索結果
判断
データ、特徴量、モデル、閾値、またはハイパーパラメータを変えるかどうか
失敗確認
リーク、不安定な検証、誤った指標、またはテストセットでのチューニング
期待される成果
次のモデリング判断を支える評価記録
症状よくある原因修正
探索が遅すぎるgrid が大きすぎる候補値を減らし、random search を使う
CV スコアは上がるが final holdout が下がる過剰調整探索を単純にし、新しい holdout を残す
最良モデルがかなり複雑指標差が小さい安い/単純なモデルを選ぶ
実行ごとに違う params が選ばれるデータが不安定、fold が小さいrepeated CV や分散確認を行う
調整しても改善しないモデルクラスや特徴量が制限先に特徴量やモデルファミリーを改善する
  1. scoring を "f1" から "recall" に変えてください。最良パラメータは変わりますか?
  2. grid に max_depth=10 を追加してください。CV スコアは改善しますか?
  3. n_iter8 から 16 に増やしてください。追加コストに見合う改善がありますか?
  4. cv_results_ から mean_fit_time を表示し、スコアが近いときは安いモデルを選んでください。
  5. 以前の CV だけの実験に、最後まで触らない test set を追加してください。
参考実装と解説
  1. recall を最適化すると、より多くの positive を拾う攻めたパラメータが選ばれることがあります。その代わり precision や F1 が下がる場合があります。
  2. max_depth=10 は前の grid が underfitting していた場合だけ役立ちます。CV スコアが伸びない、またはばらつくなら深いモデルは選びません。
  3. n_iter を倍にする価値は、スコア改善が実行時間増に見合うときだけです。ノイズ範囲の小さな改善なら、軽い探索を選びます。
  4. mean_fit_time は同点に近い候補を選ぶときに役立ちます。スコア差が小さいなら、通常は速くて単純なモデルを優先します。
  5. untouched test set はチューニング後に一度だけ使います。モデルやハイパーパラメータ選択に関わっていないデータで最終性能を見積もるためです。

次を説明できれば、この節はクリアです。

  • ハイパーパラメータは学習前に選ぶ;
  • grid search は小さな候補空間を全探索する;
  • random search は大きな探索空間に向いている;
  • final holdout は繰り返し調整に使ってはいけない;
  • 調整では、悪い特徴量や誤った検証設計は救えない。