ハイパーパラメータチューニング


# はじめに
手書き文字認識のCNNを用いたモデルを作成しようとしましたが、ハイパーパラメータをどう設定すれば精度が高いモデルが作成されるのかがわからなかったので、グリッドサーチを用いて最適なパラメータを探しました。
機械学習に関しては初心者ですので、有識者の方に精度を上げるためのいろんな方法を聞くことができればと思います。

# 前提
無料でGPUを簡単に使用することができるため、GoogleColaboratoryを用いて実装していきます。
グリッドサーチは指定したハイパーパラメータを総当たりで精度を調べるものですので、計算量が多く時間がかかってしまうのでGPUの使用をおすすめします。
ランダムサーチで求めると時間が圧縮されメリットを得られる場合がありますが、今回はグリッドサーチを使用します。

今回は隠れ層の数が３層のCNNモデルを作成することとします。
全結合層の数は適当に256,128,64としています。

層の深さ、全結合層の数が精度に及ぼす影響も調査すべきですが、今回は無視することとします。

追記
CNNのfilterの値について以下の記事にて検証しました。
[ハイパーパラメータチューニング２](https://qiita.com/yassyyuki/items/dbd32b2f022b64b8b2c8)

# グリッドサーチ

## ライブラリのインポート

```py
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D
from keras.utils.np_utils import to_categorical
from keras.wrappers.scikit_learn import KerasRegressor
from sklearn.model_selection import GridSearchCV
import numpy as np
```

## データの準備

```py
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# ピクセルの値を 0~1 の間に正規化
X_train = X_train / 255.0
X_test = X_test / 255.0

X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
```

## 形の確認
一応確認しておきましょう。

```py
print(np.shape(X_train)) # (60000, 28, 28, 1)
print(np.shape(y_train)) # (60000, 10)
print(np.shape(X_test)) # (10000, 28, 28, 1)
print(np.shape(y_test)) # (10000, 10)
```

## モデルの作成

```py
def build_model(activation , optimizer):
    model = Sequential()

    model.add(Conv2D(input_shape=(28, 28, 1), filters=32, kernel_size=(3, 3), strides=(1, 1), padding='same'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(1, 1), padding='same'))
    model.add(Conv2D(filters=32, kernel_size=(3, 3), strides=(1, 1), padding='same'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(1,1)))

    model.add(Flatten())
    model.add(Dense(256, activation=activation, input_dim=784))
    model.add(Dense(128, activation=activation))
    model.add(Dense(64, activation=activation))
    model.add(Dense(10, activation='softmax'))

    model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])

    return model
```

## 実行
結構時間がかかります。
ハイパーパラメータは調べたいものをlist型で列挙していきましょう。

```py
# ハイパーパラメータを準備
activation = ['relu', 'sigmoid']
optimizer = ['adam', 'sgd']
nb_epoch = [10, 20]
batch_size = [64, 128, 256]

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

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

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

## 結果の確認

```py
print(grid_result.best_params_)
# {'activation': 'relu', 'batch_size': 64, 'nb_epoch': 20, 'optimizer': 'adam'}
```
これで最適なハイパーパラメータを確認できることができました。
では、最後に、このハイパーパラメータを用いたモデルを作成して精度の確認をしてみることとします。

# 求めたハイパーパラメータを設定して汎化性能の確認
先程、求めたハイパーパラメータを設定して手書き文字認識モデルを作成し、精度の確認を行います。

```py
# モデルの作成
model = build_model(activation='relu', optimizer='adam')

# 学習
history = model.fit(X_train, y_train, epochs=20, batch_size=64)

# 精度の確認
model.evaluate(X_test, y_test) # [0.06611067801713943, 0.9872000217437744]
```
以上より、精度が約98.7%のモデルを作成することができました。

# ランダムサーチ
コードのみ載せておきます。`n_iter`の部分で何回ランダムに検証するかを指定します。
ランダムサーチだと時間短縮になりますが、総当たりで調べているわけではないので、より良いハイパーパラメータが存在する可能性が出てきますので、`n_iter`の値をうまく設定することが重要になってきます。


```py
from sklearn.model_selection import RandomizedSearchCV
```

```py
# ハイパーパラメータを準備
activation = ['relu', 'sigmoid']
optimizer = ['adam', 'sgd']
nb_epoch = [10, 20]
batch_size = [64, 128, 256]

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

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

# ランダムサーチの実行
rand = RandomizedSearchCV(estimator=model, param_distributions=param_dict, n_iter=16)
rand_result = rand.fit(X_train, y_train)

```

# おわりに
最後までお読みいただきありがとうございました。
どなたかの学習の助けになれば幸いです。