11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Hyperasを使ったKerasハイパーパラメータチューニング

Last updated at Posted at 2019-06-24

ディープラーニングをしているとハイパーパラメータが何か最適かを悩むことが多いです。
ハイパーパラメータの探索には、ランダムサーチやグリッドサーチなど様々な方法があります。記事「ハイパーパラメータ自動調整いろいろ」にわかりやすく書かれていて、勉強させていただきました。
今回は、Hyperasを使ってKerasのハイパーパラメータを自動チューニングします。

環境

前提としてpyenvが入っているくらいでしょうか。pyenvインストールと設定については記事「UbuntuにpyenvとvenvでPython開発環境構築」を参照ください。

種類 バージョン 内容
OS Ubuntu18.04.01 LTS 仮想で動かしています
pyenv 1.2.11 複数Python環境を使うことがあるのでpyenv使っています
Python 3.6.8 pyenv上でpython3.6.8を使っています
パッケージはvenvを使って管理しています

インストール

pipでhyperasをインストール。venvの仮想環境を使っています。バージョンは0.41です。

pip install hyperas

プログラム

Jupyter Labで動かしています。プログラムはGitHubに置いています。
今回のモデルの意味に関しては、記事「【Keras入門(1)】単純なディープラーニングモデル定義」を参照ください。

1. ライブラリインポート

Hyperas関連ライブラリをインポートします。

from hyperopt import Trials, STATUS_OK, tpe, rand
from hyperas import optim
from hyperas.distributions import choice, uniform

2. 訓練データ関数

訓練データを返す関数を作ります。今回はテスト用の乱数です。
通常はファイルを読み込んだり、訓練だけでなくテストデータも分割して渡します。

def data():
    import numpy as np
    x_train = np.random.rand(128, 2)
    y_train = (np.sum(x_train, axis=1) > 1.0) * 1
    y_train = y_train.reshape(128,1)
    
    return x_train, y_train

3. モデル関数

データを受け取ってモデル定義と訓練実行の関数です。
通常だと評価も実行するかと思います。

def create_model(x_train, y_train):
    # Sequentialモデル使用(Sequentialモデルはレイヤを順に重ねたモデル)
    model = Sequential()

    # 結合層
    model.add(Dense({{choice([4, 8, 16, 32, 64, 128])}}, input_dim=2, activation="tanh"))
    
    model.add(Dropout({{uniform(0, 1)}}))

    # 結合層:入力次元を省略すると自動的に前の層の出力次元数を引き継ぐ
    model.add(Dense(1, activation="sigmoid"))

    # モデルをコンパイル
    model.compile(loss="binary_crossentropy",
                  optimizer={{choice(['adam', 'sgd'])}},
                  metrics=["accuracy"])
    
    result = model.fit(x_train, y_train, epochs=3, validation_split=0.2, verbose=0)
    
    # 普通はここでevaluateしてその結果を使うが、今回は簡易的に訓練時の結果を使用
    
    validation_acc = np.amax(result.history['val_acc']) 
    print('Best validation acc of epoch:', validation_acc)
    
    # ここでreturnするlossの値を最小化するように探索する
    return {'loss': -validation_acc, 'status': STATUS_OK, 'model': model}

3.1. choice

上記のchoiceの詳細です。{{choice([4, 8, 16, 32, 64, 128])}}を使ってレイヤ数を選択させています。

model.add(Dense({{choice([4, 8, 16, 32, 64, 128])}}, input_dim=2, activation="tanh"))

3.2. uniform

上記のuniformの詳細です。{{uniform(0, 1)}}とすることでDropoutの数値を0から1までの間の数で探索します。

model.add(Dropout({{uniform(0, 1)}}))

4. チューニング実行

チューニングを実行して、最も結果が良かったパラメータとモデルを受け取ります。

  • max_evals : ハイパーパラメータを探索する回数です。回数が多いほどいい値を探索してくれるかもしれませんが、その半面時間がかかります。
  • eval_space : Trueを渡すことで、1番目のリターンパラメータにわかりやすい値が返ってきます。デフォルトはFalseで、Falseでchoiceを使った場合に対して指定した順の値が返ってきます。今回の例{{choice([4, 8, 16, 32, 64, 128])}}で0の場合は4が最も良かったDenseの値です。
  • notebook_name : 今回は、Jupyterで動かしているのでパラメータ"notebook_name"に自身のノートブック名を渡しています
best_run, best_model = optim.minimize(model=create_model,
                                      data=data,
                                      algo=tpe.suggest,
                                      max_evals=3,
                                      eval_space=True,
                                      notebook_name='Keras07_hyperas', # This is important!
                                      trials=Trials())

実行時出力

実行時の出力です。

>>> Imports:
#coding=utf-8

try:
    from tensorflow.keras.models import Sequential
except:
    pass

try:
    from tensorflow.keras.layers import Dense, Dropout
except:
    pass

try:
    from hyperopt import Trials, STATUS_OK, tpe, rand
except:
    pass

try:
    from hyperas import optim
except:
    pass

try:
    from hyperas.distributions import choice, uniform
except:
    pass

try:
    import numpy as np
except:
    pass

>>> Hyperas search space:

def get_space():
    return {
        'Dense': hp.choice('Dense', [8, 16, 32, 64, 128]),
        'Dropout': hp.uniform('Dropout', 0, 1),
        'optimizer': hp.choice('optimizer', ['adam', 'sgd']),
    }

>>> Data
  1: 
  2: import numpy as np
  3: x_train = np.random.rand(128, 2)
  4: y_train = (np.sum(x_train, axis=1) > 1.0) * 1
  5: y_train = y_train.reshape(128,1)
  6: 
  7: 
  8: 
  9: 
>>> Resulting replaced keras model:

  1: def keras_fmin_fnct(space):
  2: 
  3:     # Sequentialモデル使用(Sequentialモデルはレイヤを順に重ねたモデル)
  4:     model = Sequential()
  5: 
  6:     # 結合層
  7:     model.add(Dense(space['Dense'], input_dim=2, activation="tanh"))
  8:     
  9:     model.add(Dropout(space['Dropout']))
 10: #    model.add(Dropout(0.2))
 11: 
 12:     # 結合層:入力次元を省略すると自動的に前の層の出力次元数を引き継ぐ
 13:     model.add(Dense(1, activation="sigmoid"))
 14: 
 15:     # モデルをコンパイル
 16:     model.compile(loss="binary_crossentropy",
 17:                   optimizer=space['optimizer'],
 18:                   metrics=["accuracy"])
 19:     
 20:     result = model.fit(x_train, y_train, epochs=30, validation_split=0.2, verbose=0)
 21:     
 22:     
 23:     # 普通はここでevaluateしてその結果を使うが、今回は簡易的に訓練時の結果を使用
 24:     
 25:     validation_acc = np.amax(result.history['val_acc']) 
 26:     print('Best validation acc of epoch:', validation_acc)
 27:     
 28:     return {'loss': -validation_acc, 'status': STATUS_OK, 'model': model}
 29: 
  0%|          | 0/3 [00:00<?, ?it/s, best loss: ?]
WARNING:tensorflow:From Instructions for updating:
Use tf.cast instead.
Best validation acc of epoch:
0.88461536
Best validation acc of epoch:
0.9230769
Best validation acc of epoch:
0.7692308
100%|██████████| 3/3 [00:08<00:00,  3.04s/it, best loss: -0.9230769276618958]

**この出力は大事なので消さないようにしましょう。**例えばDROPOUTの変数を複数最適化しようとすると、最後に出力する最適値がどちらの変数かわからなくなります。基本的に時間がかかる処理なので、最後に無駄にしないように、コンソールに出力する値はとっておきましょう。

{'Dropout': 0.42522861686845626,
 'Dropout_1': 0.23316134447477344}

5. 評価

チューニングして最も結果が良かったモデルを使って評価もできます。

x_test, y_test = data()
print("Evalutation of best performing model:")
print(best_model.evaluate(x_test, y_test))
Evalutation of best performing model:
128/128 [==============================] - 0s 99us/sample - loss: 0.4846 - acc: 0.9141
[0.4846123978495598, 0.9140625]

6. ハイパーパラメータ表示

best_runに最も結果が良かったハイパーパラメータが入っています。

print("Best performing model chosen hyper-parameters:")
print(best_run)
Best performing model chosen hyper-parameters:
{'Dense': 3, 'Dropout': 0.21280043312755825, 'optimizer': 0}

複雑なパターンのチューニング

試していませんが、「Kerasだってハイパーパラメータチューニングできるもん。【hyperas】」の記事にあるように複雑なチューニングも可能なようです。

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)}}))

※2019/8/15追記
上記の方法だけでは失敗しました。こんなおまじないを書かないとうまくいきませんでした。変数のディクショナリを作る箇所のバグっぽいです。

# (layer = {{choice([['three', 'four', 'five'])}})

Google Colaboratoryの場合

Keras Hyperparameter Tuning in Google Colab using Hyperasに書いてあるとおりにすればできる?試せていないです。

メモリ不足によるエラー対策

GitHubのIssueを見てモデル出力をやめ、以下のコードを追加することで解決するかもしれません。私がやったときは、他に原因があったので、下記の方法でメモリ削減が達成できるのか調べられていません。

import gc
from tensorflow.python.keras import backend as K

K.clear_session()
gc.collect()
11
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?