【Keras】GridSearch(グリッドサーチ)でハイパーパラメータを調整する




ディープラーニングを行う際、モデル作成者が決めなければいけないハイパーパラメーターはたくさんあります。損失関数やエポック数、勾配降下法、バッチ数など。これらをすべて手動で変えながら、いいモデルを作るにはとても手間がかかりますし、スマートではありません。

そこで、これらのハイパーパラメータの調整そのものを自動でやれたら便利だよね、ということでKerasでGridSearchを利用してハイパーパラメータを自動調整する方法を紹介していきます。また、GridSearchの注意点についても触れたいと思います。

【Keras】RandomSearch(ランダムサーチ)でハイパーパラメータを調整する

2019年6月26日

目次

グリッドサーチとは?

グリッドサーチとは、機械学習で設定しなければいけないハイパーパラメータを自動調整するアルゴリズムです。方法としては単純で、総当たりです。

例えば、隠れ層は4か5、活性化関数はreluかsigmoidとしたときにどの組み合わせが最適化を総当たりで調べるのです。この例だと、2×2で4通りをすべて試して調べます。テストデータを利用した際の損失関数の値が最も小さくなったものが最終的に選ばれます。

グリッドサーチのメリット・デメリット

ハイパーパラメーターの自動調整には、他にランダムサーチとベイズ最適化があります。これらと比較したときのメリット・デメリットを簡単に紹介します。

メリット

指定した範囲内で最適解を取りこぼすことがない

→全通りを調べていくので、最適な組み合わせを取りこぼすことはありません。

デメリット

非常に計算時間がかかる

→全通りを調べていくので、非常に計算コストがかかります。

手順①:ライブラリをインポート

import numpy as np
import pandas as pd
from pandas import Series, DataFrame
import sklearn
from tensorflow import keras
import pathlib
import tensorflow as tf
from tensorflow.keras import layers
from sklearn import datasets, preprocessing
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers.core import Dense, Activation
from keras.utils import np_utils
from keras import backend as K
from keras.wrappers.scikit_learn import KerasRegressor

とりあえず、使うライブラリをインポートしておきます。

手順②:データの準備

from sklearn.datasets import load_boston
boston = load_boston()
X = DataFrame(boston.data, columns=boston.feature_names)
Y = DataFrame(boston.target, columns=['Price'])

scikit-learnに用意されているボストンの住宅価格に関するデータをインポートします。Xに説明変数、Yに目的変数である価格を入れます。

手順③:データの正規化

def norm(x):
    mean = np.mean(x)
    std = np.std(x)
    return (x - mean) / std

X = norm(X)
Y = norm(Y)

学習がスムーズにいくように、訓練データ・テストデータをどちらも正規化します。ディープラーニングを行う際は、正規化を行わないと訓練が難しくなることがあります。これに関しては、TensorFlowの回帰チュートリアルにも記載してあります。

スケールや値の範囲が異なる特徴量を正規化するのは良い習慣です。特徴量の正規化なしでもモデルは収束するかもしれませんが、モデルの訓練はより難しくなり、結果として得られたモデルも入力で使われる単位に依存することになります。(TensorFlow公式チュートリアル)

手順④:データをHold Out(訓練用・テスト用に分ける)

train_x, test_x, train_y, test_y = sklearn.model_selection.train_test_split(X, Y)

sckit-learnのtrain_test_splitを利用して、データを訓練用とテスト用に分けます。

手順⑤:モデルを作る

def build_model(activation , optimizer):
    model = keras.Sequential([
        layers.Dense(10, activation=activation, input_shape=[len(train_x.keys(),)]),
        layers.Dense(10, activation=activation),
        layers.Dense(15, activation=activation),
        layers.Dense(1)
      ])
    optimizer = tf.keras.optimizers.RMSprop(0.0008)
    model.compile(loss='mean_squared_error', optimizer=optimizer, metrics=['mean_absolute_error', 'mean_squared_error'])
    return model

モデルをつくるための関数を定義します。今回は回帰を行うので、出力層のニューロン数は1活性化関数は恒等関数にしてあります。損失関数は平均二乗誤差に設定しています。

手順⑥:グリッドサーチを行う

#グリッドサーチ対象のハイパーパラメーターを準備
activation = ["relu", "sigmoid"]
optimizer = ["adam", "adagrad"]
nb_epoch = [10, 25]
batch_size = [5, 10]

#グリッドサーチ対象のハイパーパラメーターを辞書型にまとめる
param_grid = dict(activation=activation, optimizer=optimizer,nb_epoch=nb_epoch, batch_size=batch_size)

#モデルを作成
model = KerasRegressor(build_fn = build_model, verbose=0)

#グリッドサーチの実行
grid = GridSearchCV(estimator=model, param_grid=param_grid)
grid_result = grid.fit(train_x, train_y)

まずは、調査対象の各ハイパーパラメータのリストを作ります。今回の例だと、活性化関数はReluとシグモイド・最適化アルゴリズムはadamかadagrad・エポック数は10か25・バッチサイズは5か10と設定しています。

グリッドサーチは全通りを試すので、この場合は24つまり16通りを試します。当然、調査対象のハイパーパラメータが増えれば増えるほど爆発的に計算量が増えていきます。

次に、ハイパーパラメーターのリストを辞書型にまとめます。

そして、モデルを作成するのですが、その際にbuilt_fnに対して関数build_modelをコールバック関数として渡します。verboseは0だと学習の進行度が非表示になります。表示する場合は1を指定します。

最後に、グリッドサーチの実行です。estimatorにはmodelを、param_gridには先ほど作ったハイパーパラメータの辞書型param_gridを渡します。そして、いつもどおりgridに対して訓練データをフィッティングさせます。

手順⑦:結果の確認

print (grid_result.best_params_)
#{'activation': 'relu', 'batch_size': 5, 'nb_epoch': 25, 'optimizer': 'adam'}

それでは結果を見てみます。計算の結果、最も良いハイパーパラメータの組み合わせも分かります。ここまででグリッドサーチの一連の流れは終了です。grid.predict(説明変数)とすることで、予測値を得ることも出来ます。詳しくは公式ドキュメントを御覧ください。

グリッドサーチの注意点

グリッドサーチは総当たりでハイパーパラメータの調整を行うので、計算時間がかかります。そのため、少量のデータかつシンプルなモデルであればCPUだけでもなんとか計算が出来ますが、基本的にGPUが使えないと待つ時間が長くて大変だと思います。

ハイパーパラメータの自動最適化には、グリッドサーチだけではなくランダムサーチやベイズ最適化といった手法もあります。計算時間を短縮したいのであれば、これらの手法も併せて検討していくのがベストです。