Note
Go to the end to download the full example code.
軽量かつ汎用性が高く、プラットフォームに依存しないアーキテクチャ
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 オブジェクトには最適化結果を分析するための便利なプロパティがあります。
パラメータ名と値の辞書を取得するには:
study.best_params
{'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)