Kerasでハイパーパラメータを自動調整したいならOptuna
イントロダクション
前回はHyperasでニューラルネットのハイパーパラメータを自動調整しました。
しかし、コメントアウトしているのに自動調整されるなど、よくわからない挙動をするため、別のモジュールでハイパーパラメータの自動調整に挑戦しようと思いました。
今回は、Optunaというモジュールを使ってハイパーパラメータの自動調整してみます。
目次
計算環境
- OS : Ubuntu 16.04 - Python : 3.5.5 - Keras : 2.2.0 - TensorFlow : 1.9.0
インストール
pip install optuna
動作確認は
import optuna
コマンドについて
モジュールのインポート
import numpy as np import optuna from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization from keras.utils import np_utils
選択
trial.suggest_categorical
を使います。
ex. ) 活性化関数にrelu
とsigmoid
のどちらかを選択するとき
activation = trial.suggest_categorical('activation', ['relu', 'sigmoid'])
数値の自動調整
trial.suggest_discrete_uniform(a, b, c)
: 区間[a, b]内を刻み幅cで区切り、最適値を探します。
trial.suggest_uniform(a, b)
: 区間[a, b]内で最適値を探します。
ex. ) Dropout率を0から1の間で調整するとき
dropout_rate = trial.suggest_uniform('dropout_rate', 0, 1)
int型にも出来ます。
int型で調整したいときは、trial.suggest_int
というのも用意されています。
ex. ) 隠れ層のユニット数を[100, 101, ... 499, 500]のどれかから選ぶとき
mid_units = int(trial.suggest_discrete_uniform('mid_units', 100, 500, 1)) mid_units = trial.suggest_int('mid_units', 100, 500)
層の数を増やしたいとき
層の数を増やしたいときは、for
文を使います。
ex. ) for文を使いユニット数100、活性化関数reluの層を増やすとき
n_layer = trial.suggest_int('n_layer', 1, 3) for i in range(n_layer): model.add(Dense('100', activation='relu'))
TPEとランダムサーチ
公式ドキュメントのstudyのページを見ると、新しくcreate_study
したときのデフォルトのsampler
はNone
とあります。
ただ、何も設定しなくても動いていますし、Preferred Researchの記事では、
最適化エンジン自体は Optuna も Hyperopt も TPE を用いており
とあるので、おそらくTPEがバックで動いていると思うのですが、一応明示しておきます。
# TPE study = optuna.create_study(sampler=optuna.samplers.TPESampler()) # random study = optuna.create_study(sampler=optuna.samplers.RandomSampler())
optunaのログを表示しないようにする
デフォルトではログを表示します。
ログを非表示にしたいときは、optuna.logging.disable_default_handler()
を使います。
逆に、非表示の状態から、表示したくなったら、optuna.logging.enable_default_handler()
を使います。
その他、ログに関しては公式ドキュメントのloggingページを参照してください。
結果の出力
いくつかありますが、代表的なものを2つ。
API | 説明 |
---|---|
best_params |
選ばれたハイパーパラメータ |
best_value |
そのときの評価値 |
MNISTでテスト
MNISTをCNNで学習するコードを作り、ハイパーパラメータをoptunaで自動調整するコードを作りました。
import numpy as np import optuna from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization from keras.utils import np_utils def MNIST_data(): (x_train, y_train), (x_test, y_test) = mnist.load_data() x_train = x_train.reshape(x_train.shape[0], 28, 28, 1) x_test = x_test.reshape(x_test.shape[0], 28, 28, 1) x_train = x_train.astype('float32')/255 x_test = x_test.astype('float32')/255 y_train = np_utils.to_categorical(y_train, 10) y_test = np_utils.to_categorical(y_test, 10) return x_train, y_train, x_test, y_test def create_model(n_layer, activation, mid_units, dropout_rate): model = Sequential() for i in range(n_layer): model.add(Conv2D(32, kernel_size=(3, 3), activation=activation, input_shape=(28, 28, 1))) model.add(MaxPooling2D(pool_size=(1, 1), strides=2)) model.add(BatchNormalization()) model.add(Flatten()) model.add(Dense(mid_units, activation=activation)) model.add(Dropout(dropout_rate)) model.add(Dense(10, activation=activation)) return model def objective(trial): # データをロード x_train, y_train, x_test, y_test = MNIST_data() # 調整したいハイパーパラメータの設定 n_layer = trial.suggest_int('n_layer', 1, 3) # 追加する層を1-3から選ぶ mid_units = int(trial.suggest_discrete_uniform('mid_units', 100, 500, 1)) # ユニット数 dropout_rate = trial.suggest_uniform('dropout_rate', 0, 1) # ドロップアウト率 activation = trial.suggest_categorical('activation', ['relu', 'sigmoid']) # 活性化関数 optimizer = trial.suggest_categorical('optimizer', ['sgd', 'adam', 'rmsprop']) # 最適化アルゴリズム # 学習モデルの構築と学習の開始 model = create_model(n_layer, activation, mid_units, dropout_rate) model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy']) history = model.fit(x_train, y_train, verbose=1, epochs=5, validation_data=(x_test, y_test), batch_size=128) # 学習モデルの保存 model_json = model.to_json() with open('keras_model.json', 'w') as f_model: f_model.write(model_json) model.save_weights('keras_model.hdf5') # 最小値探索なので return -np.amax(history.history['val_acc']) def main(): study = optuna.create_study(sampler=optuna.samplers.TPESampler()) study.optimize(objective, n_trials=10) print('best_params') print(study.best_params) print('-1 x best_value') print(-study.best_value) print('\n --- sorted --- \n') sorted_best_params = sorted(study.best_params.items(), key=lambda x : x[0]) for i, k in sorted_best_params: print(i + ' : ' + str(k)) if __name__ == '__main__': main()
結果 :
... best_params {'dropout_rate': 0.5738498759465457, 'n_layer': 3, 'mid_units': 427.0, 'optimizer': 'rmsprop', 'activation': 'sigmoid'} -1 x best_value 0.9827 --- sorted --- activation : sigmoid dropout_rate : 0.5738498759465457 mid_units : 427.0 n_layer : 3 optimizer : rmsprop
この結果をまとめると、
ハイパーパラメータ | 最適値 |
---|---|
活性化関数 | sigmoid |
ドロップアウト率 | 0.5738498759465457 |
ユニット数 | 427 |
増やした層数 | 3 |
最適化アルゴリズム | rmsprop |
感想
Hyperasより安定しています。
(コメントアウト行の自動調整されていない点など)
不満点は、Hyperasでは学習モデルが返ってきていたが、Optunaではそれが出来ないことです。
ただ、それはモデルの保存をすればクリアできる問題ですし、それ以外は使いやすいので、今日からOptunaを使うことにします。