若葉の技術メモ

コンピュータやプログラミングに関して調べたり、取り組んだりしたことをまとめる若葉のノート📓。コンピュータ・プログラミング初めてって方も一緒に勉強していきましょう!初心は大事!いつでも若葉☘のような意気込みで!

  • No.   

若葉の技術メモ

コンピュータやプログラミング・数理に関して調べたり、取り組んだりしたことをまとめる若葉のノート。

コンピュータ・プログラミング・数理が初めてって方も一緒に勉強していきましょう!

初心は大事!いつでも若葉☘のような意気込みで!

【ハイパーパラメータの自動最適化!】Preferred NetworksさんのOptunaを触ってみた!

f:id:wakaba-mafin:20181215151231j:plain

ハイパーパラメータ。モデルを支配するパラメータ。

調整するの苦戦しますよね?

ということで、今回は先日Preferred Networksさんが公開したPython上のハイパーパラメータ自動最適化ツールOptunaをご紹介します!

optuna.org

※この記事の内容をご自身で試される場合は自己責任でお願いします


はじめに

昨今のディープラーニング・AIなどを扱う際に重要な要素の1つがハイパーパラメータ

多層ニューラルネットワークを例に取れば、ニューロンの数・層の数・学習率やドロップアウトの割合など数えれば数えるほど切りがありません。 もちろん、先見の知識を扱うことができる対象であれば、どのようなハイパーパラメータがいいのかなどはある程度は知られてはいます。

しかし、自分が初めて行う対象やあまり知られていない対象に対して適用したいという場合は往々にしてあると思います。 そんな場合には自分自身でチューニングしてよいハイパーパラメータを探す必要が出てきます...

そんなときに朗報です!

2018年12月2日にPreferred Networksさんが自動でハイパーパラメータを最適化してくれるツールを公開してくださいました!

その名も“Optuna”

www.preferred-networks.jp

https://research.preferred.jp/2018/12/optuna-release/research.preferred.jp

ということで、今回はこのOptunaを触ってみたという記事になります!

Optunaの導入

嬉しいことにOptunaの導入の方法は公式ドキュメントに書いてあります。

https://optuna.readthedocs.io/en/stable/installation.htmloptuna.readthedocs.io

Pythonの環境としてはv2.7か>=v3.4が必要みたいです。

ぱっと公式ドキュメントを見る限り、

$ pip install optuna

だけでいいみたいです! というわけで早速入れてみましょう!

Optuna仮想環境の構築

私はAnacondaさんを使っていますので、Optunaさん用に環境を整えます。

仮想環境

$ conda create -n optuna_env pip python=3.4
$ conda activate optuna_env
$ conda install scikit-learn matplotlib
$ pip install cython
$ conda install  pandas
$ pip install optuna
$ conda deactivate

でOK!

ここで注意してほしいのが4行目のcython5行目のpandasです! まず4行目からいきますが、Cythonが入っていないと、Cython is required to compile pandas from a development branch.と怒られてしまいました💦 また、5行目のPandasに関しても、pandasが入っていないと、Failed building wheel for pandasと怒られてしまいました💦

ま、よくわかりませんが、condapipを使ってそれぞれ入れるとうまくいきましたので、良しとしましょう!

Optunaの利用

というわけで、簡単な例でOptunaさんの力を借りてみましょう!

ライブラリ

今回はoptunaに加えて、次のライブラリを使います

# 今回の主役
import optuna 
# サブのライブラリ
import numpy as np
import matplotlib.pyplot as plt
import sklearn
from sklearn.linear_model import LinearRegression
二次関数の最適化(公式チュートリアル

公式ドキュメントにてチュートリアルが書かれておりますので、私もチュートリアルにトライしてみます!

公式チュートリアルで紹介されている基本問題は、このような最適化問題です。


\min_{x\in [-10,10]} (x-2)^{2}

つまり、中学生のときにやった二次関数の最適化ですね! これはあっという間に、最適解はx^\star=2で最小値0となることがわかります。

ということで、これをOptunaさんの力を借りてやってみましょう!

まずは目的関数の定義です。

# objective function
def objective(trial):
    x = trial.suggest_uniform('x', -10, 10)
    return (x-2)**2

Optunaさんでは"trial"とは、一回目的関数を計算してみることを指しているようです。 つまり、-10から10の間で候補のxを一様に1つ生成してから(x-2)^{2}を計算してみることを指しているっぽい?

次はxの最適化

study = optuna.create_study()
study.optimize(objective, n_trials=100)
[I 2018-12-15 04:35:37,229] Finished a trial resulted in value: 61.2878584158245. Current best value is 61.2878584158245 with parameters: {'x': 9.828656233085248}.
[I 2018-12-15 04:35:37,273] Finished a trial resulted in value: 3.7371480273689595. Current best value is 3.7371480273689595 with parameters: {'x': 0.06682954001232488}.
[I 2018-12-15 04:35:37,290] Finished a trial resulted in value: 134.86042888109552. Current best value is 3.7371480273689595 with parameters: {'x': 0.06682954001232488}.
[I 2018-12-15 04:35:37,305] Finished a trial resulted in value: 27.434594542295343. Current best value is 3.7371480273689595 with parameters: {'x': 0.06682954001232488}.
〜中略〜
[I 2018-12-15 04:35:44,502] Finished a trial resulted in value: 24.603222898235302. Current best value is 0.0018931986286930766 with parameters: {'x': 2.0435109024118447}.
[I 2018-12-15 04:35:44,583] Finished a trial resulted in value: 6.377461997831239. Current best value is 0.0018931986286930766 with parameters: {'x': 2.0435109024118447}.

実はこれで目的関数の最適化が終わります!

簡単すぎる...!

また、Optunaさんの言い回しですが、"study"とは一回の試行であるtrialをまとめた最適化のセッションのようです。

そして、この出力をみる限り、徐々にCurrent best valueが小さくなって、最適解に近づいている様子を確認することができます!

では、得られている解を確認しましょう!

print("opt_x : ", study.best_params)
print("opt_f : ", study.best_value)
opt_x :  {'x': 2.0435109024118447}
opt_f :  0.0018931986286930766

studyの中にbest_paramsbest_valueがありますので、そこで最適解と最適値を確認できます!

確かに、2に近い最適解を見つけることができてますね!

線形回帰のモデル選択

というわけで、もう少しデータ解析に近い問題を考えてみましょう!

次のような問題を考えてみます!

今、データ\{ (t_i, x_i)|i=1,\cdots, T\}が得られているとし、周期的っぽいなってことで、そのデータに対してモデル


x = a_{0} + \sum_{m=1}^{M} a_{m} \cos(mt) + \sum_{m=1}^{M} b_{m} \sin(mt) + \xi

を仮定しているとします。ここで\xiは互いに独立な標準正規分布に従う確率変数です。 そして、考えるべき問題はデータ\{ (t_i, x_i)|i=1,\cdots, T\}からFourier係数\{a_0, a_1, \cdots, a_M, b_1, \cdots, b_M \}を推定するという問題です。

ここで、モデルに含まれているMはハイパーパラメータでモデルの形を支配しています。

この問題では、Optunaを使ったcross-validationによってこのハイパーパラメータを自動で決定します!

まずはデータを生成しましょう!

ここでは次のようにデータを作ります。

def f(t):
    return np.sin(3*t) + 0.5*np.cos(5*t)

T = 4500
t_learn = np.random.randn(T) * np.pi
x_learn = f(t_learn) + np.random.randn(T)

Tval = 500
t_val = np.random.randn(Tval) * np.pi
x_val = f(t_val) + np.random.randn(Tval)

すなわち、


x = \sin(3t) + 0.5\cos(5t) + \xi, \xi\sim\mathcal{N}(0,1)

ですね! このデータからハイパーパラメータMを5と決定できて、


a_5 = 0.5, b_3=1

と推定できれば成功です!

ちなみにデータはこんな感じ。

plt.scatter(t_learn, x_learn, c="k", alpha=0.1)
plt.show()

f:id:wakaba-mafin:20181215150402p:plain

うん。何か構造は見えるけど、難しそう

というわけでチャレンジ!

# feature function
# 今回は線形回帰とか言いつつ、実は非線形な特徴関数をかました上での線形回帰です。
# そこで特徴関数を定義します。
def phi(t, M):
    '''
       arguments:
         t : 説明変数(numpy 1d-array)
         M : Fourier次数(ハイパーパラメータ)(int)
       return:
         1,cos(t),cos(2t),...,cos(Mt),sin(t),...,sin(Mt)を並べたもの
    '''
    if M==0:
        return np.ones((len(t),1))
    return np.hstack([
        np.ones((len(t),1)),
        np.array([np.cos(i*t) for i in range(1,M+1)]).T,
        np.array([np.sin(i*t) for i in range(1,M+1)]).T,
    ])

# Objective function
def objective(trial):
    # Fourier order
    optunaM = trial.suggest_int('Fourier_order', 0, 15)
    
    # linear model
    lm = LinearRegression().fit(phi(t_learn,M=optunaM), x_learn)
   
    # Vailidation dataに対する決定係数(1に近ければ近いほど当てはまりがよい)
    R2 = lm.score(phi(t_val, M=optunaM), x_val)
    return 1-R2

study = optuna.create_study()
study.optimize(objective, n_trials=100)
[I 2018-12-15 05:11:46,482] Finished a trial resulted in value: 1.000597154371967. Current best value is 1.000597154371967 with parameters: {'Fourier_order': 1}.
[I 2018-12-15 05:11:46,545] Finished a trial resulted in value: 0.6039560086065457. Current best value is 0.6039560086065457 with parameters: {'Fourier_order': 9}.
[I 2018-12-15 05:11:46,598] Finished a trial resulted in value: 0.5993651892410196. Current best value is 0.5993651892410196 with parameters: {'Fourier_order': 5}.
[I 2018-12-15 05:11:46,665] Finished a trial resulted in value: 0.6041404708784243. Current best value is 0.5993651892410196 with parameters: {'Fourier_order': 5}.
〜中略〜
[I 2018-12-15 05:11:57,031] Finished a trial resulted in value: 0.6993435745409307. Current best value is 0.5993651892410196 with parameters: {'Fourier_order': 5}.
[I 2018-12-15 05:11:57,178] Finished a trial resulted in value: 0.6005207158780083. Current best value is 0.5993651892410196 with parameters: {'Fourier_order': 5}.

さて、これでMの最適化ができました! ここでは M=0,1,\cdots,15の範囲で探しています。

print("opt_M : ", study.best_params)
opt_M :  {'Fourier_order': 5}

実際にM^{\star}=5となって、期待していたとおりのハイパーパラメータになりました!

また、モデルの当てはまり具合も確かめてみましょう!

opt_linear_model = LinearRegression().fit(phi(t_learn,M=study.best_params["Fourier_order"]), x_learn)
predicted_x = opt_linear_model.predict(phi(t_val,M=study.best_params["Fourier_order"]))

plt.scatter(t_val, x_val, c="k", alpha=0.5, label="validation data")
plt.scatter(t_val, predicted_x, c="r", alpha=0.5, label="prediction")
plt.plot(np.linspace(-10,10,1000), f(np.linspace(-10,10,1000)), "b-", label="genuine model")
plt.xlim(-5,5)
plt.legend()
plt.show()

f:id:wakaba-mafin:20181215150605p:plain

いいですね!(なお、なんでMの最適化にValidation data用いているのに、それと同じデータを用いて当てはまり具合を見ているのかというと、単なるサボりです...ご勘弁を!💦)

推定されるパラメータはどうかというと...

print("cosine Fourier coef.:",opt_linear_model.coef_[:study.best_params["Fourier_order"]+1])
print(" sine  Fourier coef.:",opt_linear_model.coef_[study.best_params["Fourier_order"]+1:])
cosine Fourier coef.: [ 0.          0.00150789 -0.01555043  0.00190459 -0.04628791  0.48007748]
 sine  Fourier coef.: [-0.03093966  0.01295187  1.00363006 -0.03495288  0.01748351]

確かに欲しかった、


a_5 = 0.5, b_3=1

に近い値になって、それ以外は0に近くなってますね!めでたしめでたし!

まとめ

ということで、今回は自動でハイパーパラメータを最適化するツール

Optuna

をご紹介いたしました。 このツールはさすがPreferred Networksさんとしか言いようがありません! 今まで苦戦していたチューニングが楽になりそうです!

また、こちらのOptuna、

  • Chainer

  • scikit-learn

  • XGBoost

  • LightGBM

などとも併用できるようですね! ますます夢が膨らんでしまいます。

というわけで、もう少しOptunaさんと遊んでみたいと思います!

間違いのご指摘やご意見・ご質問等ございましたら、ぜひコメントのほどよろしくお願いします!