Pythonの最適化ライブラリーOptunaを使ったあとその結果を可視化してグラフに保存したいと思ったのでそのメモ.
必要なライブラリーのインストール
今回必要になるライブラリは Optuna,それと可視化のためのPlotlyそしてKaleidoの3つ.KaleidoはPlotlyのグラフを保存するのに必要みたいで,直接呼び出したりはしない. ではそれぞれpipでインストールしておく
$ pip install optuna $ pip install plotly $ pip install kaleido
Plotlyの簡単な使い方は過去の記事にある. Plotlyで簡単なグラフを描いてみる(Python) - My Memo
Optunaで最適化
次はOptunaで最適化を行う.今回はz=x2+y2+xyが最小になるように x, yを最適化する(最小/最大化させたい関数zを目的関数という).ちなみに,z=x2+y2+xyはx=0, y=0で最小となるので,0に近いx, yが得られれば最適化できたことになる.ちなみにこの式のグラフはこんな感じ. この図の描画のPlotlyでの仕方は後述する. まずは,今回必要なライブラリをインポートする.
import optuna import numpy as np import plotly.graph_objects as go
次に最小化(あるいは最大化)する関数を書く.今回はz=x2+y2+xy.
def func(x, y): return x**2+y**2+x*y
次に,最適化したい変数の範囲を設定して目的関数のラッパーを定義する. 最適化したい変数の範囲に関して,今回はx, yともに 0から1の値を取るようにする.
def objective(trial): x = trial.suggest_uniform('x', -1, 1) # xの範囲指定 y = trial.suggest_uniform('y', -1, 1) # yの範囲指定 return func(x, y)
この関数は必ずtrialを引数にとり,最適化したい目的関数を返すようにする.なお範囲を指定するとき,整数のみをとることや,リストの要素から選ぶようにすることも可能
メソッド | ||
---|---|---|
suggest_categorical(name, choices) | choicesというリストの中から選ぶ | x=trial.suggest_categorical('x', ['a', 'b', 'c']) |
suggest_int(name, low, high[, step, log]) | 整数から選ぶ | x=trial.suggest_categorical('x', 1, 10, 2) 1から10まで2刻み |
のように.
そして,目的関数と変数の範囲を指定したら最適化する.
if __name__ == "__main__": # search optimal x and y study = optuna.create_study(direction='minimize') study.optimize(objective, n_trials=50)
今回は目的関数の値を最小化させたいので,direction='minimize'と書いたが,目的関数を最大化させたい場合はdirection='maximize'とすると良い.なおdirectionを省略した場合自動的にdirection='minimize'になる.study.optimize()では目的関数と試行回数を引数として渡す.これで実行すれば最適化をしてくれる.実行すると標準出力に結果が逐次表示されるが,optunaの標準出力をファイルに保存したい場合は実行時に
$ python optimize.py &> output.txt
とすればファイルに保存できる.&を忘れずに.
結果の可視化
ここまで来るとあとはかんたん.最適化したあとの数値的な結果はstudyが持っているので 数値はそれを出せばよい.以下のコードで標準出力してくれる.
# print result print(study.best_params) # x, yの最適な値 print(study.best_value) # zの最適値 print(study.best_trial) # x, y, zの最適値
次は結果のグラフを出力する. まずは, x,yの値に対するzの値を等高線図で表示する.コードは以下.
# print result # contour plot fig = optuna.visualization.plot_contour(study) fig.write_html('image/contour.html') # save as html file fig.write_image('image/contour.png') # save as png file
optuna.visualization.plot_contour(study)メソッドは Plotlyのグラフインスタンスを返すので,保存するには上のようにする. Plotlyのグラフインスタンスの扱い方の詳細は Plotlyで簡単なグラフを描いてみる(Python) - My Memo
するとこのようなグラフが保存される. 次はどの変数が最も最小化に寄与したか(今回はx,yは対称なので,理想的には同じ数値になる).
# Hyperparameter importances fig = optuna.visualization.plot_param_importances(study) fig.write_html('image/param_importance.html') # save as html file fig.write_image('image/param_importance.png') # save as png file
グラフは となる. 他にも色々かける
# Empirical Distribution Function Plot fig = optuna.visualization.plot_edf(study) fig.write_html('image/edf.html') # save as html file fig.write_image('image/edf.png') # save as png file # Optimization History Plot fig = optuna.visualization.plot_optimization_history(study) fig.write_html('image/optimization_history.html') # save as html file fig.write_image('image/optimization_history.png') # save as png file # parallel coordinate plot fig = optuna.visualization.plot_parallel_coordinate(study) fig.write_html('image/parallel_coordinate.html') # save as html file fig.write_image('image/parallel_coordinate.png') # save as png file # slice fig = optuna.visualization.plot_slice(study) fig.write_html('image/slice.html') # save as html file fig.write_image('image/slice.png') # save as png file
それぞれグラフは といろいろ描ける.
今回のコード全体
今回のコードの全体を貼っておく.なお,目的関数の三次元グラフ描画する部分も付け足しておいた.
import optuna import numpy as np import plotly.graph_objects as go def func(x, y): return x**2+y**2+x*y def objective(trial): x = trial.suggest_uniform('x', -1, 1) y = trial.suggest_uniform('y', -1, 1) return func(x, y) if __name__ == "__main__": # plot func(x, y) by plotly x = np.linspace(-1, 1, 100) y = np.linspace(-1, 1, 100) xx, yy = np.meshgrid(x, y) z = func(xx, yy) fig = go.Figure() fig.add_trace(go.Surface(z=z, x=x, y=y)) fig.write_html('objective.html') # search optimal x and y study = optuna.create_study(direction='minimize') study.optimize(objective, n_trials=50) # print result print(study.best_params) print(study.best_value) print(study.best_trial) # visualize # contour plot fig = optuna.visualization.plot_contour(study) fig.write_html('image/contour.html') # save as html file fig.write_image('image/contour.png') # save as png file # Empirical Distribution Function Plot fig = optuna.visualization.plot_edf(study) fig.write_html('image/edf.html') # save as html file fig.write_image('image/edf.png') # save as png file # Optimization History Plot fig = optuna.visualization.plot_optimization_history(study) fig.write_html('image/optimization_history.html') # save as html file fig.write_image('image/optimization_history.png') # save as png file # parallel coordinate plot fig = optuna.visualization.plot_parallel_coordinate(study) fig.write_html('image/parallel_coordinate.html') # save as html file fig.write_image('image/parallel_coordinate.png') # save as png file # Hyperparameter importances fig = optuna.visualization.plot_param_importances(study) fig.write_html('image/param_importance.html') # save as html file fig.write_image('image/param_importance.png') # save as png file # slice fig = optuna.visualization.plot_slice(study) fig.write_html('image/slice.html') # save as html file fig.write_image('image/slice.png') # save as png file