機械学習モデルの精度は、アルゴリズムの選択だけでなく「ハイパーパラメーター」の設定に大きく左右されます。学習率、木の深さ、正則化の強さ——これらの値をどう決めるかで、同じアルゴリズムでも精度が大きく変わります。この記事では、Python の最適化フレームワーク Optuna を用いたハイパーパラメーターチューニングの手法と、Qast が内部でどのように活用しているかを解説します。
ハイパーパラメーターとは何か
機械学習のパラメーターには「モデルが学習データから自動的に学ぶパラメーター」と「人間が事前に設定するハイパーパラメーター」の2種類があります。例えば、線形回帰の重み(係数)は前者、XGBoost の学習率や木の最大深さは後者です。ハイパーパラメーターの設定が不適切だと、過学習(学習データに適合しすぎて汎化性能が落ちる)や学習不足(モデルがパターンを十分に学習できない)を引き起こします。
- 1
学習率(learning_rate)
勾配ブースティングでモデルの更新幅を制御します。小さすぎると学習が遅く、大きすぎると最適解を飛び越えてしまいます。
- 2
木の最大深さ(max_depth)
決定木の深さの上限です。深すぎると過学習、浅すぎると学習不足になります。
- 3
正則化パラメーター(lambda, alpha)
モデルの複雑さにペナルティを課す強さです。適切な値は過学習を抑えつつ精度を維持します。
- 4
サブサンプリング比率(subsample)
各イテレーションで使用するデータの割合です。1.0未満にすることでモデルの多様性が増し、汎化性能が向上します。
従来の探索手法とその限界
ハイパーパラメーターの探索には、古くからグリッドサーチとランダムサーチが使われてきました。しかし、これらの手法には実用上の課題があります。
- 1
グリッドサーチ
指定した値の全組み合わせを総当たりで試す手法です。パラメーター数が増えると組み合わせが指数的に爆発し、現実的な時間で探索を完了できなくなります。
- 2
ランダムサーチ
パラメーター空間からランダムにサンプリングする手法です。グリッドサーチより効率的ですが、過去の試行結果を活かさないため、良い領域を集中的に探索できません。
研究によると、ハイパーパラメーターの多くはモデル性能に大きな影響を与えないものです。ランダムサーチがグリッドサーチより効率的なのは、重要なパラメーターの探索範囲をより広くカバーできるためです。
Optuna とは — Define-by-Run の最適化フレームワーク
Optuna は、Preferred Networks が開発した Python 製のハイパーパラメーター最適化フレームワークです。最大の特徴は「Define-by-Run」アプローチで、探索空間をPythonコード内で動的に定義できます。これにより、あるパラメーターの値に応じて別のパラメーターの探索範囲を変える、といった条件付き探索空間が自然に記述できます。
import optuna
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
def objective(trial):
"""Optuna が最小化/最大化する目的関数。"""
# パラメーター空間を動的に定義
n_estimators = trial.suggest_int("n_estimators", 50, 500)
max_depth = trial.suggest_int("max_depth", 3, 20)
min_samples_split = trial.suggest_int("min_samples_split", 2, 20)
min_samples_leaf = trial.suggest_int("min_samples_leaf", 1, 10)
clf = RandomForestClassifier(
n_estimators=n_estimators,
max_depth=max_depth,
min_samples_split=min_samples_split,
min_samples_leaf=min_samples_leaf,
random_state=42,
)
# 5-fold CV の平均精度を返す
score = cross_val_score(clf, X_train, y_train, cv=5).mean()
return score
# Study を作成して最適化を実行
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100)
print(f"Best score: {study.best_value:.4f}")
print(f"Best params: {study.best_params}")Optuna の中核概念
- 1
Study(スタディ)
最適化セッション全体を管理するオブジェクトです。最適化の方向(最大化/最小化)や探索履歴を保持します。
- 2
Trial(トライアル)
1回のパラメーター評価を表します。各 Trial はパラメーターの組み合わせと、その評価スコアを持ちます。
- 3
目的関数(Objective Function)
最適化したいスコアを返す関数です。内部で trial.suggest_* メソッドを使ってパラメーターをサンプリングします。
- 4
Sampler(サンプラー)
次に試すパラメーターを決定するアルゴリズムです。デフォルトの TPE や、CMA-ES、ランダムサンプラーなどが用意されています。
TPE — Optuna のデフォルトサンプリング戦略
Optuna のデフォルトサンプラーである TPE(Tree-structured Parzen Estimator)は、ベイズ最適化の一種です。過去の試行結果を「良い結果を出したパラメーター群」と「悪い結果を出したパラメーター群」の2つのグループに分け、良い結果が出やすい領域から優先的にサンプリングします。
具体的には、目的関数の値に基づいてトライアルを上位群(l(x))と下位群(g(x))に分割し、l(x)/g(x) の比率が高い領域を重点的に探索します。これにより、探索の初期段階では広い範囲を探り、試行が進むにつれて有望な領域に集中するという適応的な探索が実現されます。
TPE はパラメーターごとに独立に確率モデルを構築するため、パラメーター間の相互作用は直接モデル化しません。パラメーター間の強い依存関係がある場合は、CMA-ES サンプラーの方が有効なこともあります。
Pruning — 見込みのない試行を早期打ち切り
Optuna の強力な機能の一つが「Pruning(枝刈り)」です。学習の途中経過を監視し、最終的に良い結果を出す見込みが低い試行を途中で打ち切ります。これにより、無駄な計算リソースの消費を大幅に削減できます。
import optuna
from sklearn.model_selection import cross_val_score
from xgboost import XGBClassifier
def objective(trial):
params = {
"n_estimators": trial.suggest_int("n_estimators", 100, 1000),
"max_depth": trial.suggest_int("max_depth", 3, 12),
"learning_rate": trial.suggest_float("learning_rate", 1e-3, 0.3, log=True),
"subsample": trial.suggest_float("subsample", 0.5, 1.0),
"colsample_bytree": trial.suggest_float("colsample_bytree", 0.5, 1.0),
"reg_alpha": trial.suggest_float("reg_alpha", 1e-8, 10.0, log=True),
"reg_lambda": trial.suggest_float("reg_lambda", 1e-8, 10.0, log=True),
}
clf = XGBClassifier(**params, random_state=42, use_label_encoder=False)
# Pruning: 各 fold の途中結果を報告
scores = []
for fold_idx, (train_idx, val_idx) in enumerate(kf.split(X, y)):
clf.fit(X[train_idx], y[train_idx])
score = clf.score(X[val_idx], y[val_idx])
scores.append(score)
# 途中経過を報告 → 見込みが低ければ自動打ち切り
trial.report(sum(scores) / len(scores), fold_idx)
if trial.should_prune():
raise optuna.TrialPruned()
return sum(scores) / len(scores)
study = optuna.create_study(
direction="maximize",
pruner=optuna.pruners.MedianPruner(n_startup_trials=10),
)
study.optimize(objective, n_trials=200)- 1
MedianPruner
各ステップの中間値が、過去の試行の中央値を下回る場合に打ち切ります。Optuna のデフォルト Pruner で、多くのケースで良好に機能します。
- 2
HyperbandPruner
Hyperband アルゴリズムに基づく Pruner です。リソース配分を段階的に行い、大量の試行を少ないリソースで開始して有望な試行にリソースを集中させます。
- 3
SuccessiveHalvingPruner
各ラウンドで下位半分の試行を打ち切り、残りの試行にリソースを倍増させる手法です。並列実行との相性が良好です。
探索空間の設計テクニック
効率的な最適化には、適切な探索空間の設計が不可欠です。以下に、実務で役立つテクニックを紹介します。
- 1
対数スケールの活用
学習率(0.001〜0.3)や正則化パラメーター(1e-8〜10.0)のように、桁が大きく異なる範囲を探索する場合は log=True を指定します。これにより各桁が均等にサンプリングされます。
- 2
条件付き探索空間
アルゴリズムの選択に応じて異なるパラメーターを探索する場合、if 文で分岐させます。例えば、SVM を選んだ場合のみカーネルの種類を探索するといった設計が可能です。
- 3
カテゴリカルパラメーターの活用
trial.suggest_categorical() を使えば、アルゴリズム名や前処理手法など離散的な選択肢も統一的に探索できます。
def objective(trial):
# アルゴリズム自体も探索対象にできる
algo = trial.suggest_categorical("algorithm", ["rf", "xgb", "lgbm"])
if algo == "rf":
params = {
"n_estimators": trial.suggest_int("rf_n_estimators", 50, 500),
"max_depth": trial.suggest_int("rf_max_depth", 3, 20),
}
model = RandomForestClassifier(**params)
elif algo == "xgb":
params = {
"n_estimators": trial.suggest_int("xgb_n_estimators", 100, 1000),
"learning_rate": trial.suggest_float("xgb_lr", 1e-3, 0.3, log=True),
"max_depth": trial.suggest_int("xgb_max_depth", 3, 12),
}
model = XGBClassifier(**params)
else: # lgbm
params = {
"n_estimators": trial.suggest_int("lgbm_n_estimators", 100, 1000),
"learning_rate": trial.suggest_float("lgbm_lr", 1e-3, 0.3, log=True),
"num_leaves": trial.suggest_int("lgbm_num_leaves", 15, 127),
}
model = LGBMClassifier(**params)
return cross_val_score(model, X, y, cv=5).mean()結果の可視化と分析
Optuna には探索結果を分析するための可視化機能が豊富に用意されています。最適化の履歴やパラメーターの重要度を視覚的に把握することで、モデル改善の方向性が明確になります。
import optuna.visualization as vis
# 最適化の履歴(各トライアルのスコア推移)
vis.plot_optimization_history(study)
# パラメーターの重要度(どのパラメーターがスコアに最も影響するか)
vis.plot_param_importances(study)
# パラメーター間の関係をヒートマップで表示
vis.plot_contour(study, params=["learning_rate", "max_depth"])
# 各パラメーターとスコアの関係をスライスプロットで確認
vis.plot_slice(study)
# 並行座標プロット(全パラメーターの組み合わせを俯瞰)
vis.plot_parallel_coordinate(study)- 1
Optimization History
試行回数に対するスコアの推移を表示します。探索が収束しているか、まだ改善の余地があるかを判断できます。
- 2
Parameter Importances
各パラメーターがスコアに与える影響度をランキングで表示します。重要度の低いパラメーターは固定値にして探索空間を削減する判断材料になります。
- 3
Contour Plot
2つのパラメーターの組み合わせに対するスコアの等高線図です。パラメーター間の相互作用を視覚的に把握できます。
Qast における自動ハイパーパラメーターチューニング
Qast では、内部的に Optuna を活用して39種類のアルゴリズムそれぞれのハイパーパラメーターを自動チューニングしています。ユーザーが CSV をアップロードして学習を開始するだけで、以下のプロセスが裏側で自動実行されます。
- 1
データ特性の分析
データのサイズ、特徴量の数と型、クラスの偏り、欠損値の割合などを分析し、各アルゴリズムの初期探索範囲を動的に調整します。
- 2
アルゴリズムごとの並列探索
39種類のアルゴリズムに対して、Optuna の TPE サンプラーでハイパーパラメーターを並列に探索します。Pruning により、見込みのない組み合わせは早期に打ち切られます。
- 3
交差検証によるロバストな評価
各パラメーター候補は k-fold 交差検証で評価され、過学習していないモデルが選ばれるよう設計されています。
- 4
リーダーボードへの反映
最適なハイパーパラメーターで学習された各モデルが、リーダーボードに精度メトリクスとともに一覧表示されます。
Qast のハイパーパラメーターチューニングはすべて自動で行われるため、ユーザーがパラメーターを手動設定する必要はありません。Optuna の仕組みを知っておくと、リーダーボードの結果をより深く理解し、モデル選定の判断に役立てることができます。
まとめ
ハイパーパラメーターチューニングは、機械学習モデルの性能を引き出すために不可欠なプロセスです。Optuna の TPE サンプラーによるベイズ最適化と Pruning による早期打ち切りにより、従来のグリッドサーチやランダムサーチと比べて大幅に効率的な探索が可能になります。Qast はこれらの技術を内部で自動活用し、ユーザーが手間なく最適なモデルを得られる仕組みを提供しています。


