Optuna概要
- PFN様製のハイパーパラメータの自動最適化ライブラリです。
- DeepLearningでもsklearnでも使えます。すごく便利。
- 公式ページはこちらです。
使い方
基本形
- 公式チュートリアルから転載
import optuna
def objective(trial):
x = trial.suggest_float("x", -10, 10)
return (x - 2) ** 2
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))
hist_df = study.trials_dataframe(multi_index=True)
hist_df.to_csv("result.csv")
- objectiveを作成
- objective内で、値を探索したいところに、trial.suggest_xxxと記述。
- trial.suggest_xxxの種類はいろいろあり後述。
- objectiveの返り値はスコアとし、optunaはデフォルトではこのスコアを最小する。
- objective内で、値を探索したいところに、trial.suggest_xxxと記述。
- optuna.create_studyでタスク(study)を作成。
- 引数にはSamplerを与えることができる。デフォルトはTPESampler。
- study.optimizeでobjectiveを引数として、n_trialsで試行回数を設定すると最適化が始まる。
- 結果は、studyの中にbest_paramsとして入っている。
- study.trials_dataframeで各試行の結果をデータフレームで取得できる。
- multi_indexはFalseでもよい。データフレームのカラムの見え方が変わるだけ。
trial.suggest_xxxの種類
名前 | 説明 |
---|---|
suggest_categorical | カテゴリ変数。アルゴリズム名やkernel名など各モデルで文字列で表現されるものはこれ。 |
suggest_int | 整数パラメータ。log=trueで対数的に分布させられる。stepで刻み幅を指定できる |
suggest_float | 後ろ3つのラッパー。logやstepの設定によりそれぞれが呼ばれる。 |
suggest_uniform | 連続一様分布。suggest_floatでlogやstepを指定しない場合。 |
suggest_loguniform | 連続対数分布。suggest_floatでlog=trueとした場合。 |
suggest_discreteuniform | 離散一様分布。suggest_floatでstepを指定した場合。 |
trial.suggest_xxxの使い方例
- sklearn.svm.SVCの場合
def objective(trial):
# ...(略)...
params = {
# 10のべき乗で表すことが多いので、対数一様分布
"C": trial.suggest_loguniform("C", 0.0001, 1000.0),
# kernelはカテゴリ変数
"kernel": trial.suggest_categorical("kernel", ["linear", "poly", "rbf", "sigmoid"]),
# 整数
"degree": trial.suggest_int("degree", 1, 10),
# gammaはカテゴリと数字双方使える。その場合はとりあえず"numeric"を余分に準備して…
"gamma": trial.suggest_categorical("gamma", ["scale", "auto", "numeric"]),
}
# gammaが"numeric"の場合、ここで"gamma_val"という新しい名前でパラメータを振り、
# params["gamma"]にはgamma_valにしておく。
if params["gamma"] == "numeric":
params["gamma"] = trial.suggest_loguniform("gamma_val", 0.1, 1.0)
# モデル作成
model = sklearn.svm.SVC(*params)
# ...(略)...
- カテゴリと数字が混在する、"gamma"の振り方はポイント。(振る意味があるかは置いておいて)
- その他、機械学習のモデルによっては、あるモードでしか使えない引数などもこの方法で回避できる。
objectiveに引数を与えたい場合
- 公式にあるとおりだが、lambdaを使う方法がシンプルに書ける。
import optuna
# Objective function that takes three arguments.
def objective(trial, min_x, max_x):
x = trial.suggest_uniform("x", min_x, max_x)
return (x - 2) ** 2
# Extra arguments.
min_x = -100
max_x = 100
# Execute an optimization by using the above objective function wrapped by `lambda`.
study = optuna.create_study()
study.optimize(lambda trial: objective(trial, min_x, max_x), n_trials=100)
objective内の途中計算結果も取得したい場合
- set_users_attrsを使えば、データフレームに格納することが可能。
def objective(trial):
# ...(略)...
# user_attrsに設定
trial.set_user_attr('hoge', hoge)
trial.set_user_attr('hogehoge', hoge)
# ...(略)...
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))
hist_df = study.trials_dataframe(multi_index=True) # user_attrsに設定したものもDFで取得できる。
hist_df.to_csv("result.csv")
Optunaのログをファイルに出力したい場合
- optunaのロガーを"optuna"で取得できるので、そこにFileHandlerを設定すればよい。
import logging
optuna.logging.get_logger("optuna").addHandler(logging.FileHandler('./optuna.log'))
Optunaのロガーに任意の情報を出力したい場合
- 前項と同様、objective内でロガーを取得すれば可能。
def objective(trial):
# ロガー取得
logger = optuna.logging.get_logger("optuna")
# 出力される
logger.info("test")
なんか再現性がない場合
- n_jobsを1以外に設定すると、再現性がなくなるように見えている。
- おなじn_jobs(例えば20)を設定していても実行する度に変わるため、再現性がとりたい場合は1にした方が良さそう。
多目的化最適化(Multi-objective)
- ここまで目的関数のスコアは1つだった。これをSingle-objectiveと呼んでいる。
- 詳細は以下が詳しい。
- Samplerも専用のものがあり、結果の見え方はbestを自動で選ぶのではなく、候補をいくつか教えてくれる形式
その他の参考ページ
- ソースを追うためのミニチュア版optuna、minitunaを使った解説
- https://snova301.hatenablog.com/entry/2018/12/14/191025
- https://qiita.com/hiro_o918/items/96edb40756d8adeec11b
- https://woodyzootopia.github.io/2018/12/optuna%E3%81%AE%E8%A8%93%E7%B7%B4%E5%B1%A5%E6%AD%B4%E5%87%BA%E5%8A%9B%E6%A9%9F%E8%83%BD%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E6%8E%A2%E7%B4%A2%E3%81%AE%E6%A7%98%E5%AD%90%E3%82%92%E8%A6%8B%E3%82%8B/
- https://ohke.hateblo.jp/entry/2020/07/04/230000
- https://qiita.com/koshian2/items/1c0f781d244a6046b83e