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




ディープラーニングでハイパーパラメータを調整するとき、手作業で行うのは大変です。特にビジネスの世界においては短時間で良いものを作り上げることが重要なので、ハイパーパラメーターをコンピューターに自動調整してもらう機能は大変役に立ちます。

この記事では、時間を掛けずにハイパーパラメーターを自動調整できるRandomSearch(ランダムサーチ)を紹介します。GridSearch(グリッドサーチ)との大きな違いは、パラメーターの組み合わせを総当たりではなくランダムに決めるので、計算時間が短縮されることです。計算機資源のスペックがそこまで高くないときや、短時間で結果を出したい時に利用することが多いです。

ランダムサーチとは?

ランダムサーチとは、機械学習で設定しなければいけないハイパーパラメータを自動調整するアルゴリズムです。総当たりだと時間がかかるので、上限数を設定した上でパラメーターをランダムに選び検証します。

例えば、隠れ層は4か5、活性化関数はreluかsigmoidとします。上限数を2と決めた場合は、2×2で4通りの中から2つをランダムに選び検証します。テストデータを利用した際の損失関数の値が最も小さくなったものが最終的に選ばれます。

ランダムサーチのメリット・デメリット

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

メリット

計算時間が短い

→検証する組み合わせ数に上限があるので、計算時間が短くて済みます。

デメリット

最適な組み合わせにたどり着かない可能性がある

→検証する組み合わせはランダムに決定するので、運が悪ければ最適な組み合わせにたどり着けません。

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

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 RandomizedSearchCV
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

モデルをつくるための関数を定義します。

手順⑥:ランダムサーチを行う

#ランダムサーチ用パラメーター
activation = ["relu", "sigmoid"]
optimizer = ["adam", "adagrad"]
nb_epoch = [10, 25]
batch_size = [5, 10]

param_dict = dict(activation=activation, 
                  optimizer=optimizer,
                  nb_epoch=nb_epoch, 
                  batch_size=batch_size)
model = KerasRegressor(build_fn = build_model, verbose=0)
rand = RandomizedSearchCV(estimator=model, param_distributions=param_dict, n_iter=10)
rand_result = rand.fit(train_x, train_y)

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

ランダムサーチは総当たりではなく、ランダムな組み合わせを試します。何組試行するかを指定しているのがn_iter=10の箇所です。この場合は24つまり16通りから10通りをランダムに選んで試します。

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

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

最後に、ランダムサーチの実行です。estimatorにはmodelを、param_distributionsには先ほど作ったハイパーパラメータの辞書型param_dictを渡します。そして、訓練データをフィッティングさせます。

手順⑦:結果の確認

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

それでは結果を見てみます。今回は損失関数に絶対二乗誤差を指定しているので、どこまで絶対二乗誤差を低くできたかが表示されています。

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

ランダムサーチの注意点

ランダムサーチは総当たりをしない分、計算時間が短いのが大きなメリットです。しかし、だからこそいつまで立っても真の最適なハイパーパラメーターにたどり着けないというリスクがあります。n_iterの数値をうまく調節していくことが大切になっていきます。