最近流行りのハイパーパラメータチューニング
先日(2018年12月3日)、僕のTwitterのタイムラインに下のツイートが流れてきました。ハイパーパラメータを自動で最適化してくれるフレームワークOptuna。
なんだこれ!
— Takuya Akiba (@iwiwi) 2018年12月3日
普段地道にハイパーパラメータを調節している僕はすぐにOptunaについて調べました。
これは使わねばならん!!!
そう確信したのですが、残念ながら現在行なっている大学の研究ではkerasでかなり実装を進めてしまっていました。そこで、hyperasというkerasユーザーに優しいハイパーパラメータチューニングのライブラリを使ってみることにしました。
インストール
pip install hyperas
以上。
(僕はこれでいけた)
(他の方法は自分で調べてくれい)
チュートリアル
公式GitのREADMEに英語ですがチュートリアルがありました。これを基に僕なりにチュートリアルを作り直してみました。多分僕の方がわかりやすいので、是非これに従ってhyperasに入門してみて下さい。
Tutorial.1 ~choice~
■ インポート
これらをインポートします。
一旦読み飛ばしましょう。
import numpy as np
from hyperopt import Trials, STATUS_OK, tpe
from hyperas import optim
from hyperas.distributions import choice
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.utils.np_utils import to_categorical
■ データの準備
mnistデータセットを準備します。
returnは必ずx_train, y_train, x_test, y_test
に合わせて下さい。
def prepare_data():
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
return x_train, y_train, x_test, y_test
■ モデルの設計
読み飛ばしていた方、ストップ!!!
ここが肝です。
関数内の2行目、3行目。
{{}}
の中にchoice
と書かれています。choice
は先ほどインポートしたものです。 このように書くことで、2行目ですと、全結合層のノードの数を256個と512個を試してくれます。3行目も同様に16個と32個と64個を試してくれます。内部実装を軽く説明しますと、このcreate_model
というテンプレート関数をこの後hyperasに投げることで、具体的なpythonコードを生成してくれるそうです。(間違っていたらご指摘ください。)
関数内の中盤。
普段のcreate_model
とは少し感覚が違うでしょうが、関数内でコンパイル(compile)もトレーニング(fit)も行なってしまいます。
関数内の終盤。
返り値はディクショナリとなっております。気をつけるべきことは、loss
というキーには最小化したいバリューを与えることです。今回はvalidation accuracy
を最小化したいので-val_acc
という風にマイナスをつけてあげればOKです。
def create_model(x_train, y_train, x_test, y_test):
model = Sequential()
model.add(Dense({{choice([256, 512])}}, input_shape=(784,)))
model.add(Dense({{choice([16, 32, 64])}}))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train, y_train,
batch_size=100,
epochs=10,
verbose=1,
validation_data=(x_test, y_test))
val_loss, val_acc = model.evaluate(x_test, y_test, verbose=0)
return {'loss': -val_acc, 'status': STATUS_OK, 'model': model}
■ メインルーチン
先ほどインポートしたoptim.minimize
を使用します。この関数に先ほど定義したcreate_model
関数とprepare_data
関数を与えます。あとはhyperas
君が頑張って出来るだけ最適なハイパーパラメータを探してきてくれます。max_evals
は探す回数です。
if __name__ == "__main__":
best_run, best_model = optim.minimize(model=create_model,
data=prepare_data,
algo=tpe.suggest,
max_evals=6,
trials=Trials())
print(best_model.summary())
print(best_run)
_, _, x_test, y_test = prepare_data()
val_loss, val_acc = best_model.evaluate(x_test, y_test)
print("val_loss: ", val_loss)
print("val_acc: ", val_acc)
optim.minimize
は二つの値を返してくれます。best_run
とbest_model
です。
best_run
は最も良かった組み合わせのディクショナリです。例えば、
{'Dense': 2, 'Dense_1': 0}
こんな感じです。
0番目のDenseは2番目のハイパーパラメータが良かったよ。
1番目のDenseは0番目のハイパーパラメータが良かったよ。
と教えてくれています。
best_model
はその名の通り最も良かったモデルオブジェクトです。こいつを使って上のようにvalidation accrary
を確認するのも良し、モデルをしっかり保存するのも良しです。
■ 実行結果
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_16 (Dense) (None, 512) 401920
_________________________________________________________________
dense_17 (Dense) (None, 64) 32832
_________________________________________________________________
dense_18 (Dense) (None, 10) 650
_________________________________________________________________
activation_6 (Activation) (None, 10) 0
=================================================================
Total params: 435,402
Trainable params: 435,402
Non-trainable params: 0
_________________________________________________________________
{'Dense': 1, 'Dense_1': 2}
10000/10000 [==============================] - 0s 36us/step
val_loss: 0.2902372345119715
val_acc: 0.9204
ということで、hyperas
が叩き出した最適なハイパーパラメータは以下です。
層の名前 | 値 |
---|---|
Dense | 512 |
Dense_1 | 64 |
Tutorial.2 ~uniform~
■ インポート
import numpy as np
from hyperopt import Trials, STATUS_OK, tpe
from hyperas import optim
from hyperas.distributions import choice, uniform
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.utils.np_utils import to_categorical
■ データの準備
Tutorial.1と同じ。
■ モデルの設計
さて、Dropout
が追加され、Dropout
の引数には{{uniform(0, 1)}}
と書かれています。uniform
は一様分布のことです。これで0~1までの数字でどんな値が最適であるかをhyperas
君が探してきてくれます。これで連続値でも安心ですね。
def create_model(x_train, y_train, x_test, y_test):
model = Sequential()
model.add(Dense({{choice([256, 512])}}, input_shape=(784,)))
model.add(Dropout({{uniform(0, 1)}}))
model.add(Dense({{choice([16, 32, 64])}}))
model.add(Dropout({{uniform(0, 1)}}))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train, y_train,
batch_size=100,
epochs=10,
verbose=1,
validation_data=(x_test, y_test))
val_loss, val_acc = model.evaluate(x_test, y_test, verbose=0)
return {'loss': -val_acc, 'status': STATUS_OK, 'model': model}
■ メインルーチン
Tutorial.1と同じ。
■ 実行結果
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_10 (Dense) (None, 256) 200960
_________________________________________________________________
dropout_7 (Dropout) (None, 256) 0
_________________________________________________________________
dense_11 (Dense) (None, 32) 8224
_________________________________________________________________
dropout_8 (Dropout) (None, 32) 0
_________________________________________________________________
dense_12 (Dense) (None, 10) 330
_________________________________________________________________
activation_4 (Activation) (None, 10) 0
=================================================================
Total params: 209,514
Trainable params: 209,514
Non-trainable params: 0
_________________________________________________________________
{'Dense': 0, 'Dense_1': 1, 'Dropout': 0.4844455237320119, 'Dropout_1': 0.026079803111884514}
10000/10000 [==============================] - 0s 30us/step
val_loss: 0.2799887662306428
val_acc: 0.9247
ということで、hyperas
が叩き出した最適なハイパーパラメータは以下です。
層の名前 | 値 |
---|---|
Dense | 256 |
Dense_1 | 32 |
Dropout | 0.4844455237320119 |
Dropout_1 | 0.026079803111884514 |
自分では決して出ない結論ですね...。
Tutorial.3 ~複雑なパターン~
■ インポート
import numpy as np
from sklearn.metrics import accuracy_score
from hyperopt import Trials, STATUS_OK, tpe
from hyperas import optim
from hyperas.distributions import choice, uniform
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.utils.np_utils import to_categorical
■ データの準備
Tutorial.1と同じ。
■ モデルの設計
たっぷりとパラメータチューニングしました。
組合せ爆発でパターンの数は凄そうですね。
if文の分岐に注目してください。このように、適当な言葉(threeとか)をchoiceさせることで、ニューラルネットの層数自体を変えることもできます。
def create_model(x_train, y_train, x_test, y_test):
model = Sequential()
model.add(Dense(512, input_shape=(784,)))
model.add(Activation({{choice(['linear', 'relu', 'sigmoid'])}}))
model.add(Dropout({{uniform(0, 1)}}))
model.add(Dense({{choice([400, 512, 600])}}))
model.add(Activation({{choice(['linear', 'relu', 'sigmoid'])}}))
model.add(Dropout({{uniform(0, 1)}}))
if {{choice(['three', 'four', 'five'])}} == 'three':
pass
elif {{choice(['three', 'four', 'five'])}} == 'four':
model.add(Dense(100))
model.add(Activation({{choice(['linear', 'relu', 'sigmoid'])}}))
model.add(Dropout({{uniform(0, 1)}}))
elif {{choice(['three', 'four', 'five'])}} == 'five':
model.add(Dense(200))
model.add(Activation({{choice(['linear', 'relu', 'sigmoid'])}}))
model.add(Dropout({{uniform(0, 1)}}))
model.add(Dense(100))
model.add(Activation({{choice(['linear', 'relu', 'sigmoid'])}}))
model.add(Dropout({{uniform(0, 1)}}))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer={{choice(['rmsprop', 'adam', 'sgd'])}},
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train, y_train,
batch_size=100,
epochs=12,
verbose=1,
validation_data=(x_test, y_test))
val_loss, val_acc = model.evaluate(x_test, y_test, verbose=0)
return {'loss': -val_acc, 'status': STATUS_OK, 'model': model}
■ メインルーチン
max_evals
を変えました。
組み合わせも増えてきましたので、100回検証させてみました。
そりゃもうすごい成績なんでしょう...。
if __name__ == "__main__":
best_run, best_model = optim.minimize(model=create_model,
data=prepare_data,
algo=tpe.suggest,
max_evals=100,
trials=Trials())
print(best_model.summary())
print(best_run)
_, _, x_test, y_test = prepare_data()
val_loss, val_acc = best_model.evaluate(x_test, y_test)
print("val_loss: ", val_loss)
print("val_acc: ", val_acc)
■ 実行結果
Layer (type) Output Shape Param #
=================================================================
dense_76 (Dense) (None, 512) 401920
_________________________________________________________________
activation_76 (Activation) (None, 512) 0
_________________________________________________________________
dropout_54 (Dropout) (None, 512) 0
_________________________________________________________________
dense_77 (Dense) (None, 512) 262656
_________________________________________________________________
activation_77 (Activation) (None, 512) 0
_________________________________________________________________
dropout_55 (Dropout) (None, 512) 0
_________________________________________________________________
dense_78 (Dense) (None, 10) 5130
_________________________________________________________________
activation_78 (Activation) (None, 10) 0
=================================================================
Total params: 669,706
Trainable params: 669,706
Non-trainable params: 0
_________________________________________________________________
None
{'Activation': 1, 'Activation_1': 1, 'Activation_2': 1, 'Activation_3': 1, 'Activation_4': 0, 'Dense': 1, 'Dropout': 0.34508543889575766, 'Dropout_1': 0.3554330239504615, 'Dropout_2': 0, 'Dropout_3': 1, 'Dropout_4': 0.1408466259445935, 'Dropout_5': 0, 'Dropout_6': 0.9822922940890546, 'Dropout_7': 0.6033247984045469, 'optimizer': 1}
10000/10000 [==============================] - 1s 82us/step
val_loss: 0.0508109834289553
val_acc: 0.986
結果:validation accuracy 0.986
CNNを用いずにここまでの精度は素晴らしいんじゃないでしょうか!
公式Example
他にも公式GitHubにいくつかのExampleがありますので、ぜひ試してみて下さい。
参考
- ハイパーパラメータチューニング全般
- Optuna
- hyperas
- keras
自己紹介
冒頭に書くと邪魔になるので最後にひっそりと自己紹介させてください。
名前 | 綿岡晃輝 |
---|---|
職業 | 大学4年生 |
分野 | 機械学習, 深層学習, 音声処理 |
@Wataoka_Koki |
Twitterフォローしてね!