軽量かつ汎用性が高く、プラットフォームに依存しないアーキテクチャ

Optuna は Python のみで実装されており、依存関係が少ないのが特徴です。 これにより、Optuna に興味を持ったらすぐに実際の例を試すことができます。

二次関数の最適化例

Optuna は通常ハイパーパラメータの最適化に使用されますが、ここでは例として 単純な二次関数 \((x - 2)^2\) を最適化します。

まず optuna モジュールをインポートします。

import optuna

Optuna では、最適化対象の関数は通常 objective という名前で定義します。

def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 2) ** 2

この関数は \((x - 2)^2\) の値を返します。目的は objective 関数の出力を最小化する x の値を見つけることです。これは「最適化」と呼ばれるプロセスです。 最適化中、Optuna は x の値を変えながら繰り返し objective 関数を呼び出します。

Trial オブジェクトは objective 関数の1回の実行に対応し、 関数が呼び出されるたびに内部的に生成されます。

suggest API(例:suggest_float())は トライアルのパラメータを取得するために objective 関数内で使用されます。 suggest_float() は指定された範囲(例:-10 から 10)で パラメータを一様に選択します。

最適化を開始するには、study オブジェクトを作成し、 以下のように optimize() メソッドに objective 関数を渡します。

study = optuna.create_study()
study.optimize(objective, n_trials=100)

最適なパラメータは以下のように取得できます。

best_params = study.best_params
found_x = best_params["x"]
print("Found x: {}, (x - 2)^2: {}".format(found_x, (found_x - 2) ** 2))
Found x: 2.0062742758255823, (x - 2)^2: 3.9366537135486014e-05

Optuna が見つけた x の値は、最適値である 2 に近いことがわかります。

Note

機械学習のハイパーパラメータ探索では、 通常 objective 関数はモデルの損失や精度を返します。

Study オブジェクト

Optuna の用語を以下のように定義します:

  • トライアル: objective 関数の1回の実行

  • スタディ: 一連のトライアルからなる最適化セッション

  • パラメータ: 最適化対象の変数(例:上記の x

Optuna では study オブジェクトを使用して最適化を管理します。 create_study() は study オブジェクトを返します。 study オブジェクトには最適化結果を分析するための便利なプロパティがあります。

パラメータ名と値の辞書を取得するには:

{'x': 2.0062742758255823}

objective 関数の最良の観測値を取得するには:

study.best_value
3.9366537135486014e-05

最良のトライアルを取得するには:

study.best_trial
FrozenTrial(number=95, state=1, values=[3.9366537135486014e-05], datetime_start=datetime.datetime(2025, 6, 9, 16, 12, 52, 595733), datetime_complete=datetime.datetime(2025, 6, 9, 16, 12, 52, 598792), params={'x': 2.0062742758255823}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=95, value=None)

全てのトライアルを取得するには:

study.trials
for trial in study.trials[:2]:  # 最初の2つのトライアルを表示
    print(trial)
FrozenTrial(number=0, state=1, values=[17.45884935271442], datetime_start=datetime.datetime(2025, 6, 9, 16, 12, 52, 310930), datetime_complete=datetime.datetime(2025, 6, 9, 16, 12, 52, 311580), params={'x': 6.1783787947856545}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=0, value=None)
FrozenTrial(number=1, state=1, values=[1.3058623720181026], datetime_start=datetime.datetime(2025, 6, 9, 16, 12, 52, 311884), datetime_complete=datetime.datetime(2025, 6, 9, 16, 12, 52, 312058), params={'x': 0.8572566464782643}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=1, value=None)

トライアル数を取得するには:

len(study.trials)
100

optimize() を再度実行することで最適化を継続できます。

study.optimize(objective, n_trials=100)

更新されたトライアル数を取得するには:

len(study.trials)
200

objective 関数が簡単すぎて最後の100トライアルで結果が改善しない場合: しかし、結果を再確認できます:

best_params = study.best_params
found_x = best_params["x"]
print("Found x: {}, (x - 2)^2: {}".format(found_x, (found_x - 2) ** 2))
Found x: 1.9997041029333535, (x - 2)^2: 8.755507404997695e-08

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

Gallery generated by Sphinx-Gallery