Kerasでとりあえず動かしてみるディープラーニング#5

#はじめに
GPUが載ってないPCでディープラーニングをやってみたい、という人向けです。
数字画像の判定をやってみます。

#事前準備
ColaboratoryでハードウェアアクセラレータにGPUを選択します。
Colaboratoryの使い方については前回の投稿も参考にしてください。
https://qiita.com/norikawamura/items/637c92545e222f16c535

#データの準備
MNISTの数字画像（28×28）でもやや大きいので、もっと気軽に動かせるscikit-learnの数字画像（8×8）でやってみます。

```Python
import sklearn as skl
from sklearn import datasets
import pandas as pd

dgt = skl.datasets.load_digits()
digits_df = pd.DataFrame(dgt.data)
target_df = pd.DataFrame(dgt.target)

# 特徴量のセットを変数Xに、ターゲットを変数yに格納
X = digits_df.values
y = target_df.values
```

#データの中身を確認します。

データのshapeを見ると以下のようになっています。
y（ターゲット)の方は(～,1)とデータ件数×１列の２次元の配列になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93459/66f97a85-3be3-cfa0-77ec-775ae15cadcc.png)
X（画像データ）は(～,64)と、こちらはデータ件数×64画素の２次元の配列になっています。
(～,8,8)にreshapeした値をpyplot.imshowで画像として確認できます。

```Python
import matplotlib.pyplot as plt
plt.imshow(X[0].reshape(8,8),cmap='gray')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93459/bcb4607a-3c24-6f99-7575-7c32f2a38ce1.png)

#学習データと検証用データに分けます。
特に前回までと違いはありません。

```Python
import numpy as np
import keras
# データの順番を入れ替えるためのランダムなNumPy配列
np.random.seed(42)
indices = np.random.permutation(len(X))
val_len = int(len(X) * -0.1)

# 学習用のデータ。全体から100データを省いたもの
X_train = X[indices[:val_len]]
y_train = y[indices[:val_len]]

# テスト用のデータ。全体から100データ取り出したもの
X_test  = X[indices[val_len:]]
y_test  = y[indices[val_len:]]

# サンプル数、特徴量の次元、クラス数の取り出し
(n_samples, n_features) = X_train.shape
n_classes = len(np.unique(y))

# ターゲットyをkeras用の形式に変換
y_keras = keras.utils.to_categorical(y_train, n_classes)
```
#損失関数のグラフ表示

これも特に前回までと違いはありません。

```Python
import matplotlib.pyplot as plt
#損失関数グラフ
def plotHistory(history):
    # 損失関数のグラフの軸ラベルを設定
    plt.xlabel('time step')
    plt.ylabel('loss')
    # グラフ縦軸の範囲を0以上と定める
    plt.ylim(0, max(np.r_[history.history['val_loss'], history.history['loss']]))
    # 損失関数の時間変化を描画
    val_loss, = plt.plot(history.history['val_loss'], c='#56B4E9')
    loss, = plt.plot(history.history['loss'], c='#E69F00')
    # グラフの凡例（はんれい）を追加
    plt.legend([loss, val_loss], ['loss', 'val_loss'])
    # 描画したグラフを表示
    plt.show()
```

#ニューラルネットワークの定義

ひとまず、前回までと同じ（※）100個×３層のネットワークでやってみます。
※DropoutをBatchNormalizationにだけ変えてます。

```Python
from keras.models import Sequential
from keras.layers import Dense, Dropout, BatchNormalization
from keras.optimizers import Adam
from keras.callbacks import EarlyStopping

savefile = 'keras_clf_skldigit.h5'
useSavedModel = False
if useSavedModel == False:
    # ニューラルネットワークを定義
    model = Sequential()

    # 中間層と入力層を定義
    model.add(Dense(units=100, activation='relu', input_shape=(n_features,)))
    model.add(BatchNormalization())

    model.add(Dense(units=100, activation='relu'))
    model.add(BatchNormalization())

    model.add(Dense(units=100, activation='relu'))
    model.add(BatchNormalization())

    # 出力層を定義
    model.add(Dense(units=n_classes, activation='softmax'))

    # モデルのコンパイル
    model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.0002), metrics=['accuracy'])

    # モデルの学習
    early_stopping = EarlyStopping(monitor='val_loss', mode='min', patience=1000)
    plotHistory(
        model.fit(
             X_train
            ,y_keras
            ,epochs=10000
            ,validation_split=0.1
            ,batch_size=n_samples
            ,verbose=2
            ,callbacks=[early_stopping]
        )
    )    

    # 学習結果を保存    
    model.save(savefile)
else:    
    # 学習済ファイルを読み込んでmodelを作成
    model = keras.models.load_model(savefile)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93459/228e3bbe-5564-b689-7994-5d54d8bd4c07.png)


#予測結果を確認する。

yが２次元配列（データ数×１列）なので、reshape(-1)で１次元配列にして比較します。

```Python
y_test2 = y_test.reshape(-1)
# 結果の表示
result = model.predict_classes(X_test, verbose=0)

print('ターゲット')
print(y_test2)
print('ディープラーニングによる予測')
print(result)

# データ数をtotalに格納
total = len(X_test)
# ターゲット（正解）と予測が一致した数をsuccessに格納
success = sum(result==y_test2)

# 正解率をパーセント表示
print('正解率')
print(100.0*success/total)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93459/7ea7766c-0510-efd5-1a01-fbe17850081a.png)

これでも結構高い正解率のような気もしますが、画像としての性質を生かしたモデルでないと、100%に近い正解率は出ないようです。

#CNNのモデルを使ってみる

MNISTのデータで判定するモデルをちょっと変更しています。
元は「MaxPooling2D」をやっていますが、
元の画像が小さいので、これをコメントアウトし、
代わりに「model.add(Conv2D(128, (3, 3), activation='relu'))」をもう一段入れています。

```Python
from keras.models import Sequential
from keras.layers import Dense, Dropout, BatchNormalization
from keras.optimizers import Adam
from keras.callbacks import EarlyStopping
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Flatten

X_train = X_train.reshape(-1, 8, 8, 1)
X_test  = X_test.reshape(-1, 8, 8, 1)

savefile = 'keras_clf_skldigit_cnn.h5'
useSavedModel = False
if useSavedModel == False:
    # ニューラルネットワークを定義
    model = Sequential()

    # ニューラルネットワークを定義
    model = Sequential()
    # 中間層と入力層を定義
    model.add(Conv2D(32, kernel_size=(3, 3),
                     activation='relu',
                     input_shape=(8,8,1,)))
    model.add(Conv2D(64, (3, 3), activation='relu'))
    #model.add(MaxPooling2D(pool_size=(2, 2)))
    #model.add(BatchNormalization())
    model.add(Conv2D(128, (3, 3), activation='relu'))
    model.add(Flatten())
    model.add(Dense(128, activation='relu'))
    model.add(BatchNormalization())

    # 出力層を定義
    model.add(Dense(units=n_classes, activation='softmax'))

    # モデルのコンパイル
    model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.0001), metrics=['accuracy'])

    # モデルの学習
    early_stopping = EarlyStopping(monitor='val_loss', mode='min', patience=1000)
    plotHistory(
        model.fit(
             X_train
            ,y_keras
            ,epochs=10000
            ,validation_split=0.1
            ,batch_size=n_samples
            ,verbose=2
            ,callbacks=[early_stopping]
        )
    )    

    # 学習結果を保存    
    model.save(savefile)
else:    
    # 学習済ファイルを読み込んでmodelを作成
    model = keras.models.load_model(savefile)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93459/31b6a294-2a4d-6a8c-6e0f-1721529af00b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93459/7bd6cd9d-34ac-2e24-aa75-7b741bee541b.png)

おおむね100%に近い精度が出るようです。

#次回
　今回のプログラムをTPUを使用する形に変換します。
　https://qiita.com/norikawamura/items/dce9225a279874e0b4b2