TensorflowのAttention OCRを動かしてみる

この記事は
===

* Tensorflowの公開モデルの中に入っていた[Attention OCR](https://github.com/tensorflow/models/tree/master/research/attention_ocr)という、道端の看板に書かれている文字を画像から読み取るOCRを動かしてみたのでメモです

Attention OCR
===

* このモデルの学習データは[FSNS](https://github.com/tensorflow/models/tree/master/research/street)というフランスの道路名標識のデータセットの利用を想定しています
* このデータセットでは下記のように4枚の同じ看板を別角度から撮影した画像で構成されているのが特徴的です

![3-Figure1-1.png](https://qiita-image-store.s3.amazonaws.com/0/27361/61d5c317-3a72-cd6c-b1cf-42197059a6c9.png)

* [論文](https://arxiv.org/pdf/1704.03549.pdf)によると、4枚の画像をCNNに入力して組み合わせた後、RNNへの入力として、文字読み取りの精度を上げる仕組みになっているみたいです

![スクリーンショット 2017-12-13 12.07.51.png](https://qiita-image-store.s3.amazonaws.com/0/27361/e38102f4-1ccb-6936-5048-b48345b1135a.png)


* ちなみに[この記事](https://www.infoq.com/jp/news/2017/06/google-maps-deep-learning-street)に下記の記載があり、StreetViewの画像からGoogleMapの品質向上にフィードバックするような用途に使っているみたいです


>ディープラーニングモデルは、新しいStreet Viewイメージに対する自動的なラベル付け、命名規則に一致させるためのテキストの正規化、データ分析とは無関係な余剰テキストの無視、といったことも可能にした。これにより、街路名や住所が分からなくても、画像から直接、新たな住所を作成することが可能になる。例えばStreet View車両が新たに建設された道路を通る場合、このモデルによって、キャプチャイメージを分析して道路の名称と番号を抽出することで、GoogleMap上にそのアドレスを生成し、配置することが可能になるのだ。

* はっきりこのライブラリのことだとは書いてないですが、多分これのことだと思われます

学習の実施
===

環境
---

普通に環境作ってもらえればいいのですが、例のごとくPython3では動きませんので注意

* Python 2.7.14
* Tensorflow 1.4.1

データのダウンロード
---

* ariaを使って学習データをダウンロードします
* ariaはubuntuなら ``` apt-get install -y aria2 ```で入れられます
* 相当容量があるので數十分〜数時間は待つことになります

```
$ cd research/attention_ocr/python/datasets
$ aria2c -c -j 20 -i ../../../street/python/fsns_urls.txt
```

学習の実施
---

* 普通に学習を実施します
* batchサイズはデフォルトで32なのですが、めちゃくちゃにメモリを食ってOOM Killer的なエラーが出てしまうので下げてます

```
$ python train.py --batch_size=8
```

* ちゃんと動いていると下記のようにlossが下がっていく様子をログから確認できます
* 12時間ほど学習させたところ、loss値は30位まで落ちました
* 学習を終えたいタイミングでCtrl+cで止めます。学習済みモデルは```/tmp/attention_ocr/train/```に入っています。

```
INFO 2017-12-12 08:36:17.000449: train.py: 167 Use already existing training directory /tmp/attention_ocr/train
INFO 2017-12-12 08:36:17.000449: fsns.py: 130 Using FSNS dataset split_name=train dataset_dir=/root/models/research/attention_ocr/python/datasets/data/fsns
DEBUG 2017-12-12 08:36:18.000132: model.py: 354 images: Tensor("shuffle_batch:0", shape=(8, 150, 600, 3), dtype=float32)
DEBUG 2017-12-12 08:36:18.000133: model.py: 359 Views=4 single view: Tensor("AttentionOcr_v1/split:0", shape=(8, 150, 150, 3), dtype=float32)
DEBUG 2017-12-12 08:36:18.000133: model.py: 200 Using final_endpoint=Mixed_5d
DEBUG 2017-12-12 08:36:18.000633: model.py: 200 Using final_endpoint=Mixed_5d
DEBUG 2017-12-12 08:36:18.000832: model.py: 200 Using final_endpoint=Mixed_5d
DEBUG 2017-12-12 08:36:19.000033: model.py: 200 Using final_endpoint=Mixed_5d
DEBUG 2017-12-12 08:36:19.000233: model.py: 365 Conv tower: Tensor("AttentionOcr_v1/conv_tower_fn/INCE/InceptionV3/Mixed_5d/concat:0", shape=(8, 16, 16, 288), dtype=float32)
DEBUG 2017-12-12 08:36:19.000234: model.py: 368 Conv tower w/ encoded coordinates: Tensor("AttentionOcr_v1/conv_tower_fn/INCE/InceptionV3/Mixed_5d/concat:0", shape=(8, 16, 16, 288
), dtype=float32)
DEBUG 2017-12-12 08:36:19.000236: model.py: 371 Pooled views: Tensor("AttentionOcr_v1/pool_views_fn/STCK/Reshape:0", shape=(8, 1024, 288), dtype=float32)
DEBUG 2017-12-12 08:36:19.000236: sequence_layers.py: 421 Use AttentionWithAutoregression as a layer class
DEBUG 2017-12-12 08:36:20.000406: model.py: 374 chars_logit: Tensor("AttentionOcr_v1/sequence_logit_fn/SQLR/concat:0", shape=(8, 37, 134), dtype=float32)
WARNING:tensorflow:From /root/models/research/attention_ocr/python/model.py:406: get_total_loss (from tensorflow.contrib.losses.python.losses.loss_ops) is deprecated and will be r
emoved after 2016-12-30.
Instructions for updating:
Use tf.losses.get_total_loss instead.
WARNING 2017-12-12 08:36:21.000031: tf_logging.py: 90 From /root/models/research/attention_ocr/python/model.py:406: get_total_loss (from tensorflow.contrib.losses.python.losses.lo
ss_ops) is deprecated and will be removed after 2016-12-30.
Instructions for updating:
Use tf.losses.get_total_loss instead.
WARNING:tensorflow:From /opt/conda/envs/py27/lib/python2.7/site-packages/tensorflow/contrib/losses/python/losses/loss_ops.py:261: get_losses (from tensorflow.contrib.losses.python
.losses.loss_ops) is deprecated and will be removed after 2016-12-30.
Instructions for updating:
Use tf.losses.get_losses instead.
WARNING 2017-12-12 08:36:21.000032: tf_logging.py: 90 From /opt/conda/envs/py27/lib/python2.7/site-packages/tensorflow/contrib/losses/python/losses/loss_ops.py:261: get_losses (fr
om tensorflow.contrib.losses.python.losses.loss_ops) is deprecated and will be removed after 2016-12-30.
Instructions for updating:
Use tf.losses.get_losses instead.
WARNING:tensorflow:From /opt/conda/envs/py27/lib/python2.7/site-packages/tensorflow/contrib/losses/python/losses/loss_ops.py:263: get_regularization_losses (from tensorflow.contri
b.losses.python.losses.loss_ops) is deprecated and will be removed after 2016-12-30.
Instructions for updating:
Use tf.losses.get_regularization_losses instead.
WARNING 2017-12-12 08:36:21.000032: tf_logging.py: 90 From /opt/conda/envs/py27/lib/python2.7/site-packages/tensorflow/contrib/losses/python/losses/loss_ops.py:263: get_regulariza
tion_losses (from tensorflow.contrib.losses.python.losses.loss_ops) is deprecated and will be removed after 2016-12-30.
Instructions for updating:
Use tf.losses.get_regularization_losses instead.
WARNING:tensorflow:From /opt/conda/envs/py27/lib/python2.7/site-packages/tensorflow/contrib/training/python/training/training.py:412: get_or_create_global_step (from tensorflow.co
ntrib.framework.python.ops.variables) is deprecated and will be removed in a future version.
Instructions for updating:
Please switch to tf.train.get_or_create_global_step
WARNING 2017-12-12 08:36:21.000072: tf_logging.py: 90 From /opt/conda/envs/py27/lib/python2.7/site-packages/tensorflow/contrib/training/python/training/training.py:412: get_or_cre
ate_global_step (from tensorflow.contrib.framework.python.ops.variables) is deprecated and will be removed in a future version.
Instructions for updating:
Please switch to tf.train.get_or_create_global_step
2017-12-12 08:36:29.531740: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 
AVX AVX2 FMA
2017-12-12 08:36:29.763497: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 0 with properties:
name: GeForce GTX 780 Ti major: 3 minor: 5 memoryClockRate(GHz): 0.928
pciBusID: 0000:01:00.0
totalMemory: 2.93GiB freeMemory: 2.49GiB
2017-12-12 08:36:29.934856: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 1 with properties:
name: GeForce GT 730 major: 3 minor: 5 memoryClockRate(GHz): 0.954
pciBusID: 0000:02:00.0
totalMemory: 981.12MiB freeMemory: 954.88MiB
2017-12-12 08:36:30.134193: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 2 with properties:
name: GeForce GT 730 major: 3 minor: 5 memoryClockRate(GHz): 0.954
pciBusID: 0000:03:00.0
totalMemory: 981.12MiB freeMemory: 954.88MiB
2017-12-12 08:36:30.134271: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Device peer to peer matrix
2017-12-12 08:36:30.134337: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1051] DMA: 0 1 2
2017-12-12 08:36:30.134348: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1061] 0:   Y N N
2017-12-12 08:36:30.134357: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1061] 1:   N Y N
2017-12-12 08:36:30.134365: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1061] 2:   N N Y
2017-12-12 08:36:30.134381: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: GeForce GTX 780 Ti, pci bus id
: 0000:01:00.0, compute capability: 3.5)
2017-12-12 08:36:30.134392: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1107] Ignoring gpu device (device: 1, name: GeForce GT 730, pci bus id: 0000:02:00.0, compute capability: 3.5) with Cuda multiprocessor count: 2. The minimum required count is 8. You can adjust this requirement with the env var TF_MIN_GPU_MULTIPROCESSOR_COUNT.
2017-12-12 08:36:30.134403: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1107] Ignoring gpu device (device: 2, name: GeForce GT 730, pci bus id: 0000:03:00.0, compute capability: 3.5) with Cuda multiprocessor count: 2. The minimum required count is 8. You can adjust this requirement with the env var TF_MIN_GPU_MULTIPROCESSOR_COUNT.
INFO:tensorflow:Restoring parameters from /tmp/attention_ocr/train/model.ckpt-0
INFO 2017-12-12 08:36:30.000152: tf_logging.py: 82 Restoring parameters from /tmp/attention_ocr/train/model.ckpt-0
INFO:tensorflow:Starting Session.
INFO 2017-12-12 08:36:39.000435: tf_logging.py: 82 Starting Session.
INFO:tensorflow:Saving checkpoint to path /tmp/attention_ocr/train/model.ckpt
INFO 2017-12-12 08:36:39.000625: tf_logging.py: 82 Saving checkpoint to path /tmp/attention_ocr/train/model.ckpt
INFO:tensorflow:Starting Queues.
INFO 2017-12-12 08:36:39.000629: tf_logging.py: 82 Starting Queues.
INFO:tensorflow:global_step/sec: 0
INFO 2017-12-12 08:36:42.000843: tf_logging.py: 121 global_step/sec: 0
INFO:tensorflow:Recording summary at step 1.
INFO 2017-12-12 08:36:57.000238: tf_logging.py: 82 Recording summary at step 1.
INFO:tensorflow:global step 1: loss = 183.7825 (17.617 sec/step)
INFO 2017-12-12 08:36:57.000382: tf_logging.py: 82 global step 1: loss = 183.7825 (17.617 sec/step)
INFO:tensorflow:global step 2: loss = 178.9005 (0.576 sec/step)
INFO 2017-12-12 08:36:58.000141: tf_logging.py: 82 global step 2: loss = 178.9005 (0.576 sec/step)
INFO:tensorflow:global step 3: loss = 172.0602 (0.573 sec/step)
INFO 2017-12-12 08:36:58.000723: tf_logging.py: 82 global step 3: loss = 172.0602 (0.573 sec/step)
INFO:tensorflow:global step 4: loss = 162.3374 (0.551 sec/step)
INFO 2017-12-12 08:36:59.000282: tf_logging.py: 82 global step 4: loss = 162.3374 (0.551 sec/step)
INFO:tensorflow:global step 5: loss = 154.0065 (0.537 sec/step)
INFO 2017-12-12 08:36:59.000825: tf_logging.py: 82 global step 5: loss = 154.0065 (0.537 sec/step)
INFO:tensorflow:global step 6: loss = 137.2892 (0.584 sec/step)
INFO 2017-12-12 08:37:00.000415: tf_logging.py: 82 global step 6: loss = 137.2892 (0.584 sec/step)
INFO:tensorflow:global step 7: loss = 125.0101 (0.590 sec/step)
INFO 2017-12-12 08:37:01.000015: tf_logging.py: 82 global step 7: loss = 125.0101 (0.590 sec/step)
INFO:tensorflow:global step 8: loss = 111.8398 (0.538 sec/step)
INFO 2017-12-12 08:37:01.000562: tf_logging.py: 82 global step 8: loss = 111.8398 (0.538 sec/step)
INFO:tensorflow:global step 9: loss = 113.1288 (0.521 sec/step)
INFO 2017-12-12 08:37:02.000091: tf_logging.py: 82 global step 9: loss = 113.1288 (0.521 sec/step)
INFO:tensorflow:global step 10: loss = 119.3941 (0.565 sec/step)
INFO 2017-12-12 08:37:02.000669: tf_logging.py: 82 global step 10: loss = 119.3941 (0.565 sec/step)
INFO:tensorflow:global step 11: loss = 118.0959 (0.599 sec/step)
INFO 2017-12-12 08:37:03.000284: tf_logging.py: 82 global step 11: loss = 118.0959 (0.599 sec/step)
INFO:tensorflow:global step 12: loss = 131.7896 (0.553 sec/step)
INFO 2017-12-12 08:37:03.000841: tf_logging.py: 82 global step 12: loss = 131.7896 (0.553 sec/step)
INFO:tensorflow:global step 13: loss = 119.9262 (0.537 sec/step)
INFO 2017-12-12 08:37:04.000387: tf_logging.py: 82 global step 13: loss = 119.9262 (0.537 sec/step)
INFO:tensorflow:global step 14: loss = 112.8570 (0.576 sec/step)
...
```

読み取り実施
===

実行コード
---

* READMEには[inferenceのコードは提供していない](https://github.com/tensorflow/models/tree/master/research/attention_ocr#how-to-use-a-pre-trained-model)と書いてあるんですが、ソースをよく見ると[demo_inference.py](https://github.com/tensorflow/models/blob/master/research/attention_ocr/python/demo_inference.py)というのが置いてあるのを発見しました
* あくまでデモ用だと強く書いてありますが、とりあえず今回はこれで動かしてみます。

予測対象の画像の準備
---

* 画像は上記の通り４枚の写真が横に連なっている物を準備します
* サイズは600x150固定なのでサイズが異なっている場合はImageMagickだとかでリサイズしてから入力させます
* またファイル名には必ず数字を含めて、そこを```%d```形式で指定します

```
convert -scale 600x150! /tmp/impasse.png /tmp/impasse0.jpg
python demo_inference.py --batch_size=1 --checkpoint=/tmp/attention_ocr/train/model.ckpt-209056 --image_path_pattern=/tmp/impasse%d.jpg
```

予測結果(バッチサイズ:4, 20万ステップ)
---

![avdessapins.png](https://qiita-image-store.s3.amazonaws.com/0/27361/67d1f704-a764-bf96-2ffc-5f4e50a0b281.png)

```
Predicted strings:
Au de Wa de Wa de
```

うーむ・・・

![impasse.png](https://qiita-image-store.s3.amazonaws.com/0/27361/226113ac-0f85-13e0-7d1c-3981d84c54ad.png)

```
Predicted strings:
Impas du J Jwa Imp Imaint Avent du du
```

若干マシ・・・かな？

![rue.png](https://qiita-image-store.s3.amazonaws.com/0/27361/8679c84e-0429-49cb-13a3-a50ab9be8c55.png)

```
Predicted strings:
Rue du du du du du du Avara du du du 
```

おっ、メインの部分は正解してる！

予測結果(バッチサイズ:32, 26万ステップ)
---

* 後日、もっといいGPU(Geforce TITAN X)で数日かけて学習し直しました
* GPUのメモリが増えたのでバッチサイズを標準の32に上げています

![avdessapins.png](https://qiita-image-store.s3.amazonaws.com/0/27361/67d1f704-a764-bf96-2ffc-5f4e50a0b281.png)

```
Predicted strings:
Ale Ale Ale Al A Al Ana—— Ale Ale A A
```

悪化してる気がするな・・・

![impasse.png](https://qiita-image-store.s3.amazonaws.com/0/27361/226113ac-0f85-13e0-7d1c-3981d84c54ad.png)

```
Predicted strings:
Impasse Anporgeste————4 Ro—de—Anporge
```

おお！メイン部分は正解！

![rue.png](https://qiita-image-store.s3.amazonaws.com/0/27361/8679c84e-0429-49cb-13a3-a50ab9be8c55.png)

```
Predicted strings:
Rue Antaue de Antant—Aronte
```

若干改善しているかな？

終わりに
===

* なんか微妙な結果でしたが一旦終わります
* ファインチューニング/転移学習も試したいなあ