TensorFlowによるももクロメンバー顔認識（後編）

前編: http://qiita.com/kenmaz/items/4b60ea00b159b3e00100
中編: http://qiita.com/kenmaz/items/ef0a1308582fe0fc5ea1

機械学習やディープラーニングについては全く初心者のソフトウェアエンジニアが、畳込みニューラルネットワークを用いて「ももいろクローバーZ」のメンバーの顔識別を行うアプリを作った話の続きです。

#学習過程

[前回](http://qiita.com/kenmaz/items/ef0a1308582fe0fc5ea1)までで精度85%のモデルができたものの、無知の状態からのスタートだったので最初の頃は全然精度が出せてませんでした。誰かの役に立つかもしれないので試行錯誤の経過をメモっておこう。

## ファーストバージョン
  - MNISTのexpertのサンプルを元にとりあえず実装
  - => **精度53~56%**
  - 課題1: 例えば、右向きの顔写真=>全部「高城れに」、真正面を向いている写真はあーりん、といったようなおかしな判定をしているように見えた
  - 課題2: 訓練データがそもそも各メンバー50枚ずつくらいしか用意できてなかった

## 訓練データの改善
  - 各訓練データ画像から、左右反転、ズーム・縮小、回転したバージョンをそれぞれ生成して訓練データを水増しした
  - 再度crawlerを実行し、訓練データを追加し、各メンバーごとに200枚くらい用意した
  - => **精度58%**
  - ログをよく見ると訓練の途中からテストデータに対する正答率が一定になっていた

## 訓練データの改善2
  - 水増しした訓練データの一部が偏って使われていた
  - batchで流す訓練データをランダムに選択するようにした
  - => **精度65-70%**

## 長時間の学習
  - これまでは手元のMacBookProで数百stepくらいでなんとなく訓練を打ち切ってた
  - しかしサンプルコードなどを見るとmax_step=10万とかになってて、そもそも学習が足りてないのではと考える
  - ec2上に環境を作っていろんなインスタンスを借りてぶん回してみることに
  - 1時間1ドルの c4.4xlarge を借りて15,000ステップくらい実行
  - => **精度73%**
  - 実際は8000ステップでも73%程度いってた

## テストデータの再見直し
メンバー別に精度を見てみると以下のようになっていた。

|member|精度|
|:-----|---|
|れに  | 77% |
|夏菜子  | 44% |
|詩織  | 73% |
|あーりん|58%|
|杏果|88%|

「うん確かに推されたいは特徴的な顔してるからな・・美男美女ほど平均顔に近いという話もあるからな・・ん？」という考えが頭に浮かばなかったわけでもなかったが、冷静に考えるとやはり訓練データ自体のばらつきが大きすぎるんだろうな、という考えに至った。（[もも角度](http://livedoor.blogimg.jp/tomoyun-zubapedia/imgs/b/5/b56583c9.jpg) ってのもありますし、杏果が比較的精度が高いのも納得・・）

これまで使ってきた訓練データは、何度も作り直しした結果の寄せ集めだったので、それらを生成するプログラム自体も微妙に違ってて、それが訓練データのバラ付きにつながったっぽい。

ちょうどwebサービスとして公開するための準備も並行してすすめており、顔認識部分をもうちょいなんとかしなきゃな、ってところだったので、各メンバー間でばらつきが出ないような安定した顔認識のロジックが必要だな、ということで調整を進めた結果が前編で解説した[現状のコード](https://github.com/kenmaz/momo_mind/blob/master/face_detect/detect.py)。こいつで訓練データをすべて作り直して再度訓練。

=> **精度77%**

## 畳み込み、プーリング層を追加
- 安定した訓練データができた実感があったので、ここでモデルに層を追加しよりディープに。それが前回解説した[現状のモデルのコード](https://github.com/kenmaz/momo_mind/blob/master/deeplearning/mcz_model.py)
- => **精度85%**

というように、いろいろアホな間違いを犯しつつ、試行錯誤を経たきたのでした。。
**訓練データの質と、繰り返しの訓練が重要**、ということが骨身にしみてよくわかりました。

# ec2便利
ところでスポットでハイパフォーマンスなマシンが必要な今回のようなケースでは、awsみたいな環境はほんと便利ですねー。高いけど。学習時に各インスタンスをいろいろさわったのでざっくりとした性能はこんな感じ。

ためしにすごく深い(16層）のディープなニューラルネットワークを作って訓練を実行してみると..

|インスタンスタイプ|大体の性能|
|:---------------|:----|
|t2.micor         |N/A (リソース不足で実行できず)|
|手元のMacBookPro |3.4 examples/sec; 34.967 sec/batch)|
| c4.4xlarge     |3.9 examples/sec; 31.142 sec/batch|
| c4.8xlarge     |8.6 examples/sec; 14.025 sec/batch|

c4.4xlarge、1時間$2ですが、速いですね。c4.4xlargeはMBPと同じくらいだ。
とはいえ、いずれもTensorFlowのcpuビルドを使った結果なので、今後GPUビルドを試してどんくらい速いのか確かめてみたいですね。

また、当初は手持ちのVPSで色々やろうとしてたんですが、CentOS6で、Glibcのバージョンとnumpyの組み合わせがよろしくなく（CentOS6はglibc v2.12までしかサポートしておらず、numpyはGlibc2.15を要求 => CentOS7が必要）、めんどくさかったので、環境を作っては壊しできる、とうい点でもec2は便利ですねー。高いけど。

# 訓練の成果を外部から叩けるように
学習済みのモデルは `tf.train.Saver` を使って、調整を繰り返した重みやバイアス値のスナップショットをモデルファイルとして出力したり、読み込んだりできるようになっています。

今回は1000step実行するたびにモデルファイルを出力するようにしました。
https://github.com/kenmaz/momo_mind/blob/master/deeplearning/mcz_main.py

学習結果を使ってwebサービスを作りたい場合は、このモデルファイルを読み込み、推論を実行し、web側に結果を返してやるようなコードを書けばいいわけです。

ということで、画像ファイルパスを渡したら、その画像に対し推論を実行し、メンバーの分類結果を返す、というようなコードを用意しました。
https://github.com/kenmaz/momo_mind/blob/master/deeplearning/mcz_eval.py

```
result = mcz_eval.execute([imgfile1], '.', ckpt_path)
```
ckpt_pathのパスにあるモデルファイルを読み込み、imgfile1に対して推論を実行した結果を返す、といったコードを実行できます。

# webアプリとして仕上げ
mcz_eval.pyが出来たので、あとは手慣れたwebプログラミングをすすめるだけです。とはいえPythonでwebアプリを書いたことは無かったので、いろいろ調べたところ、`Flask + uWSGI + Nginx` の組み合わせが筋が良さそう。とうことで下記ページなどを参考に粛々と実装。

 - https://zafiel.wingall.com/archives/7513
 - http://qiita.com/yasunori/items/64606e63b36b396cf695
   - ※関係ありませんが、こちらの記事を[書いた方](http://qiita.com/yasunori)は、私が新卒入社した前前職の同期の書いたものでした。お元気ですかー！

#まとめと今後
だいぶ雑な記事になってしまいまいたが、とりあえず試行錯誤しながら、なんとなくぽいものが出来たよー、というお話でした。

## 今後やってみたいこと
 - 精度をもうちょい高めたい（[twangoさん](http://qiita.com/tawago)アドバイスありがとうございます）
 - 「どんな特徴に反応して、その推論結果を出したのか」を可視化してみたい。例えば「えくぼっぽいピクセルの並びが存在する=夏菜子」「目が離れがち=杏果」みたいな。実はGWに一度ためしてみたんですが、うまく行かず。再度挑戦してみたい。
 - GPUビルド試したい
 - 他の種類の、たとえば再帰型ニューラルネットなども遊んでみたい

以上！