はじめに

とあるコンペに Keras で参加しているのだけれども、 ハイパーパラメータ、たとえば以下のようなパラメータ

  • 隠れ層のニューロンの数
  • ネットワークを何層にするか
  • 学習率

をどう決めていたかというと、直感である。

これでは、いけないなと最近気づく。もっと早く気付けよ。

モデルに最適なハイパーパラメータの決め方を調べてみた。

“ディープラーニング ハイパーパラメータ"を検索

みんな困っているようで、事例はすぐにみつかった。 たとえば、この記事なんて、まさに自分のこと!!

これはテストデータの精度に対して、試行錯誤を繰り返しながら決めていくしかありません。

StackOverFlow でも、

asically it is just trial and error. Those are called hyperparameters and should be tuned on a validation set (split from your original data into train/validation/test).

Tuning just means trying different combinations of parameters and keep the one with the lowest loss value or better accuracy on the validation set, depending on the problem.

ようは、総当たりしろ!!!ということか??

3 つの手法:

また、別のページでは、もっと効率のよい方法が紹介されている。大きく分けて、3 つあるようだ。

  • グリッドサーチ:

昔からある手法で、格子状の空間で最適なパラメータを探索する方法です。 格子の範囲を総当りするため、膨大な計算時間がかかるという課題があります。

  • ランダムサーチ :

無作為にパラメータを抽出して探索します。 グリッドサーチよりも計算時間が短くて済むというメリットがあります。

  • ベイズ最適化 (Bayesian Optimization) :

無作為にパラメータを抽出して探索します。 グリッドサーチよりも計算時間が短くて済むというメリットがあります。 ディープラーニングを含む機械学習の手法で、比較的良いハイパーパラメータを探索できることが知られています。

また、bengio 先生のおすすめレシピというのがある。

ハイパーパラメータはグリッドサーチするのではなく,ランダムサンプリングしたほうが性能が出る場合が多いよ

ベイズ最適化

ちょっとこのページは難しいが読み解くと、なにか得られそう。

ツール

scikit-learn には、GridSearchCV というものがあるらしい。いつか試そう。

hyperopt というツールもある。いつか試そう。

hyperopt の keras ラッパーもみつけた。いつか試そう。

調査結果

とりあえず、バカでも出来る全探索(グリッドサーチ)をする

目的

ここからが本題。

ニューラルネットワークのハイパーパラメータを調節したい。今回は簡単に以下の2つを調整。

  • 隠れ層の数
  • ニューロンの数

TensorBoard を Keras で使う

TensorBoard は、TensorFlow のログビューア。いい感じに可視化してくれる。

Keras で TensorBoard を使うには、Keras のコールバック機能を使う。 以下のように keras.collbacks.TensorBoard を利用する。

import keras
tbcb = keras.callbacks.TensorBoard(log_dir=log_filepath)

定義した変数を fit に渡す。

model.fit(trainX, trainY, epochs=30, batch_size=batch_size, validation_data=(validX, validY), callbacks=[tbcb])

これだけだ。簡単簡単。あとは log_filepath を指定して、tensorboard をコマンドラインから起動。port 6006 でアクセスできる。

$ tensorboard --logdir=[log_filepath]

参考: Keras から Tensorboard を使用する方法 - Qiita

複数のパラメータを追う

複数のパラメータをログするには、以下のように、log_dir に渡す文字列に変数を組み込む。

実例で示す。

import keras
from keras.models import Sequential
from keras.layers import Flatten, Dense, Dropout, BatchNormalization
from keras.optimizers import Nadam

batch_size=64

def get_model(num_layers, layer_size):
    model = Sequential()
    model.add(BatchNormalization(axis=1, input_shape=(256,3)))
    model.add(Flatten())
    for _ in range(num_layers):
        model.add(Dense(layer_size, activation='relu'))
        model.add(BatchNormalization(axis=1))
        model.add(Dropout(0.2))
    model.add(Dense(24, activation='softmax'))
    model.compile(Nadam(), loss='categorical_crossentropy', metrics=['accuracy'])
    return model

for num_layers in [2,3,4,5]:
    for layer_size in [64,128,265,512,1024]:
        log_string = 'logs/1/nl={},ls={}'.format(num_layers, layer_size)
        tbcb = keras.callbacks.TensorBoard(log_dir=log_string)
        model = get_model(num_layers, layer_size)
        model.fit(trainX, trainY, epochs=30, batch_size=batch_size, validation_data=(validX, validY), callbacks=[tbcb])

こうすると、logs/1/配下に nl=?,ls=? というディレクトリがそれぞれできて、そのなかにログがたまる。

$ tree 1
1
├── nl=2,ls=1024
│   └── events.out.tfevents.1495116649.letsnote-ubuntu
├── nl=2,ls=128
│   └── events.out.tfevents.1495115992.letsnote-ubuntu
├── nl=2,ls=256
│   └── events.out.tfevents.1495116161.letsnote-ubuntu
├── nl=2,ls=512
│   └── events.out.tfevents.1495116351.letsnote-ubuntu
├── nl=2,ls=64
│   └── events.out.tfevents.1495115856.letsnote-ubuntu
├── nl=3,ls=1024
│   └── events.out.tfevents.1495118093.letsnote-ubuntu
├── nl=3,ls=128
│   └── events.out.tfevents.1495117340.letsnote-ubuntu
├── nl=3,ls=256
│   └── events.out.tfevents.1495117513.letsnote-ubuntu
├── nl=3,ls=512
│   └── events.out.tfevents.1495117727.letsnote-ubuntu
├── nl=3,ls=64
│   └── events.out.tfevents.1495117193.letsnote-ubuntu
├── nl=4,ls=1024
│   └── events.out.tfevents.1495119950.letsnote-ubuntu
├── nl=4,ls=128
│   └── events.out.tfevents.1495119010.letsnote-ubuntu
├── nl=4,ls=256
│   └── events.out.tfevents.1495119233.letsnote-ubuntu
├── nl=4,ls=512
│   └── events.out.tfevents.1495119509.letsnote-ubuntu
├── nl=4,ls=64
│   └── events.out.tfevents.1495118840.letsnote-ubuntu
├── nl=5,ls=1024
│   └── events.out.tfevents.1495122077.letsnote-ubuntu
├── nl=5,ls=128
│   └── events.out.tfevents.1495121077.letsnote-ubuntu
├── nl=5,ls=256
│   └── events.out.tfevents.1495121291.letsnote-ubuntu
├── nl=5,ls=512
│   └── events.out.tfevents.1495121586.letsnote-ubuntu
└── nl=5,ls=64
    └── events.out.tfevents.1495120894.letsnote-ubuntu

これを tensorboard から見ると、accuracy, loss, valid loss, valid accuracy が見える。 以下の図は、valid accuracy.

闇雲にパラメータを調べるよりもよっぽど効率がよい。