効率的な最適化アルゴリズム

Optuna はハイパーパラメータのサンプリングと有望でないトライアルの 効率的な剪定に最新のアルゴリズムを採用することで、効率的なハイパーパラメータ最適化を実現します。

サンプリングアルゴリズム

サンプラーは基本的に、提案されたパラメータ値と評価された目的値の記録を用いて 探索空間を継続的に絞り込んでいき、より良い目的値をもたらすパラメータを 導く最適な探索空間へと導きます。サンプラーがパラメータを提案する仕組みの詳細は BaseSampler を参照してください。

Optuna では以下のサンプリングアルゴリズムを提供しています:

  • GridSampler で実装されたグリッドサーチ

  • RandomSampler で実装されたランダムサーチ

  • TPESampler で実装されたツリー構造化パーゼン推定器 (TPE) アルゴリズム

  • CmaEsSampler で実装された CMA-ES ベースのアルゴリズム

  • GPSampler で実装されたガウス過程ベースのアルゴリズム

  • 部分的に固定されたパラメータを可能にするアルゴリズムを実装した PartialFixedSampler

  • NSGAIISampler で実装された非支配ソート遺伝的アルゴリズム II (NSGA-II)

  • QMCSampler で実装された準モンテカルロサンプリングアルゴリズム

デフォルトのサンプラーは TPESampler です。

サンプラーの切り替え

import optuna

デフォルトでは、Optuna は TPESampler を以下のように使用します:

study = optuna.create_study()
print(f"Sampler is {study.sampler.__class__.__name__}")
Sampler is TPESampler

例えば RandomSamplerCmaEsSampler など 異なるサンプラーを使用したい場合:

study = optuna.create_study(sampler=optuna.samplers.RandomSampler())
print(f"Sampler is {study.sampler.__class__.__name__}")

study = optuna.create_study(sampler=optuna.samplers.CmaEsSampler())
print(f"Sampler is {study.sampler.__class__.__name__}")
Sampler is RandomSampler
Sampler is CmaEsSampler

プルーニングアルゴリズム

Pruners はトレーニングの初期段階で見込みのないトライアルを自動的に停止します (自動早期停止)。 現在 pruners モジュールは単一目的最適化でのみ使用されることを想定しています。

Optuna は以下のプルーニングアルゴリズムを提供します:

ほとんどの例では MedianPruner を使用しますが、 基本的には SuccessiveHalvingPrunerHyperbandPruner の方が優れています (このベンチマーク結果 参照)。

プルーナーの有効化

プルーニング機能を有効にするには、反復トレーニングの各ステップ後に report()should_prune() を呼び出す必要があります。 report() は中間の目的値を定期的に監視します。 should_prune() は事前定義された条件を満たさないトライアルの終了を決定します。

主要な機械学習フレームワーク向けの統合モジュールの使用を推奨します。 専用リストは integration で、使用例は optuna-examples で確認できます。

import logging
import sys

import sklearn.datasets
import sklearn.linear_model
import sklearn.model_selection


def objective(trial):
    iris = sklearn.datasets.load_iris()
    classes = list(set(iris.target))
    train_x, valid_x, train_y, valid_y = sklearn.model_selection.train_test_split(
        iris.data, iris.target, test_size=0.25, random_state=0
    )

    alpha = trial.suggest_float("alpha", 1e-5, 1e-1, log=True)
    clf = sklearn.linear_model.SGDClassifier(alpha=alpha)

    for step in range(100):
        clf.partial_fit(train_x, train_y, classes=classes)

        # 中間の目的関数値を報告
        intermediate_value = 1.0 - clf.score(valid_x, valid_y)
        trial.report(intermediate_value, step)

        # 中間値に基づくプルーニング処理
        if trial.should_prune():
            raise optuna.TrialPruned()

    return 1.0 - clf.score(valid_x, valid_y)

プルーニング条件として中央値停止ルールを設定

# メッセージ表示用のストリームハンドラを追加
optuna.logging.get_logger("optuna").addHandler(logging.StreamHandler(sys.stdout))
study = optuna.create_study(pruner=optuna.pruners.MedianPruner())
study.optimize(objective, n_trials=20)
A new study created in memory with name: no-name-22358b06-8471-4f4a-8c06-25442bf6d0b1
Trial 0 finished with value: 0.23684210526315785 and parameters: {'alpha': 6.002488122330942e-05}. Best is trial 0 with value: 0.23684210526315785.
Trial 1 finished with value: 0.3157894736842105 and parameters: {'alpha': 0.057114819700918584}. Best is trial 0 with value: 0.23684210526315785.
Trial 2 finished with value: 0.3421052631578947 and parameters: {'alpha': 0.054724364377324954}. Best is trial 0 with value: 0.23684210526315785.
Trial 3 finished with value: 0.3157894736842105 and parameters: {'alpha': 0.05765219737235124}. Best is trial 0 with value: 0.23684210526315785.
Trial 4 finished with value: 0.42105263157894735 and parameters: {'alpha': 1.3922767958252884e-05}. Best is trial 0 with value: 0.23684210526315785.
Trial 5 pruned.
Trial 6 pruned.
Trial 7 finished with value: 0.23684210526315785 and parameters: {'alpha': 0.0026351538536296165}. Best is trial 0 with value: 0.23684210526315785.
Trial 8 finished with value: 0.23684210526315785 and parameters: {'alpha': 0.0033304708175182357}. Best is trial 0 with value: 0.23684210526315785.
Trial 9 pruned.
Trial 10 finished with value: 0.3421052631578947 and parameters: {'alpha': 0.00019818926827461317}. Best is trial 0 with value: 0.23684210526315785.
Trial 11 pruned.
Trial 12 pruned.
Trial 13 pruned.
Trial 14 finished with value: 0.052631578947368474 and parameters: {'alpha': 0.009331645147593867}. Best is trial 14 with value: 0.052631578947368474.
Trial 15 finished with value: 0.052631578947368474 and parameters: {'alpha': 0.013439904951087454}. Best is trial 14 with value: 0.052631578947368474.
Trial 16 pruned.
Trial 17 finished with value: 0.02631578947368418 and parameters: {'alpha': 0.010328209370343028}. Best is trial 17 with value: 0.02631578947368418.
Trial 18 pruned.
Trial 19 finished with value: 0.02631578947368418 and parameters: {'alpha': 0.020402078780853126}. Best is trial 17 with value: 0.02631578947368418.

ご覧の通り、いくつかのトライアルは全イテレーションを完了する前にプルーニング(停止)されました。 メッセージの形式は "Trial <トライアル番号> pruned." です。

どのサンプラーとプルーナーを使用するべきか?

optuna/optuna - wiki “Benchmarks with Kurobako” で公開されているベンチマーク結果によると、少なくとも深層学習以外のタスクでは、

ただし、このベンチマークは深層学習タスクを対象としていない点にご注意ください。 深層学習タスクについては、以下の表を参照してください。 この表は Ozaki et al., Hyperparameter Optimization Methods: Overview and Characteristics, in IEICE Trans, Vol.J103-D No.9 pp.615-631, 2020 論文(日本語)に基づいています。

並列計算リソース

カテゴリカル/条件付きハイパーパラメータ

推奨アルゴリズム

制限あり

なし

TPE. 探索空間が低次元かつ連続的なら GP-EI

あり

TPE. 探索空間が低次元かつ連続的なら GP-EI

十分

なし

CMA-ES, ランダムサーチ

あり

ランダムサーチまたは遺伝的アルゴリズム

プルーニング用統合モジュール

Optuna では、プルーニング機構をより簡潔に実装するため、以下のライブラリ向けの統合モジュールを提供しています。

Optuna の統合モジュールの完全な一覧は integration を参照してください。

例えば、LightGBMPruningCallback は、学習イテレーションのロジックを直接変更することなくプルーニングを導入します。 (完全なスクリプトについては example も参照してください)

import optuna.integration

pruning_callback = optuna.integration.LightGBMPruningCallback(trial, 'validation-error')
gbm = lgb.train(param, dtrain, valid_sets=[dvalid], callbacks=[pruning_callback])

Total running time of the script: (0 minutes 2.579 seconds)

Gallery generated by Sphinx-Gallery