Inception-v3は猫の同種個体差を見分けられるか

うちの実家ではチンチラを２匹(モモとタマ)飼っているのでInception-v3でその２匹を見分けれるかを検証しました。
きっかけはGDG DevFest Tokyo 2017のセッション「Android Things＋TensorFlowで作る猫トイレ監視システム」にて判別が上手く行かなかったとの話だったのですが、２匹混ざってる写真等も判定しようとしていたので純粋に２匹を見分けれるかに興味が湧いたので試してみました。

#学習データの用意
これが大変でした…。
そんなに写真を持ってる訳ではなかったので、会社帰りにたまに実家に寄っては２匹の写真をチマチマ取りました。
１匹あたり大体150枚程の写真を撮り、更には左右を反転させたデータも用意して学習データとして使用しました。
同じチンチラにしては顔つき、体格、毛の色合いが違うので飼い主的には簡単に見分けられるのですが知人等は難しいと言います。皆さんはどうなんでしょう？
ただそうは言っても角度等によっては飼い主でも間違えることはあります。なので、写真を整理してる際にもどっちか分からないような写真はデータからは除外しました。

用意したデータは↓のような感じ。

![猫学習データ.jpg](https://qiita-image-store.s3.amazonaws.com/0/200860/99708b1f-2629-1873-ef91-efe08b32c5fb.jpeg)

#環境の構築
次にTensorFlow上でInception-v3を転移学習させれる環境の構築です。
この環境は比較的簡単に構築できます。多大なリソースを使って学習させたモデルを無料で使えるなんてGoogleさんは太っ腹ですな。
Ubuntu 16.04上への構築です。

まず最初にソースを落としたり。

```bash:bash
$ sudo apt install git
$ git clone https://github.com/tensorflow/tensorflow
$ cd tensorflow/
$ git checkout r1.3
$ sudo apt-get install openjdk-8-jdk
$ echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
$ curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
$ sudo apt-get update && sudo apt-get install bazel
$ sudo apt-get install python-numpy python-dev python-pip python-wheel
$ ./configure
```
configureを実行すると色々聞かれるので基本はデフォルトもしくはNoで答えときます。

```
Extracting Bazel installation...
...........
You have bazel 0.6.1 installed.
Please specify the location of python. [Default is /usr/bin/python]: /usr/bin/python
Found possible Python library paths:
  /usr/local/lib/python2.7/dist-packages
  /usr/lib/python2.7/dist-packages
Please input the desired Python library path to use.  Default is [/usr/local/lib/python2.7/dist-packages]
/usr/local/lib/python2.7/dist-packages
Do you wish to build TensorFlow with MKL support? [y/N] N
No MKL support will be enabled for TensorFlow
Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is -march=native]: -march=native
Do you wish to use jemalloc as the malloc implementation? [Y/n] n
jemalloc disabled
Do you wish to build TensorFlow with Google Cloud Platform support? [y/N] N
No Google Cloud Platform support will be enabled for TensorFlow
Do you wish to build TensorFlow with Hadoop File System support? [y/N] N
No Hadoop File System support will be enabled for TensorFlow
Do you wish to build TensorFlow with the XLA just-in-time compiler (experimental)? [y/N] N
No XLA JIT support will be enabled for TensorFlow
Do you wish to build TensorFlow with VERBS support? [y/N] N
No VERBS support will be enabled for TensorFlow
Do you wish to build TensorFlow with OpenCL support? [y/N] N
No OpenCL support will be enabled for TensorFlow
Do you wish to build TensorFlow with CUDA support? [y/N] N
No CUDA support will be enabled for TensorFlow
Do you wish to build TensorFlow with MPI support? [y/N] N
MPI support will not be enabled for TensorFlow
Configuration finished
```
次にInception-v3のC++モデルをビルドして動作するようにします。
※これはもしかしたら実行しなくてもいいかも

```bash:bash
$ curl -L "https://storage.googleapis.com/download.tensorflow.org/models/inception_v3_2016_08_28_frozen.pb.tar.gz" | tar -C tensorflow/examples/label_image/data -xz
$ bazel build tensorflow/examples/label_image/...
$ bazel-bin/tensorflow/examples/label_image/label_image
```

label_imageを叩いて↓の結果が表示されればOKです。
examples/label_image/data/grace_hopper.jpg
の画像を判別してるようです。

```
2017-10-12 19:41:57.889176: I tensorflow/examples/label_image/main.cc:250] military uniform (653): 0.834306
2017-10-12 19:41:57.889217: I tensorflow/examples/label_image/main.cc:250] mortarboard (668): 0.0218692
2017-10-12 19:41:57.889234: I tensorflow/examples/label_image/main.cc:250] academic gown (401): 0.0103579
2017-10-12 19:41:57.889239: I tensorflow/examples/label_image/main.cc:250] pickelhaube (716): 0.00800814
2017-10-12 19:41:57.889242: I tensorflow/examples/label_image/main.cc:250] bulletproof vest (466): 0.00535088
```

そして最後に転移学習の環境をビルド。

```bash:bash
$ bazel build tensorflow/examples/image_retraining:retrain
$ pip install mock
```

#転移学習
次にいよいよ学習を走らせます。
用意した学習データを判別したいクラス毎にフォルダに格納しておきます。
※今回の場合は/home/knox/TFtrain/nekoにmomoとtamaフォルダを作成して格納。

そして学習の実行！

```bash:bash
$ bazel-bin/tensorflow/examples/image_retraining/retrain --how_many_training_steps 800 --image_dir /home/knox/TFtrain/neko
```

ステップ数を多くしてもあんまり判定率が改善しないので800にしてます。意外と時間かからないんですよね。ほんとにこれでいいのだろうか…？
ただメモリが10GB位無いと全然動かないです。

結果はこんな感じ。

```
INFO:tensorflow:2017-11-06 21:56:11.017447: Step 799: Train accuracy = 99.0%
INFO:tensorflow:2017-11-06 21:56:11.017626: Step 799: Cross entropy = 0.110971
INFO:tensorflow:2017-11-06 21:56:11.068960: Step 799: Validation accuracy = 91.0% (N=100)
INFO:tensorflow:Final test accuracy = 91.1% (N=45)
```

#検証
最後にテストデータで検証してみます。
テストデータは学習には使用していない専用のデータを用意しました。
また学習データは比較的短期間で用意するために家の中で猫の周りをグルグル回るように撮ったので、背景が学習要素になっちゃってるような気もしたので背景を適当に黒塗りした画像も用意してテストしました。

##結果

| 猫   | テストデータ数  | 誤判定数     | 正解率    |
|:----|:--------------|:------------|:---------|
| モモ | 29            | 2           | 93%      |
| タマ | 19            | 4 　　       | 79%     |
| 全体 | 48            | 6 　　       | 87%     |

まぁ学習データの数の割には悪くは無い？という感じでしょうか。

##誤判定詳細
判定を誤った画像は↓

![猫失敗データ.jpg](https://qiita-image-store.s3.amazonaws.com/0/200860/ca9d02a4-a182-4e19-576a-cc1c00da7617.jpeg)

#まとめ
- 予想してたよりは精度が出た。
- 全身じゃなくて顔だけに絞ったりすればもっと精度は上がるような気がする。
- 誤判定の結果を見ると背景の存在有無で判定結果が変わってしまっているのが分かる。(テスト用に撮影した画像は背景塗りつぶし版も用意したので、A写真で誤判定したなら必ずA写真の背景塗りつぶし版でも誤判定するという考え)これはテストデータのバリエーション不足のせいで背景も判断の要因の一因になってしまってるのかもしれない…。
- 余談だが途中でGooglePhotoが人間の顔判別だけじゃなくてペットの顔判別もしてくれる機能があることに気がついた。ただタマとモモの判別は全然出来てない。

#参考リンク
https://www.tensorflow.org/
https://tokyo.gdgjapan.org/
http://sssslide.com/speakerdeck.com/kojira/android-things-plus-tensorflowdezuo-rumao-toirejian-shi-sisutemu