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

#はじめに
GPUが載ってないPCでディープラーニングをやってみたい、という人向けです。
今回は前回作成した数字の判定をTPUを使ってやってみます。
https://qiita.com/norikawamura/items/a5c3844e7555202c35a4#_reference-64514a0a96dfd9b1648e

#事前準備
ColaboratoryでハードウェアアクセラレータにTPUを選択します。

#ColaboratoryでTPUを使用するには
結論としては以下の変更が必要なようです。（2019/5/7時点）
①「keras.～」でimportしていたパッケージを「tensorflow.keras.～」とtensorflowの物に変更する。（optimizerの指定等も含む）
②CPU/GPU向けで作成したモデルをTPU向けのモデルに変換する。
　・環境情報を取得後、keras_to_tpu_model()で変換する。
③TPUモデルではpredict_classes()が使えないので、分類を行う場合は代わりにpredict()の結果の最大値をとる。
④TPUモデルではpredict()に渡すデータの件数が、TPUコア数（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
```

#学習データと検証用データに分けます。
kerasの代わりにtensorflow.kerasに変更しています。
また、検証用データの数を8の倍数にしておきます。
※学習用データの数は8の倍数になっている必要はないようです。

```Python
import numpy as np
import tensorflow.keras as keras
# データの順番を入れ替えるためのランダムなNumPy配列
np.random.seed(42)
indices = np.random.permutation(len(X))
val_len = int(len(X) * -0.1) // 8 * 8    #TPUを使った場合のために８の倍数にする

# 学習用のデータ。全体から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()
```

#モデルの定義・学習
「#TPU」のコメントの下のkeras_to_tpu_model()を呼ぶまでの部分が、今回追加したTPU用にモデルを変換する部分です。

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

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

savefile = 'keras_clf_skldigit_tpu.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.0002), metrics=['accuracy'])

    # TPU
    tpu_grpc_url = "grpc://"+os.environ["COLAB_TPU_ADDR"]
    tpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver(tpu_grpc_url)
    #strategy = keras_support.TPUDistributionStrategy(tpu_cluster_resolver)
    strategy = tf.contrib.tpu.TPUDistributionStrategy(tpu_cluster_resolver)
    model = tf.contrib.tpu.keras_to_tpu_model(model, strategy=strategy)

    # モデルの学習
    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/de65cdfe-5018-a371-3675-ea9528d4ad2d.png)


#判定処理
TPUだとmodel.predict_classes()が使えないので、predict()の結果の最大値をnumpy.argmax()で取得して代用します。

```Python
y_test2 = y_test.reshape(-1)

# 結果の表示
#result = model.predict_classes(X_test, verbose=0)
y_result = model.predict(X_test, verbose=0)
import numpy as np
result = np.zeros(y_test2.shape, int)
for idx, data in enumerate(y_result):
  result[idx] = int(np.argmax(data))

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

# データ数をtotalに格納
total = len(result)
# ターゲット（正解）と予測が一致した数を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/ad0d72b9-c2e2-0971-38d7-4e1ff9c13148.png)

#次回
　「CAEを使ったノイズ除去」をやってみます。
　https://qiita.com/norikawamura/items/044f7fda43f609f8f1d8