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

# はじめに
[前回](https://qiita.com/yassyyuki/items/d55c36bde9b865d83b8d)、CNNでの手書き文字認識のモデルを作成し、グリッドサーチを使用してハイパーパラメータチューニングを行いました。これによって、activation,optiizer,epoch,batch_sizeの値をどうすれば精度の高いモデルが得られるのかについてがわかりました。
今回は、CNNのfilter属性に関しての検証を行っていきたいと思います。

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

```py
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D, Reshape, Input
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from sklearn.metrics import accuracy_score
from collections import OrderedDict
import pandas as pd
import os
```

# データの準備

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

# モデルの作成

```py
class CNNModel:
    def __init__(self, hid_dim_0=32, hid_dim_1=64):
        self.input = Input(shape=(28, 28), name='Input')
        self.reshape = Reshape(target_shape=(28, 28, 1), name='Reshape')
        self.layers = OrderedDict()
        self.layers['conv_0'] = Conv2D(hid_dim_0, (3, 3), strides=(1, 1), name='Conv_0')
        self.layers['pool_0'] = MaxPooling2D((2, 2), strides=(1, 1), name='Pool_0')
        self.layers['conv_1'] = Conv2D(hid_dim_1, (3, 3), strides=(1, 1), name='Conv_1')
        self.layers['pool_1'] = MaxPooling2D((2, 2), strides=(1, 1), name='Pool_1')
        self.layers['flatten'] = Flatten()
        self.layers['dense_0'] = Dense(256, activation='relu')
        self.layers['dense_1'] = Dense(128, activation='relu')
        self.layers['dense_2'] = Dense(64, activation='relu')
        self.last = Dense(10, activation='softmax', name='last')

    def build(self):
        x = self.input
        z = self.reshape(x)
        for layer in self.layers.values():
            z = layer(z)
        p = self.last(z)

        model = Model(inputs=x, outputs=p)

        return model
```

# CNNのfilterの値を複数個設定（16, 32, 64, 128 とした）

```py
dim_hidden_layers = [2**i for i in range(4, 8)]
```

# 比較し、結果をテーブルに保存
`df_accuracy`という変数にパラメータの個数と精度（正解率）を格納していきます。

```py
df_accuracy = pd.DataFrame()

for hid_dim_0 in dim_hidden_layres:
    for hid_dim_1 in dim_hidden_layres:
        print('========', 'hid_dim_0:', hid_dim_0, '; hid_dim_1:', hid_dim_1, '========')
        model = CNNModel(hid_dim_0=hid_dim_0, hid_dim_1=hid_dim_1)
        model = model.build()
        model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['acc'])
        callbacks = [
            EarlyStopping(patience=3),
            ModelCheckpoint(filepath=os.path.join('models', 'CNN', 'model_{}_{}.h5'.format(hid_dim_0, hid_dim_1)), save_best_only=True),
        ]
        n_param = model.count_params()
        model.fit(x=X_train, y=y_train, batch_size=64, epochs=20, callbacks=callbacks, validation_split=0.1)
        acc = accuracy_score(y_test, model.predict(X_test).argmax(axis=-1))
        
        df_accuracy = pd.concat([df_accuracy, pd.DataFrame([[hid_dim_0, hid_dim_1, n_param, acc]], columns=['hid_dim_0', 'hid_dim_1', 'n_param', 'accuracy'])])
```

# 結果の確認

```py
display(df_accuracy.set_index(['hid_dim_0', 'hid_dim_1'])[['n_param']].unstack())
display(df_accuracy.set_index(['hid_dim_0', 'hid_dim_1'])[['accuracy']].unstack())
```

![スクリーンショット 2020-09-11 14.16.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298735/3005c3b6-05f0-283e-d28d-cb97c5e25207.png)

それぞれに大した差はないように見られますが、`128`と`64`の組み合わせがもっとも精度が高いモデルとなるとわかりました。

# 求めた結果を設定して汎化性能の確認

```py
model = CNNModel(hid_dim_0=128, hid_dim_1=64)
model = model.build()
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['acc'])

history = model.fit(x=X_train, y=y_train, batch_size=64, epochs=20, callbacks=callbacks, validation_split=0.1)

print(model.evaluate(X_test, y_test)) # [0.09829007089138031, 0.9854999780654907]
```

したがって、精度が約98.5%のモデルができました。
精度の高い良いモデルです。


# おわりに

今回の検証では、filterの値に大した差はないという結果になってしまいましたが、他の場合はどうであるのかが気になるところです。
今度は層の深さについても調べていけたらなと思います。
最後まで読んでいただきありがとうございました。