GoogleのTPUを使って、佐藤健と亀梨和也を人よりも賢く見分けたい

[こちら](https://qiita.com/koshian2/items/25a6341c035e8a260a01)で、Google ColaboratoryでTPUが使えるようになったと知り、早速試してみました。

元のコード等は[こちら](https://qiita.com/mine820/items/e7334eb843a82ad711e6)。

# 設定
これは簡単。
GPUからTPUに変更するだけ。
![image01.png](https://qiita-image-store.s3.amazonaws.com/0/130771/8de8ab37-8874-d75c-fcc8-6e0b09acc1d7.png)

# Googleドライブのマウント
以前書いたコードのまま実行しようとしたところ、エラーに。

```text
E: Unable to locate package google-drive-ocamlfuse
```

調べてみると[こちら](https://qiita.com/k_uekado/items/45b76f9a6f920bf0f786)に情報が。
そっか、簡単になっていたのね。

```python
from google.colab import drive
drive.mount('/content/drive')
```

この修正に関連して、パスが変わった（「drive/xxx」→「drive/My Drive/xxx」）ので、そこだけはコードを変更します。

# とりあえずそのまま実行
Kerasのパスとか変更しなくてはいけないことはわかっているが、エラーになる体験もしてみたいので、そのまま実行。

```text
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.5/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
87916544/87910968 [==============================] - 7s 0us/step
Train on 162 samples, validate on 18 samples
Epoch 1/1000
162/162 [==============================] - 248s 2s/step - loss: 0.6976 - acc: 0.5617 - val_loss: 0.6406 - val_acc: 0.7222
Epoch 2/1000
 64/162 [==========>...................] - ETA: 2:13 - loss: 0.5129 - acc: 0.7812
```

おや？変な動きだけど、エラーにはならない．．．というか、すごく遅い！
一旦止めて、もう一度やり直してみる、が遅い状況は変わらず。

# コードを修正する
ということで、「keras」を「tensorflow.keras」に置き換えて、実行し直しています。

```text
Train on 162 samples, validate on 18 samples
Epoch 1/1000
162/162 [==============================] - 239s 1s/step - loss: 0.7062 - acc: 0.5309 - val_loss: 0.7075 - val_acc: 0.5556
Epoch 2/1000
 64/162 [==========>...................] - ETA: 2:15 - loss: 0.5428 - acc: 0.8281
```

はて？変わらず遅いままだぞ？？
※実行速度もほとんど変わっていない
TPUの恩恵を受けているどころか、GPUよりも遥かに（140倍！）遅い．．．

# 修正漏れ発見
じっくりと確認してみたところ、他にも修正が必要なところがあることがわかりました。

```python
from tensorflow.contrib.tpu.python.tpu import keras_support

# 途中省略

# 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)
model = tf.contrib.tpu.keras_to_tpu_model(model, strategy=strategy)
```

再度実行してみたところ、今度はエラーが発生。

```text
ValueError: 
Layer <tensorflow.python.keras.engine.input_layer.InputLayer object at 0x7febf3fdfef0> has a variable shape in a non-batch dimension.  TPU models must
have constant shapes for all operations.

You may have to specify `input_length` for RNN/TimeDistributed layers.

Layer: <tensorflow.python.keras.engine.input_layer.InputLayer object at 0x7febf3fdfef0>
Input shape: (None, None, None, 3)
Output shape: (None, None, None, 3)
```

InceptionV3（tensorflow.keras.applications.inception_v3.InceptionV3）を使用しているのですが、ちゃんとinput_shapeを明記してあげないといけないようです。

```python
base_model = InceptionV3(weights='imagenet', include_top=False, input_shape=(299,299,3))
```

しかしまだエラーが。

```text
AssertionError: batch_size must be divisible by strategy.num_towers (52 vs 8)
```

はて、指定しているバッチサイズは「64」なんだけど．．．
あ、学習データが90枚×2種類で180枚、64バッチで学習させるから、余りが52ってことか？
では合計128枚（64枚×2）にして、64バッチにして．．．へ？ダメ？？？
ではでは合計160枚にして、16枚評価用にして、バッチサイズを72にすると．．．

```text
INFO:tensorflow:Querying Tensorflow master (b'grpc://10.104.87.194:8470') for TPU system metadata.
INFO:tensorflow:Found TPU system:
INFO:tensorflow:*** Num TPU Cores: 8
INFO:tensorflow:*** Num TPU Workers: 1
INFO:tensorflow:*** Num TPU Cores Per Worker: 8
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:CPU:0, CPU, -1, 13775578281952910176)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:XLA_CPU:0, XLA_CPU, 17179869184, 11472039453562412302)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:XLA_GPU:0, XLA_GPU, 17179869184, 16699149825347599599)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:0, TPU, 17179869184, 12947970202392715099)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:1, TPU, 17179869184, 5831191770111493889)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:2, TPU, 17179869184, 12266886104312379857)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:3, TPU, 17179869184, 12195656273203857672)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:4, TPU, 17179869184, 5342099664455282737)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:5, TPU, 17179869184, 4909200579248859706)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:6, TPU, 17179869184, 6257043378114727165)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:7, TPU, 17179869184, 11756941070616601390)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU_SYSTEM:0, TPU_SYSTEM, 17179869184, 2180101584340784034)
WARNING:tensorflow:tpu_model (from tensorflow.contrib.tpu.python.tpu.keras_support) is experimental and may change or be removed at any time, and without warning.
Train on 160 samples, validate on 16 samples
Epoch 1/1000
INFO:tensorflow:New input shapes; (re-)compiling: mode=train, [TensorSpec(shape=(9, 299, 299, 3), dtype=tf.float32, name='input_20'), TensorSpec(shape=(9, 1), dtype=tf.float32, name='dense_3_target_10')]
INFO:tensorflow:Overriding default placeholder.
INFO:tensorflow:Remapping placeholder for input_2
INFO:tensorflow:Cloning SGD {'lr': 0.009999999776482582, 'momentum': 0.8999999761581421, 'decay': 0.0, 'nesterov': False}
INFO:tensorflow:Get updates: Tensor("loss_1/add:0", shape=(), dtype=float32)
INFO:tensorflow:Started compiling
INFO:tensorflow:Finished compiling. Time elapsed: 93.16143774986267 secs
INFO:tensorflow:Setting weights on TPU model.
 72/160 [============>.................] - ETA: 2:56 - loss: 0.6279 - acc: 0.5556INFO:tensorflow:New input shapes; (re-)compiling: mode=train, [TensorSpec(shape=(2, 299, 299, 3), dtype=tf.float32, name='input_20'), TensorSpec(shape=(2, 1), dtype=tf.float32, name='dense_3_target_10')]
INFO:tensorflow:Overriding default placeholder.
INFO:tensorflow:Remapping placeholder for input_2
INFO:tensorflow:Cloning SGD {'lr': 0.009999999776482582, 'momentum': 0.8999999761581421, 'decay': 0.0, 'nesterov': False}
INFO:tensorflow:Get updates: Tensor("loss_2/add:0", shape=(), dtype=float32)
INFO:tensorflow:Started compiling
INFO:tensorflow:Finished compiling. Time elapsed: 52.98347616195679 secs
144/160 [==========================>...] - ETA: 30s - loss: 0.6840 - acc: 0.5000 INFO:tensorflow:New input shapes; (re-)compiling: mode=eval, [TensorSpec(shape=(2, 299, 299, 3), dtype=tf.float32, name='input_20'), TensorSpec(shape=(2, 1), dtype=tf.float32, name='dense_3_target_10')]
INFO:tensorflow:Overriding default placeholder.
INFO:tensorflow:Remapping placeholder for input_2
INFO:tensorflow:Cloning SGD {'lr': 0.009999999776482582, 'momentum': 0.8999999761581421, 'decay': 0.0, 'nesterov': False}
INFO:tensorflow:Started compiling
INFO:tensorflow:Finished compiling. Time elapsed: 43.58580756187439 secs
160/160 [==============================] - 327s 2s/step - loss: 0.6705 - acc: 0.5500 - val_loss: 0.8207 - val_acc: 0.5000
（以下、省略）
```

（何故か）動いた！
が、始まるまでが遅い！！（苦笑）
TPU用のバイナリに変換するのが遅い模様。

# 識別してみたが．．．
一枚づつ識別しようとしたら、また

```text
AssertionError: batch_size must be divisible by strategy.num_towers (1 vs 8)
```

のエラーが．．．
あぁ、めんどくさい（爆）

# 感想
簡単に動かせるかと思ったら、結構ハマるポイントがありました。
モデルによってはもっとありそうな感じです。

もう少しいじってみたいと思います。