数行で書ける・枝刈り (pruning) ・並列実行 が標準装備。trial.suggest_float('lr', 1e-5, 1e-1, log=True) のように Python の制御フローの中で探索空間を定義 できる。storage に指定すれば、 複数プロセス・複数ノードでの並列実行が可能。機械学習の論文や Kaggle 解法では、 次のような記述をよく見かけます:
この Optuna は、 機械学習モデルの「最適なハイパーパラメータの組み合わせ」を効率的に探索するフレームワークです。 「学習率は 0.01? 0.1?」「木の深さは 5? 8?」を 人手で当てずっぽうに変える のではなく、 過去の試行結果から学習して次の候補を提案 する。 これにより、 同じ計算予算で得られるモデル性能が大きく向上 します。 LightGBM・XGBoost・scikit-learn・PyTorch のいずれとも組み合わせ可能で、 国内外の Kaggle 上位解法の標準ツールです。
あなたは広い遊園地で宝箱を探しているとします。 1 つの場所を掘るのに 1 分かかり、 1 時間しかありません。 どう動くべきでしょうか?
| 戦略 | 動き | たとえ |
|---|---|---|
| グリッドサーチ | 遊園地を 10×10 の網目に区切り、 全マス順番に掘る | 「全部試す」。 確実だが時間が足りない |
| ランダムサーチ | サイコロを振って 60 マスをランダムに選んで掘る | 「運任せ」。 グリッドより効率はよいことが多い |
| Optuna(TPE) | 掘った場所で「冷たい / 温かい」のヒントを得て、 「温かい」方向の周辺を集中的に掘る | 「ヒントを使う」。 限られた試行で高確率に最良点に近づく |
Optuna のデフォルトサンプラー TPE (Tree-structured Parzen Estimator) は、 これまでの試行 (パラメータ → 目的関数値) を次のように扱います:
ハイパーパラメータ空間を $\mathcal{X}$、 目的関数を $f: \mathcal{X} \to \mathbb{R}$ とする。 目標は最小化問題:
過去の試行集合を $D = \{(x_1, y_1), \dots, (x_n, y_n)\}$、 値のしきい値(上位 $\gamma$ 分位点、 デフォルト $\gamma \approx 0.25$)を $y^{*}$ とする:
study.optimize(objective, n_trials=100) なら 100 試行。SSDSE-B 2026 の 47 都道府県(年度パネル)を使って、 「翌年の人口総数を予測する LightGBM 回帰モデル」 のハイパーパラメータを Optuna で 100 trial 探索します。
LGBMRegressor| パラメータ | 範囲 | スケール | 役割 |
|---|---|---|---|
learning_rate | 0.005 〜 0.3 | 対数 | 1 本の木の影響度。 小さいと精度↑だが trial 時間↑ |
num_leaves | 15 〜 255 | 整数 | 木の複雑さ。 大きすぎると過学習 |
max_depth | 3 〜 12 | 整数 | 木の最大深さ。 上限を設けて過学習抑制 |
min_child_samples | 5 〜 100 | 整数 | 葉に必要な最低サンプル数 |
subsample | 0.5 〜 1.0 | 連続 | 行サンプリング率 |
colsample_bytree | 0.5 〜 1.0 | 連続 | 列サンプリング率 |
reg_lambda | 1e-3 〜 10.0 | 対数 | L2 正則化強度 |
learning_rate (寄与 ≈ 0.34) と num_leaves (0.27)。subsample や colsample_bytree は寄与 ≈ 0.05 程度で、 デフォルトでもよかった。
1 2 3 4 5 6 7 8 9 | import optuna def objective(trial): x = trial.suggest_float('x', -10, 10) return (x - 2) ** 2 study = optuna.create_study(direction='minimize') study.optimize(objective, n_trials=100) print(study.best_params, study.best_value) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import pandas as pd import numpy as np import optuna from lightgbm import LGBMRegressor from sklearn.model_selection import GroupKFold from sklearn.metrics import mean_squared_error # データ読み込み df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8-sig', header=1) df = df.sort_values(['地域', '年度']).reset_index(drop=True) df['人口_翌年'] = df.groupby('地域')['A1101'].shift(-1) df = df.dropna(subset=['人口_翌年']) feat_cols = ['A1101', 'A1102', 'A4101', 'A4200', 'A6101', 'B1101'] X = df[feat_cols].values y = df['人口_翌年'].values groups = df['地域'].values def objective(trial): params = { 'learning_rate': trial.suggest_float('learning_rate', 5e-3, 0.3, log=True), 'num_leaves': trial.suggest_int('num_leaves', 15, 255), 'max_depth': trial.suggest_int('max_depth', 3, 12), 'min_child_samples': trial.suggest_int('min_child_samples', 5, 100), 'subsample': trial.suggest_float('subsample', 0.5, 1.0), 'colsample_bytree': trial.suggest_float('colsample_bytree', 0.5, 1.0), 'reg_lambda': trial.suggest_float('reg_lambda', 1e-3, 10.0, log=True), 'n_estimators': 800, 'random_state': 42, 'verbose': -1, } cv = GroupKFold(n_splits=5) rmses = [] for tr, va in cv.split(X, y, groups): model = LGBMRegressor(**params) model.fit(X[tr], y[tr]) pred = model.predict(X[va]) rmses.append(np.sqrt(mean_squared_error(y[va], pred))) return np.mean(rmses) study = optuna.create_study(direction='minimize', sampler=optuna.samplers.TPESampler(seed=42)) study.optimize(objective, n_trials=100, show_progress_bar=True) print('best RMSE :', study.best_value) print('best params:', study.best_params) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from optuna.integration import LightGBMPruningCallback def objective_pruned(trial): params = {# ...上と同じ...} cv = GroupKFold(n_splits=5) rmses = [] for fold, (tr, va) in enumerate(cv.split(X, y, groups)): model = LGBMRegressor(**params) model.fit(X[tr], y[tr], eval_set=[(X[va], y[va])], callbacks=[LightGBMPruningCallback(trial, 'rmse', valid_name='valid_0')]) pred = model.predict(X[va]) rmses.append(np.sqrt(mean_squared_error(y[va], pred))) return np.mean(rmses) study = optuna.create_study(direction='minimize', pruner=optuna.pruners.MedianPruner(n_warmup_steps=50)) study.optimize(objective_pruned, n_trials=200) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | import optuna.visualization as vis # trial 数 vs ベスト値(収束曲線) vis.plot_optimization_history(study).show() # どのパラメータが効いたか(fANOVA) vis.plot_param_importances(study).show() # パラメータ空間の探索軌跡(パラレル座標) vis.plot_parallel_coordinate(study).show() # 2 パラメータの目的値ランドスケープ vis.plot_contour(study, params=['learning_rate', 'num_leaves']).show() |
1 2 3 4 5 6 7 8 9 | # 共通ストレージを指定すれば、複数プロセス・複数マシンで study を共有できる study = optuna.create_study( study_name='ssdse_lgbm_pop', storage='sqlite:///optuna_study.db', direction='minimize', load_if_exists=True, ) # 別ターミナルでも同じコードを起動すれば、自動で trial が分散される study.optimize(objective, n_trials=50) |
learning_rate の範囲を 0.1 〜 0.5 に狭めてしまうと、 真の最適 0.04 を絶対に見つけられない。log=True)を使う、 ② ベスト trial が範囲の端に張り付いていたら範囲を広げて再探索、 ③ 文献・公式ドキュメントの推奨範囲を参考にする。
TPESampler(seed=42)、 モデル側にも random_state=42 を渡す。 numpy/torch のシードも合わせて固定。
directions=['minimize', 'minimize'] として 多目的最適化(NSGA-II) を使い、 パレート最適解を取得。
optuna.samplers.CmaEsSampler() や optuna.samplers.RandomSampler() をベンチマーク比較する。
| 手法/ライブラリ | 特徴 | Optuna との関係 |
|---|---|---|
| グリッドサーチ | 格子状の探索点を全部試す | 少数パラメータ・小範囲で有効。 Optuna でも GridSampler 利用可 |
| ランダムサーチ | 探索空間からランダム抽出 | 多次元では Grid よりよい。 Optuna で RandomSampler |
| ベイズ最適化 (GP) | ガウス過程で目的関数をモデル化 | scikit-optimize、 GPyOpt が代表。 Optuna も内蔵可 |
| Hyperopt | TPE の元祖実装 | Optuna は Hyperopt を進化させた後発 |
| Ray Tune | 分散環境特化、 多数のサンプラー | クラウド・GPU 多数なら Ray が強い |
| scikit-optimize | sklearn 互換 BO | scikit-learn 標準ワークフローに統合しやすい |
| HyperBand / BOHB | 枝刈りに特化 | Optuna の HyperbandPruner として利用可能 |
| 階層 | 概念 | 関係 |
|---|---|---|
| 最上位 | 数理最適化 | Optuna はブラックボックス最適化の実装 |
| 上位 | ハイパーパラメータ最適化 | 機械学習特化の自動チューニング |
| 同列 | Hyperopt, Ray Tune, scikit-optimize | 同種のフレームワーク |
| 内部アルゴリズム | TPE, CMA-ES, NSGA-II | サンプラーとして選択可 |
| 前提 | 交差検証、 損失関数、 機械学習モデル | これらと組み合わせて動く |
| 応用 | AutoML、 NAS(Neural Architecture Search) | Optuna は両者の中核要素 |