---
title: "Optunaとは？v5.0の既定TPE変更と枝刈り・並列実行を実装目線で解説"
url: "https://www.issoh.co.jp/tech/details/18671/"
published: 2026-10-21
updated: 2026-10-21
categories: ["AI"]
publisher: "株式会社一創"
---

# Optunaとは？v5.0の既定TPE変更と枝刈り・並列実行を実装目線で解説

Optuna は2026年9月7日に 5.0.0 が公開され、既定の探索アルゴリズムの設定が v1.5 以来はじめて大きく変わりました。単一目的の TPE で multivariate と constant liar が最初から有効になり、多目的の既定も NSGA-II から TPE へ移った点が変更のひとつです。この記事では、Optuna とは何かを目的関数・trial・study の3要素から整理し、動くコードで探索・枝刈り・保存と再開・並列実行までを順に示します。後半では v4系から上げるときに止まる非推奨API と、受託開発で Optuna を採用する条件・見送る場面も言い切ります。

## まとめ：Optunaの正体と、v5.0系で導入前に押さえる変更点

Optuna は、Preferred Networks が開発する MIT ライセンスのハイパーパラメータ探索フレームワークです。利用者は「パラメータを受け取って評価値を返す関数」を書くだけで、Optuna がパラメータを変えながらその関数を繰り返し呼び、評価値の良い組み合わせを探します。探索範囲を Python の if 文やループでその場で組み立てられる点が、設定ファイルで範囲を宣言する道具との違いです。

実装で押さえるのは4つ。既定は TPE で、最初の10試行は無作為に振る。学習ループに途中経過を報告させれば、見込みの薄い試行を枝刈りで止められる。保存先を SQLite や JournalStorage にしておけば、中断した探索の再開と複数プロセスでの並列実行ができる。そして v5.0 では既定サンプラーの挙動が変わったため、v4系の結果と同じ条件で比べたいなら版を固定します。

採用の目安は、連続値を含むパラメータが4本以上あり、1試行が数十秒以上かかる案件です。1試行が数秒で終わる小さなモデルや、検証データが少なく評価が揺れる案件では、Optuna を入れる前にやるべきことがあります。

## Optunaの定義と、目的関数・study・trialで探索を回す仕組み

最初に、Optuna がどこから来た道具で、どういう単位でコードを書くのかを押さえます。

### Preferred NetworksのOSS：2018年公開から5.0への変遷

Optuna は2018年12月にベータ版として公開され、[@IT の公開時の記事](https://atmarkit.itmedia.co.jp/ait/articles/1812/05/news042.html)では、同社の深層学習フレームワーク Chainer だけでなく scikit-learn・XGBoost・LightGBM でも使える道具として紹介されています。2020年1月に v1.0、2022年8月に v3.0 と版を重ね、設計は [KDD 2019 で発表された論文](https://arxiv.org/abs/1907.10902)にまとまっています。ソースは [GitHub の optuna/optuna リポジトリ](https://github.com/optuna/optuna)で公開され、ライセンスは MIT です。

2026年10月時点の最新は 5.0.0（2026-09-07 公開）で、直前の 4.9.0 は2026年6月1日の公開でした。[公式サイト](https://optuna.org/)は Python 3.10 以降を対応範囲として案内しています。新規案件では 5.0系を前提に組むのが素直です。

### 目的関数・trial・studyの3要素で書く最小コードと実行手順

Optuna のコードは3つの部品でできています。評価値を返す目的関数、1回分の試行を表す trial、試行全体を束ねる study です。trial の `suggest_int` や `suggest_float` を呼んだ時点で値が決まり、study の `optimize` が目的関数を指定回数だけ呼び出します。

```
pip install "optuna==5.0.*" scikit-learn
```

```
# tune_rf.py
import optuna
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

X, y = load_breast_cancer(return_X_y=True)


def objective(trial):
    n_estimators = trial.suggest_int("n_estimators", 50, 400)
    max_depth = trial.suggest_int("max_depth", 2, 16, log=True)
    clf = RandomForestClassifier(
        n_estimators=n_estimators, max_depth=max_depth, random_state=0
    )
    return cross_val_score(clf, X, y, cv=5).mean()


study = optuna.create_study(
    direction="maximize",
    sampler=optuna.samplers.TPESampler(seed=42),
)
study.optimize(objective, n_trials=50)
print(study.best_params, study.best_value)
```

`direction` は評価値を大きくしたいなら `"maximize"`、損失のように小さくしたいなら `"minimize"` を渡します。木の深さのように桁で効く値は `log=True` で対数の刻みにする。この2点を外すと、試行回数を増やしても狙った領域が試されません。

### Pythonのif文で探索範囲を組み替えるdefine-by-runの実例

Optuna の設計上の特徴は、探索範囲を実行しながら決める define-by-run です。モデルの種類ごとに振るパラメータが違う場合でも、if 文で分岐させるだけで書けます。

```
from sklearn.svm import SVC


def objective(trial):
    model = trial.suggest_categorical("model", ["svc", "rf"])
    if model == "svc":
        c = trial.suggest_float("svc_c", 1e-3, 1e2, log=True)
        clf = SVC(C=c)
    else:
        depth = trial.suggest_int("rf_max_depth", 2, 32, log=True)
        clf = RandomForestClassifier(max_depth=depth, random_state=0)
    return cross_val_score(clf, X, y, cv=5).mean()
```

SVC を選んだ試行では `rf_max_depth` が登場しません。範囲を事前に宣言する方式では、使われないパラメータを無視させる記述が別途要ります。条件付きの探索範囲が多い案件ほど、この差がコード量に出ます。

## v5.0で変わった既定のTPEと、サンプラーを選び替える判断基準

「次にどの値を試すか」を決める部品をサンプラーと呼びます。指定しなければ TPE が使われ、その中身が v5.0 で変わりました。

### multivariateとconstant\_liarが既定で有効になったTPEの挙動変化

[v5.0.0 のリリースノート](https://github.com/optuna/optuna/releases/tag/v5.0.0)によれば、単一目的の TPESampler で2つの設定が既定になりました。ひとつは multivariate で、パラメータ同士の関係をまとめて推定します。学習率と木の本数のように片方を動かすと他方の良い値がずれる組み合わせで、独立に推定するより当たりやすくなる設定です。

もうひとつの constant liar は、並列実行中でまだ結果の出ていない試行の周辺を避ける仕組みです。並列で回すと、結果待ちの試行と似た値が同時に試される無駄が起きやすく、それを抑える狙いがあります。加えて多目的の既定は NSGAIISampler から TPESampler に替わり、ベンチマークに基づいて選び直したと説明されています。

実務上の影響は再現性に出ます。同じ `seed` を渡しても、v4系と v5.0 では提案される値の列が一致しません。

### 最初の10試行はランダムで動くn\_startup\_trialsの意味と調整

[TPESampler の公式リファレンス](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.TPESampler.html)では、`n_startup_trials` の既定値は10です。同じ study で10試行が終わるまでは TPE ではなく無作為に値を振り、そこから先で過去の結果をもとにした提案が始まります。

この仕様から、総試行数が20回程度の探索では TPE の恩恵は半分しか出ないと分かります。試行が10回に満たない予算なら、サンプラーの違いはほぼ結果に表れません。逆に1試行が重くて総数を30回しか取れない場合は、`n_startup_trials` を5前後へ下げて推定を早めに始める調整が効きます。

### GPSampler・CmaEsSamplerへ切り替える試行予算の目安

[サンプラー一覧の比較表](https://optuna.readthedocs.io/en/stable/reference/samplers/index.html)には、各手法が想定する試行回数の目安が載っています。選び替えの判断は、何回試せるかと、カテゴリ値や条件分岐を含むかの2軸で決まります。

| サンプラー         | 公式表の推奨試行数  | 向いている場面              |
| ------------- | ---------- | -------------------- |
| TPESampler    | 100〜1000   | カテゴリ値と条件分岐を含む一般的な探索  |
| GPSampler     | 500以下      | 連続値中心で1試行が重く、試行を絞る場面 |
| CmaEsSampler  | 1000〜10000 | 連続値だけで試行を大量に回せる場面    |
| RandomSampler | 制限なし       | 比較の基準線や、ごく少ない試行数     |

実務ではまず既定の TPE で始め、連続値だけで1試行が数分以上かかるなら GPSampler を試す、という順で足ります。GPSampler は v5.0 で安定版の扱いになり、実行中の試行を考慮した並列向けの獲得関数も入りました。CmaEsSampler はカテゴリ値が苦手で、制約付きの探索では使わないよう警告が出るようになっています。

## 学習ループに枝刈りを組み込む実装手順と、Prunerの選定基準

試行の数を増やすより、ダメな試行を早く止めるほうが総時間は縮みます。Optuna ではこれを枝刈り（pruning）と呼びます。

### trial.reportとshould\_pruneで見込みの薄い試行を止める実装例

枝刈りは、学習の各ステップで途中の評価値を `trial.report` に渡し、`trial.should_prune` が真なら `optuna.TrialPruned` を送出する、という3行で入ります。[公式チュートリアルの枝刈りの章](https://optuna.readthedocs.io/en/stable/tutorial/10%5Fkey%5Ffeatures/003%5Fefficient%5Foptimization%5Falgorithms.html)と同じ形を、少しずつ学習を進められる SGDClassifier で書くと次のとおりです。

```
import numpy as np
import optuna
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import SGDClassifier
from sklearn.model_selection import train_test_split

X, y = load_breast_cancer(return_X_y=True)
X_train, X_valid, y_train, y_valid = train_test_split(X, y, random_state=0)
classes = np.unique(y)


def objective(trial):
    alpha = trial.suggest_float("alpha", 1e-6, 1e-1, log=True)
    clf = SGDClassifier(alpha=alpha, random_state=0)
    for step in range(100):
        clf.partial_fit(X_train, y_train, classes=classes)
        score = clf.score(X_valid, y_valid)
        trial.report(score, step)
        if trial.should_prune():
            raise optuna.TrialPruned()
    return score


study = optuna.create_study(
    direction="maximize",
    pruner=optuna.pruners.HyperbandPruner(),
)
study.optimize(objective, n_trials=100)
```

深層学習なら、エポックの終わりで検証損失を報告する位置が同じ役割を果たします。LightGBM のように学習ループを自分で書かないライブラリには、[optuna\_integration の LightGBMPruningCallback](https://optuna-integration.readthedocs.io/en/stable/reference/generated/optuna%5Fintegration.LightGBMPruningCallback.html) のようなコールバックが用意されています。

### TPEと無作為探索：HyperbandPrunerとMedianPrunerの公式比較

Pruner は2026年10月時点で8種類あります。中央値を下回る試行を止める MedianPruner、上位の割合で切る PercentilePruner、資源を段階的に配る SuccessiveHalvingPruner と HyperbandPruner、しきい値で切る ThresholdPruner などです。

どれを選ぶかについて、公式チュートリアルは深層学習以外のタスクでのベンチマーク結果を示しています。RandomSampler には MedianPruner、TPESampler には HyperbandPruner が最良でした。既定の TPE を使うなら HyperbandPruner から始めるのが筋です。

ひとつ注意があります。Pruner は単一目的の探索だけを想定しており、多目的の study では使えません。精度と推論速度を同時に追う多目的の探索では、枝刈り以外の手段で時間を削る設計が要ります。

## SQLite・JournalStorageで探索を保存し、複数プロセスで並列実行する手順

保存先を指定しない study はメモリ上にしか履歴を持たず、プロセスが落ちた時点で全試行が消えます。数時間以上回す探索は、最初から保存先を置きます。

### SQLiteへ保存してload\_if\_existsで中断した探索を再開する設定

もっとも手軽なのは SQLite です。`storage` に接続文字列を渡し、`load_if_exists=True` を付けると、同じ名前の study があればその続きから再開します。

```
study = optuna.create_study(
    study_name="rf-tuning",
    storage="sqlite:///optuna.db",
    direction="maximize",
    load_if_exists=True,
)
study.optimize(objective, n_trials=50)
```

このスクリプトを途中で止めて再度実行すると、試行番号は続きから振られ、それまでの結果も TPE の推定に使われます。夜間に回して朝に止め、翌晩に再開するといった運用が可能な仕組みです。[並列化のチュートリアル](https://optuna.readthedocs.io/en/stable/tutorial/10%5Fkey%5Ffeatures/004%5Fdistributed.html)では、同じ書き方で MySQL の接続文字列を渡す例も示されています。

### 同一ホストはJournalFileBackend、複数マシンはRDBという使い分け

並列実行は、同じ `study_name` と `storage` を指定したスクリプトを複数起動するだけです。ただし保存先の選び方で安全性が変わります。公式チュートリアルの整理は次のとおりです。

- 同一ホスト上の複数プロセス：JournalStorage と JournalFileBackend（ファイルロックで排他）
- NFS などを介した複数マシン：ファイルロックが正しく働かず競合しうるため、RDBStorage（MySQL・PostgreSQL）
- 数千ノード規模で RDB が負荷に耐えない場合：GrpcStorageProxy

JournalStorage は `optuna.storages.JournalStorage(optuna.storages.journal.JournalFileBackend("optuna_journal.log"))` の形で作ります。[JournalStorage の公式リファレンス](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.storages.JournalStorage.html)には、Windows で権限エラーが出る場合は `JournalFileOpenLock` を渡す書き方も載っています。

### Optuna Dashboardで試行履歴とパラメータ重要度を確認する手順

保存した study は、別パッケージの [Optuna Dashboard](https://github.com/optuna/optuna-dashboard) でブラウザから見られます。2026年9月10日公開の 0.21.0 が最新で、Python 3.10 以降が必要です。

```
pip install optuna-dashboard
optuna-dashboard sqlite:///optuna.db
# http://localhost:8080/ をブラウザで開く
```

試行ごとの評価値の推移、パラメータの重要度、枝刈りされた試行の途中経過が画面で追えます。重要度の算出は v5.0 で既定が PED-ANOVA に替わり、従来の f-ANOVA より速く、条件付きの探索範囲や多目的の study にも対応しました。重要度の低いパラメータを次の探索から外す判断に使えます。

## v4系からv5.0へ上げる前に確認する非推奨APIと移行時の注意点

入門記事の多くは v3 以前の書き方のままです。ここでは、既存コードを 5.0系へ上げるときに実際に止まる箇所を挙げます。

### 制約：constraints\_funcをtrial.set\_constraintへ移行

v5.0 では、制約付き探索の書き方が trial の API に統一されました。従来はサンプラーごとに `constraints_func` を渡していましたが、これは非推奨になり、目的関数の中で `trial.set_constraint` を呼ぶ形に変わっています。[Trial の公式リファレンス](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html)では、すべての制約値が0以下のとき実行可能とみなします。

```
def objective(trial):
    clf = build_model(trial)
    accuracy, latency_ms = evaluate(clf)
    # 推論50ms以内を条件にする（0以下で条件を満たす）
    trial.set_constraint("latency", latency_ms - 50.0)
    return accuracy
```

`build_model` と `evaluate` は各自の処理に置き換えてください。あわせて、制約を設定しない試行は実行可能として扱う、という仕様変更も入っています。

削除された機能もあります。`optuna.multi_objective` モジュール、AllenNLP・Chainer・MXNet 向けの連携、位置引数で渡す古い呼び方などです。古い入門記事のサンプルを写したコードは、版を上げる前にこれらの使用箇所を検索しておくと安全です。

### MLflowCallbackなど連携機能のOptunaHub移行と実験管理の併用

実験管理ツールとの連携は、v4.9.0 の時点で整理が始まっています。[v4.9.0 のリリースノート](https://github.com/optuna/optuna/releases/tag/v4.9.0)では、MLflowCallback・TensorBoardCallback・CometCallback を OptunaHub へ移す予定として非推奨にし、WeightsAndBiasesCallback は移行済みとしています。[MLflowCallback の公式リファレンス](https://optuna-integration.readthedocs.io/en/stable/reference/generated/optuna%5Fintegration.MLflowCallback.html)にも、v6.0.0 で削除予定であることが明記された形です。

連携機能は `optuna.integration` 経由の import も非推奨になり、`optuna_integration` パッケージから直接 import する形に変わりました。今から組むなら、MLflow へは目的関数の中で自前で記録するか、[OptunaHub](https://hub.optuna.org/) 側の実装を確認して使うほうが、v6 で書き直す手間を避けられます。

Optuna の保存先は試行のパラメータと評価値を持ちますが、モデルの成果物やデータの版までは持ちません。そこを担う道具の選び方は[実験管理の記録項目とrun設計](https://www.issoh.co.jp/tech/details/16340/)で、W&B と MLflow の違いは[Weights & Biasesの仕組みとMLflowとの使い分け](https://www.issoh.co.jp/tech/details/16077/)で整理しています。

### v4系の探索結果とv5.0の結果を同列に比べてはいけない理由

既定のサンプラーが変わったため、同じ目的関数・同じ `seed` でも v4系と v5.0 では試される値の列が違います。v4系で出した最良値と v5.0 で回し直した最良値の差を「改善」と報告すると、サンプラーの違いを手柄にしてしまう。

比較が必要な案件では、`requirements.txt` で `optuna==5.0.0` のように版を固定し、サンプラーの設定も明示的に書きます。RDBStorage と JournalStorage が記録する試行の時刻も v5.0 で UTC に揃えられたので、日本時間のつもりで既存の履歴と突き合わせると9時間ずれる点にも気をつけてください。

## Optunaを採用する条件と、受託開発で導入を見送る場面の線引き

Optuna は書きやすい道具ですが、入れれば精度が上がるわけではありません。採用と見送りの線をここで引きます。

### 連続値を含む4本以上のパラメータと数十回の試行予算による採用条件

採用するのは、連続値を含むパラメータが4本以上あり、1試行が数十秒から数十分で、総試行数を50回以上確保できる案件です。この条件なら、TPE の推定が効き始める10試行目以降に十分な回数が残り、枝刈りで総時間も削れます。勾配ブースティング木や中規模のニューラルネットワークがここに入ります。

パラメータが2本で候補が数通りなら、全組み合わせを試すほうが速く、結果の説明もしやすくなります。手法の選び分けと試行予算の逆算は[ハイパーパラメータ探索の手法の選び方と打ち切り設計](https://www.issoh.co.jp/tech/details/16351/)で詳しく扱っています。

### 1試行が数秒の小規模モデルや評価が揺れる案件での導入見送りの判断

見送るのは2つの場面です。ひとつは1試行が数秒で終わる小さなモデルで、この規模なら既定値と数回の手調整で差がつかず、Optuna を組み込む手間のほうが大きくなります。

もうひとつは、検証データが数百件しかなく、乱数を変えるだけで評価値が数ポイント動く案件です。評価のぶれが探索で得られる伸びより大きいと、Optuna は偶然良かった値を「最良」として選びます。この場合は探索より先に、データの追加や[特徴量エンジニアリングの工程](https://www.issoh.co.jp/tech/details/15843/)を見直してください。

### 探索スクリプトと保存先まで含めて納品する受託開発の引き渡し設計

受託開発で Optuna を使うなら、納品物の範囲を先に決めます。決まったハイパーパラメータの値だけを渡すと、データが更新されたときに発注側で探索をやり直せません。目的関数のコード、探索範囲の定義、SQLite のファイルか RDB の接続先、Optuna の版、再実行の手順書までをひと組で引き渡すのが実務的な線です。

加えて、v6.0.0 で削除予定の連携機能に依存しない書き方にしておくと、引き渡し後の版上げで動かなくなる事態を避けられます。[機械学習モデル開発の受託](https://www.issoh.co.jp/service/ai/machine-learning/)では、こうした探索と再現の仕組みまで含めた構成で開発を請けています。

## よくある質問

Optuna を導入する際によく出る質問に答えます。

### Optunaは無料で商用利用できますか？

できます。Optuna 本体は MIT ライセンスで GitHub に公開されており、著作権表示とライセンス文を残せば、社内システムや受託開発の納品物に組み込んで利用できます。Optuna Dashboard も別リポジトリで同じく MIT ライセンスです。ただし、Optuna と組み合わせる学習ライブラリや OptunaHub に登録された各機能は、それぞれ別のライセンスを持つ場合があります。納品前に依存パッケージ全体のライセンスを一覧で確認してください。

### Optunaで探索するたびに結果が変わるのはなぜですか？

サンプラーが乱数を使うためです。`TPESampler(seed=42)` のように seed を固定すると、単一プロセスでは提案される値の列が再現します。ただし並列実行では、どの試行が先に終わるかが毎回変わるため、seed を固定しても完全には再現しません。加えて、学習側のモデルにも乱数があるなら `random_state` を固定する必要があります。v4系と v5.0 では既定サンプラーの挙動が違うため、版の固定も再現の条件に入ります。

### LightGBMやPyTorchと組み合わせるにはどうすればよいですか？

目的関数の中で学習と評価を行い、評価値を返す形にすれば、どの学習ライブラリとも組み合わせられます。枝刈りを入れる場合、PyTorch のように学習ループを自分で書くなら、エポックごとに `trial.report` と `trial.should_prune` を呼ぶ実装が必要です。LightGBM のようにループがライブラリ内にある場合は、optuna\_integration パッケージの LightGBMPruningCallback を学習時のコールバックに渡します。v4.9.0 以降は `optuna.integration` ではなく `optuna_integration` から import してください。

### 精度と推論速度など、複数の指標を同時に追えますか？

複数の指標を同時に追うことが可能です。`optuna.create_study(directions=["maximize", "minimize"])` のように方向を複数渡し、目的関数から値をタプルで返すと多目的の探索になります。結果は1点ではなく、どちらかを改善するともう片方が悪化する組み合わせの集合として得られ、`study.best_trials` で取り出せます。v5.0 では多目的の既定サンプラーが TPE になりました。ただし枝刈りは単一目的の探索だけが対象です。

### Optunaの最新バージョンと動作するPythonの版はどれですか？

2026年10月時点の最新は 2026年9月7日公開の 5.0.0 です。公式サイトは Python 3.10 以降を対応範囲としています。PyPI 上の要求は 3.9 以上と表示されますが、Optuna Dashboard は 3.10 以上を必要とするため、新規に環境を作るなら 3.10 以降を選ぶのが無難です。

## 関連記事

- [ハイパーパラメータ探索とは？手法の選び方と打ち切り設計を実装目線で解説](https://www.issoh.co.jp/tech/details/16351/)：Optuna を使う前に決める試行予算と打ち切り条件
- [実験管理とは？記録項目とrun設計・導入判断を実装目線で解説【2026年版】](https://www.issoh.co.jp/tech/details/16340/)：探索の試行履歴と成果物を残す枠組み
- [Weights & Biasesとは？実験管理の仕組みとWeave・料金・MLflowとの使い分け【2026年版】](https://www.issoh.co.jp/tech/details/16077/)：Optuna と併用する実験管理ツールの選び方
- [特徴量エンジニアリングとは？変換・選択・自動化の工程と実装判断【2026年版】](https://www.issoh.co.jp/tech/details/15843/)：探索より先に手を付けるべき工程
- [MLOpsツール比較｜実験管理・パイプライン・監視の主要スタックと選定基準](https://www.issoh.co.jp/tech/details/15341/)：Optuna を含む MLOps スタック全体の見取り図

---

出典: [Optunaとは？v5.0の既定TPE変更と枝刈り・並列実行を実装目線で解説](<https://www.issoh.co.jp/tech/details/18671/>)（株式会社一創）
