よくある質問

Optuna は X と組み合わせて使えますか?(X はお好みの機械学習ライブラリ)

Optuna はほとんどの機械学習ライブラリと互換性があり、それらと組み合わせて簡単に使用できます。 サンプルコード を参照してください。

独自の引数を持つ目的関数の定義方法

2つの方法があります。

まず、呼び出し可能なクラスを以下のように使用できます:

import optuna


class Objective:
    def __init__(self, min_x, max_x):
        # 実装固有の引数をクラスのフィールドとして保持します。
        self.min_x = min_x
        self.max_x = max_x

    def __call__(self, trial):
        # 追加の引数を使用して目的関数値を計算します。
        x = trial.suggest_float("x", self.min_x, self.max_x)
        return (x - 2) ** 2


# `Objective` インスタンスを使用して最適化を実行します。
study = optuna.create_study()
study.optimize(Objective(-100, 100), n_trials=100)

次に、追加の引数を保持する関数(クロージャ)を作成するには、lambda または functools.partial を使用できます。 以下は lambda を使用した例です:

import optuna

# 3つの引数を取る目的関数
def objective(trial, min_x, max_x):
    x = trial.suggest_float("x", min_x, max_x)
    return (x - 2) ** 2


# 追加の引数
min_x = -100
max_x = 100

# 上記の目的関数を `lambda` でラップして最適化を実行
study = optuna.create_study()
study.optimize(lambda trial: objective(trial, min_x, max_x), n_trials=100)

また、sklearn_additional_args.py の例では、 各トライアル実行時にデータセットを読み込むのではなく、再利用しています。

リモート RDB サーバーなしで Optuna を使用できますか?

はい、可能です。

最も単純な形式では、Optuna は InMemoryStorage で動作します:

study = optuna.create_study()
study.optimize(objective)

スタディを保存して再開したい場合は、ローカルストレージとして SQLite を使用するのが便利です:

study = optuna.create_study(study_name="foo_study", storage="sqlite:///example.db")
study.optimize(objective)  # `study` の状態がローカルの SQLite ファイルに保存されます。

詳細は RDBバックエンドを使用したスタディの保存/再開 を参照してください。

スタディを保存して再開する方法

スタディを保存する方法は、 InMemoryStorage (デフォルト)かリレーショナルデータベース(RDB)かによって異なります。 インメモリスタディは picklejoblib を使用して通常の Python オブジェクトと同様に保存/読み込みできます。 例えば joblib を使用する場合:

study = optuna.create_study()
joblib.dump(study, "study.pkl")

スタディを再開するには:

study = joblib.load("study.pkl")
print("今までの最高トライアル:")
print(" 値: ", study.best_trial.value)
print(" パラメータ: ")
for key, value in study.best_trial.params.items():
    print(f"    {key}: {value}")

なお、Optuna は pickle を使用して異なる Optuna バージョン間での保存/読み込みをサポートしていません。 異なる Optuna バージョン間でスタディを保存/読み込みする場合は、RDB を使用し、 必要に応じて ストレージスキーマをアップグレード してください。 RDB を使用している場合は、RDBバックエンドを使用したスタディの保存/再開 の詳細を参照してください。

Optuna のログメッセージを非表示にする方法

デフォルトでは、Optuna は optuna.logging.INFO レベルでログメッセージを表示します。 optuna.logging.set_verbosity() を使用してログレベルを変更できます。

例えば、各トライアルの結果を表示しないようにするには:

optuna.logging.set_verbosity(optuna.logging.WARNING)

study = optuna.create_study()
study.optimize(objective)
# 「[I 2020-07-21 13:41:45,627] Trial 0 finished with value:...」のようなログは表示されなくなります。

詳細は optuna.logging を参照してください。

目的関数で学習した機械学習モデルを保存する方法

Optuna はハイパーパラメータ値と対応する目的値をストレージに保存しますが、 機械学習モデルやニューラルネットワークの重みなどの中間オブジェクトは破棄されます。

モデルや重みを保存するには、Optuna の組み込み ArtifactStore の利用を推奨します。 例えば upload_artifact() を以下のように使用できます:

base_path = "./artifacts"
os.makedirs(base_path, exist_ok=True)
artifact_store = optuna.artifacts.FileSystemArtifactStore(base_path=base_path)

def objective(trial):
    svc_c = trial.suggest_float("svc_c", 1e-10, 1e10, log=True)
    clf = sklearn.svm.SVC(C=svc_c)
    clf.fit(X_train, y_train)

    # ArtifactStore を使用してモデルを保存
    with open("model.pickle", "wb") as fout:
        pickle.dump(clf, fout)
    artifact_id = optuna.artifacts.upload_artifact(
        artifact_store=artifact_store,
        file_path="model.pickle",
        study_or_trial=trial.study,
    )
    trial.set_user_attr("artifact_id", artifact_id)
    return 1.0 - accuracy_score(y_valid, clf.predict(X_valid))

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

モデルや重みを取得するには、get_all_artifact_meta()download_artifact() を使用して 以下のように一覧表示/ダウンロードできます:

# 全てのモデルを一覧表示
for artifact_meta in optuna.artifacts.get_all_artifact_meta(study_or_trial=study):
    print(artifact_meta)
# 最高のモデルをダウンロード
trial = study.best_trial
best_artifact_id = trial.user_attrs["artifact_id"]
optuna.artifacts.download_artifact(
    artifact_store=artifact_store,
    file_path='best_model.pickle',
    artifact_id=best_artifact_id,
)

より詳細なガイドについては、ArtifactStore チュートリアル を参照してください。

再現性のある最適化結果を得る方法

Optuna が提案するパラメータを再現性のあるものにするには、samplers のインスタンスの seed 引数で 固定の乱数シードを指定します:

sampler = TPESampler(seed=10)  # サンプラーを決定論的に動作させる
study = optuna.create_study(sampler=sampler)
study.optimize(objective)

HyperbandPruner によるプルーニングを再現性のあるものにするには、 seed 引数に加えて Studystudy_name を固定指定してください。

ただし、2つの注意点があります。

まず、分散/並列モードでスタディを最適化する場合、本質的に非決定性が存在します。 そのため、このような条件下では同じ結果を再現することは非常に困難です。 結果を再現したい場合は、スタディの最適化を逐次的に実行することをお勧めします。

次に、目的関数が非決定的な動作をする場合(つまり、同じパラメータが提案されても 同じ値が返されない場合)、最適化を再現することはできません。 この問題に対処するには、最適化対象(例:MLライブラリ)が提供しているオプション(例:乱数シード)を 設定して動作を決定論的にする必要があります。

トライアルで発生した例外はどのように処理されますか?

例外をキャッチせずに発生するトライアルは、FAIL ステータスで 失敗として扱われます。

デフォルトでは、optimize() の呼び出し元に TrialPruned 以外の例外がすべて伝播されます。 つまり、このような例外が発生するとスタディは中断されます。 残りのトライアルでスタディを継続したい場合、optimize()catch 引数で捕捉する例外タイプを指定できます。 このタイプの例外はスタディ内で捕捉され、それ以上伝播されません。

失敗したトライアルはログメッセージで確認できます。

[W 2018-12-07 16:38:36,889] Setting status of trial#0 as TrialState.FAIL because of \
the following error: ValueError('A sample error in objective.')

また、以下のようにトライアルの状態を確認して失敗したトライアルを見つけることもできます:

study.trials_dataframe()

number

state

value

params

system_attrs

0

TrialState.FAIL

0

Setting status of trial#0 as TrialState.FAIL because of the following error: ValueError(‘A test error in objective.’)

1

TrialState.COMPLETE

1269

1

See also

optimize()catch 引数

トライアルが NaN を返した場合の処理方法は?

トライアルが NaN (float('nan')) を返した場合、失敗として扱われますが、 スタディは中断されません。

NaN を返したトライアルは以下のように表示されます:

[W 2018-12-07 16:41:59,000] Setting status of trial#2 as TrialState.FAIL because the \
objective function returned nan.

探索空間を動的に変更した場合の動作は?

パラメータの探索空間は提案 API の呼び出しごとに指定されるため、 例えば suggest_float()suggest_int() のように、 単一のスタディ内で異なるトライアルが異なる探索空間からパラメータをサンプリングすることで、 探索空間の範囲を変更することが可能です。 変更時の動作は各サンプラーごとに定義されます。

Note

TPE サンプラーに関する議論: https://github.com/optuna/optuna/issues/822

2つのトライアルを同時に評価するために2つのGPUを使用するには?

最適化対象がGPU(CUDA)アクセラレーションをサポートしており、 スクリプト内で使用するGPUを指定したい場合、 main.pyCUDA_VISIBLE_DEVICES 環境変数を設定するのが最も簡単です:

# ターミナルで
#
# 最初のGPUを使用するように指定し、最適化を実行
$ export CUDA_VISIBLE_DEVICES=0
$ python main.py


# 別のターミナルで
#
# 2番目のGPUを使用するように指定し、別の最適化を実行
$ export CUDA_VISIBLE_DEVICES=1
$ python main.py

詳細は CUDA C Programming Guide を参照してください。

目的関数のテスト方法は?

目的関数をテストする際、サンプリングされた値ではなく固定のパラメータ値を使用したい場合があります。 その場合は、与えられたパラメータ辞書に基づいて固定のパラメータ値を提案する FixedTrial を使用できます。 例えば、目的関数 \(x + y\)\(x\)\(y\) の任意の値を以下のように入力できます:

def objective(trial):
    x = trial.suggest_float("x", -1.0, 1.0)
    y = trial.suggest_int("y", -5, 5)
    return x + y


objective(FixedTrial({"x": 1.0, "y": -1}))  # 0.0
objective(FixedTrial({"x": -1.0, "y": -4}))  # -5.0

FixedTrial を使用すると、以下のようにユニットテストを記述できます:

# pytest のテスト関数
def test_objective():
    assert 1.0 == objective(FixedTrial({"x": 1.0, "y": 0}))
    assert -1.0 == objective(FixedTrial({"x": 0.0, "y": -1}))
    assert 0.0 == objective(FixedTrial({"x": -1.0, "y": 1}))

スタディの最適化時にメモリ不足(OOM)を回避する方法は?

トライアルを実行するにつれてメモリ使用量が増加する場合、定期的にガベージコレクタを実行してください。 optimize() を呼び出す際に gc_after_trialTrue に設定するか、 コールバック内で gc.collect() を呼び出してください。

def objective(trial):
    x = trial.suggest_float("x", -1.0, 1.0)
    y = trial.suggest_int("y", -5, 5)
    return x + y

study = optuna.create_study()
study.optimize(objective, n_trials=10, gc_after_trial=True)

# `gc_after_trial=True` は以下とほぼ同じです
study.optimize(objective, n_trials=10, callbacks=[lambda study, trial: gc.collect()])

ガベージコレクタを実行するとパフォーマンスに影響が出る可能性があり、 目的関数の実行速度によっては無視できない場合があります。 そのため、gc_after_trial はデフォルトで False に設定されています。 上記の例は、TrialPruned などのエラーが発生した場合でも gc.collect() が呼び出される点を除けば、目的関数内でガベージコレクタを実行するのと似ています。

Note

ChainerMNStudy は現在、 optimize()gc_after_trial やコールバックをサポートしていません。 このクラスを使用する場合、目的関数内でガベージコレクタを呼び出す必要があります。

最適な値が更新された時だけログを出力する方法は?

optuna のログ機能を独自のログコールバック関数で置き換える方法です。 実装したコールバックは optimize() に渡すことができます。 以下に例を示します:

import optuna


# optuna のログレベルを WARN に設定
optuna.logging.set_verbosity(optuna.logging.WARN)


def objective(trial):
    x = trial.suggest_float("x", 0, 1)
    return x ** 2

def logging_callback(study, frozen_trial):
    previous_best_value = study.user_attrs.get("previous_best_value", None)
    if previous_best_value != study.best_value:
        study.set_user_attr("previous_best_value", study.best_value)
        print(
            "Trial {} finished with best value: {} and parameters: {}. ".format(
            frozen_trial.number,
            frozen_trial.value,
            frozen_trial.params,
            )
        )

study = optuna.create_study()
study.optimize(objective, n_trials=100, callbacks=[logging_callback])

このコールバックは、n_jobs!=1 の場合(分散最適化など)、 ストレージへの読み書きが競合状態になる可能性があるため、 誤った値を表示する可能性があることに注意してください。

ディリクレ分布に従う割合変数を提案するには?

\(n\) 個の変数 \(p[0], p[1], ..., p[n-1]\) を提案し、 任意の \(k\) に対して \(0 \le p[k] \le 1\) かつ \(p[0] + p[1] + ... + p[n-1] = 1\) を満たす場合、以下の方法を試してみてください。 例えば、これらの変数は損失関数の重みとして使用できます。 これらの変数はフラットな ディリクレ分布 に従います。

import numpy as np
import matplotlib.pyplot as plt
import optuna

def objective(trial):
    n = 5
    x = []
    for i in range(n):
        x.append(- np.log(trial.suggest_float(f"x_{i}", 0, 1)))

    p = []
    for i in range(n):
        p.append(x[i] / sum(x))

    for i in range(n):
        trial.set_user_attr(f"p_{i}", p[i])

    return 0

study = optuna.create_study(sampler=optuna.samplers.RandomSampler())
study.optimize(objective, n_trials=1000)

n = 5
p = []
for i in range(n):
    p.append([trial.user_attrs[f"p_{i}"] for trial in study.trials])
axes = plt.subplots(n, n, figsize=(20, 20))[1]

for i in range(n):
    for j in range(n):
        axes[j][i].scatter(p[i], p[j], marker=".")
        axes[j][i].set_xlim(0, 1)
        axes[j][i].set_ylim(0, 1)
        axes[j][i].set_xlabel(f"p_{i}")
        axes[j][i].set_ylabel(f"p_{j}")

plt.savefig("sampled_ps.png")

この手法の正当性は以下の通りです: まず、一様分布 \(Uni(0, 1)\) からサンプリングした変数 \(u\) に 変換 \(x = - \log (u)\) を適用すると、変数 \(x\) は スケールパラメータ \(1\) の指数分布 \(Exp(1)\) に従います。 さらに、スケールパラメータ \(1\) の指数分布に従う 独立な変数 \(x[0], ..., x[n-1]\)\(p[i] = x[i] / \sum_i x[i]\) で正規化すると、 ベクトル \(p\) はスケールパラメータ \(\alpha = (1, ..., 1)\) の ディリクレ分布 \(Dir(\alpha)\) に従います。 ヤコビ行列の要素を計算することでこの変換を検証できます。

制約付きモデルの最適化方法は?

制約付きモデルを最適化する場合、以下のクラスを使用できます: TPESamplerNSGAIISampler または BoTorchSampler。 以下の例は、NSGAIISampler を使用した Binh と Korn 関数のベンチマークで、制約付き多目的最適化の例です。 この関数には2つの制約 \(c_0 = (x-5)^2 + y^2 - 25 \le 0\)\(c_1 = -(x - 8)^2 - (y + 3)^2 + 7.7 \le 0\) があり、 これらの制約を満たす最適解を求めます。

import optuna

def objective(trial):
    # Binh と Korn 関数(制約付き)
    x = trial.suggest_float("x", -15, 30)
    y = trial.suggest_float("y", -15, 30)

    # 制約条件(0以下なら有効)
    # 有効領域は(x=5, y=0)を中心とする円と
    # (x=8, y=-3)を中心とする円の補集合の交点
    c0 = (x - 5) ** 2 + y ** 2 - 25
    c1 = -((x - 8) ** 2) - (y + 3) ** 2 + 7.7

    # 最適化後に復元できるよう制約を保存
    trial.set_user_attr("constraint", (c0, c1))


    v0 = 4 * x ** 2 + 4 * y ** 2
    v1 = (x - 5) ** 2 + (y - 5) ** 2


    return v0, v1

def constraints(trial):
    return trial.user_attrs["constraint"]

sampler = optuna.samplers.NSGAIISampler(constraints_func=constraints)
study = optuna.create_study(
    directions=["minimize", "minimize"],
    sampler=sampler,
)
study.optimize(objective, n_trials=32, timeout=600)

print("Number of finished trials: ", len(study.trials))

print("Pareto front:")

trials = sorted(study.best_trials, key=lambda t: t.values)

for trial in trials:
    print("  Trial#{}".format(trial.number))
    print(
        "    Values: Values={}, Constraint={}".format(
            trial.values, trial.user_attrs["constraint"][0]
        )
    )
    print("    Params: {}".format(trial.params))

BoTorchSampler の使用例については、 こちらのサンプルコード を参照してください。

制約付き最適化には、ソフト制約とハード制約の2種類があります。 ソフト制約は満たされなくても構いませんが、満たされない場合は目的関数にペナルティが課されます。 一方、ハード制約は必ず満たさなければなりません。

Optuna はソフト制約のみをサポートしており、 ハード制約はサポートしていません。つまり、Optuna には ハード制約用の組み込みサンプラーは用意されていません。

最適化を並列化する方法は?

並列化には以下3つのケースがあります:

  1. 単一ノードでのマルチスレッド並列化

  2. 単一ノードでのマルチプロセス並列化

  3. 複数ノードでのマルチプロセス並列化

1. 単一ノードでのマルチスレッド並列化

optuna.study.Study.optimize() の引数 n_jobs を設定することで 並列化が可能です。ただし、n_jobs!=1 の場合の optuna.study.Study.optimize() はマルチスレッドを使用するため、 Python コードの実行速度は向上しません。

最適化中、他のサーバーリクエストの待機や numpy を使用した C/C++ 処理など、特定の状況では高速化されますが、 それ以外のケースでは高速化されません。

  1. の詳細については、APIReference を参照してください。

2. 単一ノードでのマルチプロセス並列化

JournalFileBackend または クライアント/サーバー RDB(PostgreSQL や MySQL など)を使用することで 実現できます。

  1. の詳細については、TutorialEasyParallelization を参照してください。

3. 複数ノードでのマルチプロセス並列化

クライアント/サーバー RDB(PostgreSQL や MySQL など)を使用することで 実現できます。ただし、クライアント/サーバー RDB をインストールできない 環境では、複数ノードでのマルチプロセス並列化は実行できません。

  1. の詳細については、TutorialEasyParallelization を参照してください。

SQLite3 で並列最適化を実行する際に発生するエラーを解決するには?

SQLite3 を並列最適化に使用することを推奨しない理由は以下の通りです:

  • enqueue_trial() でキューされたトライアルを 同時に評価するため、RDBStorageSELECT … FOR UPDATE 構文を使用します。しかし、SQLite3 では SQLite3 で サポートされていません。

  • SQLAlchemy のドキュメント に記載されている通り、 SQLite3(および pysqlite ドライバ)は高いレベルの並行性をサポートしていません。 データベース接続(実際にはファイルハンドル)に対して1つのスレッド/プロセスが 排他ロックを保持している場合、別のスレッドがロックの解放を待機して “database is locked” エラーが発生する可能性があります。 optuna.storages.RDBStorage(“sqlite:///example.db”, engine_kwargs={“connect_args”: {“timeout”: 20.0}}) のように デフォルトの timeout 値を 増やすことで対処可能です。

  • NFS を介した分散最適化では、SQLite3 は sqlite.org の FAQ セクションで説明されているように 動作しません。

これらのシナリオでファイルベースの Optuna ストレージを使用したい場合は、 代わりに JournalFileBackend の使用を検討してください。

import optuna
from optuna.storages import JournalStorage
from optuna.storages.journal import JournalFileBackend

storage = JournalStorage(JournalFileBackend("optuna_journal_storage.log"))

study = optuna.create_study(storage=storage)
...

詳細は Medium ブログ投稿 を参照してください。

予期せず終了したトライアルを監視し、自動的に失敗させることは可能ですか?

Note

ハートビート機構は実験段階です。API は将来的に変更される可能性があります。

クラスター環境のジョブスケジューラなどによって、トライアルを実行するプロセスが 予期せず終了する場合があります。 トライアルが予期せず終了した場合、ストレージには RUNNING 状態のまま残り、 手動で削除または状態を更新するまでその状態が維持されます。 このような場合、Optuna は heartbeat 機構を使用して トライアルを監視することをサポートしています。 ハートビートを使用すると、トライアルを実行するプロセスが予期せず終了した場合、 Optuna は自動的にそのプロセスで実行されていたトライアルの状態を RUNNING から FAIL に 変更します。

import optuna

def objective(trial):
    (非常に時間のかかる計算)

# 60 秒ごとにハートビートを記録します。
# 最後のハートビート記録から 120 秒以上経過した他のプロセスのトライアルは
# 自動的に失敗とみなされます。
storage = optuna.storages.RDBStorage(url="sqlite:///:memory:", heartbeat_interval=60, grace_period=120)
study = optuna.create_study(storage=storage)
study.optimize(objective, n_trials=100)

Note

ハートビートは optimize() で使用することを想定しています。 ask()tell() を使用する場合は、 tell() を明示的に呼び出して 終了したトライアルの状態を更新してください。

失敗したトライアルを処理するコールバック関数を実行することも可能です。 Optuna は失敗したトライアルを再試行するコールバックとして RetryFailedTrialCallback を提供しています。 コールバックはトライアルの開始時に呼び出されるため、 RetryFailedTrialCallback は 新しいトライアルが評価を開始するたびに失敗したトライアルを再試行します。

import optuna
from optuna.storages import RetryFailedTrialCallback


storage = optuna.storages.RDBStorage(
    url="sqlite:///:memory:",
    heartbeat_interval=60,
    grace_period=120,
    failed_trial_callback=RetryFailedTrialCallback(max_retry=3),
)

study = optuna.create_study(storage=storage)

パラメータとして順列を扱うにはどうすればよいですか?

既存の API では順列のような組み合わせ探索空間を直接扱うのは容易ではありませんが、 便利な手法が存在します。 この手法では、\(n\) 個の項目からなる順列探索空間を、 独立した \(n\) 次元整数探索空間として再パラメータ化します。 この手法は レーマー符号 の概念に基づいています。

レーマー符号とは、同じサイズの整数列であり、 その \(i\) 番目の要素が順列の \(i\) 番目の要素より後にある 要素の数を表します。 つまり、レーマー符号の \(i\) 番目の要素は、 元の順列の \(i\) 番目の要素より後にあり、 かつ小さい要素の数を表します。 例えば、順列 \((3, 1, 4, 2, 0)\) のレーマー符号は \((3, 1, 2, 1, 0)\) です。

レーマー符号は順列を整数空間に一意に符号化するだけでなく、 いくつかの望ましい性質を持っています。 例えば、レーマー符号の要素の総和は、 対応する順列を恒等順列に変換するために必要な 隣接交換の最小回数と等しくなります。 さらに、2 つの順列の符号化の辞書順は、 元の順列の辞書順と同じです。 したがって、レーマー符号は最適化アルゴリズムにとって重要な、 順列間の「近さ」を保持します。 Euclid TSP を解く Optuna の実装例を以下に示します:

import numpy as np


import optuna


def decode(lehmer_code: list[int]) -> list[int]:
    """レーマー符号を順列に復号します。

    この関数は整数のリストとして表現されたレーマー符号を、
    順列に復号します。
    """
    all_indices = list(range(n))
    output = []
    for k in lehmer_code:
        value = all_indices[k]
        output.append(value)
        all_indices.remove(value)
    return output

# TSP の都市のユークリッド座標
city_coordinates = np.array(
    [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [2.0, 2.0], [-1.0, -1.0]]
)
n = len(city_coordinates)


def objective(trial: optuna.Trial) -> float:
    # レーマー符号表現で順列を提案します。
    lehmer_code = [trial.suggest_int(f"x{i}", 0, n - i - 1) for i in range(n)]
    permutation = decode(lehmer_code)

    # 提案された経路の総距離を計算します。
    total_distance = 0.0
    for i in range(n):
        total_distance += np.linalg.norm(
            city_coordinates[permutation[i]] - city_coordinates[np.roll(permutation, 1)[i]]
        )
    return total_distance


study = optuna.create_study()
study.optimize(objective, n_trials=10)
lehmer_code = study.best_params.values()
print(decode(lehmer_code))

重複したサンプルを無視するにはどうすればよいですか?

Optuna は過去に評価されたパラメータを提案することがあり、 この問題を回避したい場合は、以下の回避策をお試しください:

import optuna
from optuna.trial import TrialState


def objective(trial):
    # パラメータをサンプリングします。
    x = trial.suggest_int("x", -5, 5)
    y = trial.suggest_int("y", -5, 5)
    # 考慮するすべてのトライアルを取得します。
    # この例では完了したトライアルのみを使用しますが、
    # ユーザーは TrialState.PRUNED や TrialState.FAIL などの他の状態も指定できます。
    states_to_consider = (TrialState.COMPLETE,)
    trials_to_consider = trial.study.get_trials(deepcopy=False, states=states_to_consider)
    # サンプリングした (x, y) を既に評価したかどうかを確認します。
    for t in reversed(trials_to_consider):
        if trial.params == t.params:
            # パラメータが重複している場合は、既存の値を使用します。
            return t.value

    # パラメータが重複していない場合、目的関数を計算します。
    # この例では 2D 球面関数を使用します。
    return x ** 2 + y ** 2

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

スタディにアップロードされたすべてのアーティファクトを削除するにはどうすればよいですか?

Optuna は最適化中の大規模データ保存のために artifacts をサポートしています。 膨大な数の実験を行った後、最適化中に保存されたアーティファクトを削除したい場合があります。

各スタディごとに新しいディレクトリまたはバケットを作成することを強く推奨します。 これにより、ディレクトリまたはバケットを削除することで、 スタディにリンクされたすべてのアーティファクトを完全に削除できます。

ただし、Python スクリプトからアーティファクトを削除する必要がある場合は、 以下のコードを使用できます:

Warning

add_trial()copy_study() は、 Study または Trial にリンクされた アーティファクトファイルをコピーしません。 ソーススタディまたはトライアルからアーティファクトを削除しないように注意してください。 そうしないと、Optuna が外部で remove() を呼び出した場合の 予期しない動作につながる可能性があります。 Optuna のソフトウェア設計上、削除機能を公式にサポートすることは困難であり、 将来的にもサポートする予定はありません。

from optuna.artifacts import get_all_artifact_meta

def remove_artifacts(study, artifact_store):
    # 注意: ``artifact_store.remove`` は内部機能であるため使用は推奨されません。
    storage = study._storage
    for trial in study.trials:
        for artifact_meta in get_all_artifact_meta(trial, storage=storage):
            # 各トライアルについて、``base_path`` にアップロードされたアーティファクトを削除します。
            artifact_store.remove(artifact_meta.artifact_id)