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

前編はこちら
http://qiita.com/kenmaz/items/4b60ea00b159b3e00100

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

# deeplearning
前編ではクローラーが集めてきた画像から顔画像を取り出し、学習用データを生成するところまで説明しました。ここからようやく学習に入ります。

機械学習部分のコードは、Tensorflowに付属にしている [Deep MNIST for Experts](https://www.tensorflow.org/versions/r0.8/tutorials/mnist/pros/index.html)と、[CIFAR-10 Classification](https://www.tensorflow.org/versions/r0.8/tutorials/deep_cnn/index.html)あたりのコードをベースに実装をすすめました。

# CSVの生成と読み込み
gen_testdata.py
https://github.com/kenmaz/momo_mind/blob/master/deeplearning/gen_testdata.py

まず訓練データをTensorflowに食わせる部分ですが、MNISTのサンプルコードと同様、CSVを生成し、そいつを読みこませることにしました。前編では学習用のももクロメンバー画像をそれぞれMacのFinder上でフォルダ分けしましたが、そのフォルダ／ファイルの構造を元に、

```
訓練用画像のファイルパス,メンバー名を0-4に対応させた数値
```
という形式のCSVを吐く小さいスクリプトを書きました。

ちなみにTensorflowでは訓練データを[こんなような](https://github.com/tensorflow/tensorflow/blob/r0.8/tensorflow/core/example/feature.proto) protocol bufferでシリアライズして、[TFRecords](https://www.tensorflow.org/versions/r0.8/how_tos/reading_data/index.html#standard-tensorflow-format)というファイル形式に書き出すのがオススメらしいです。ちょっと面倒だったので今回は見送りました。

## 学習データの入力
以下がCSVを読み込み後述のモデルの入力データとなるTensorを構築するスクリプトです。
mcz_input.py
https://github.com/kenmaz/momo_mind/blob/master/deeplearning/mcz_input.py

TensorFlowにはテキストファイルを読み込む [tf.TextLineReader](https://www.tensorflow.org/versions/r0.8/api_docs/python/io_ops.html#TextLineReader)や、読み込んだデータをcsvとしてデコードしてくれる[tf.decode_csv関数](https://www.tensorflow.org/versions/r0.8/api_docs/python/io_ops.html#decode_csv)など、便利なクラスや関数が予め用意されています。こいつらを使って入力データを読み込めば、そのままTensorFlowへの入力データ形式である[tf.Tensor](https://www.tensorflow.org/versions/r0.8/api_docs/python/framework.html#Tensor)を自動的に構築してくれます。

さらに機械学習のテクニックとして、訓練サンプル画像を左右反転したり、ちょっと回転させたりズームさせたり、コントラスをランダムに変えるなどすることによって、訓練サンプル画像を「水増し」するテクニック（「データ拡張」と呼ぶらしい）がありますが、それらの処理もほとんどTensorFlowが用意してくれています。

ランダムに左右反転させる[tf.image.random_flip_up_down()](https://www.tensorflow.org/versions/r0.8/api_docs/python/image.html#random_flip_up_down), 明るさをランダムに変える[tf.image.random_brightness()](https://www.tensorflow.org/versions/r0.8/api_docs/python/image.html#random_brightness)、同じくコントラストを変える [tf.image.random_contrast](https://www.tensorflow.org/versions/r0.8/api_docs/python/image.html#random_contrast)など。

最初はこれらの関数の存在に気づかず、自分でopenCVを使ってがんばっていたのですが、素直にTensorFlowのものを使ったほうが良さそうですね。

## バッチ入力
今回は訓練データとして、各メンバー150枚ずつ、計750枚の顔画像を用意してあります。これらからランダムに**120**枚の画像を取り出し、上記の通りデータ拡張を行いランダム化させた上で、学習モデルの入力としてまとめてぶち込みます。この作業を1ステップとし、1000ステップ〜30000ステップほどひたすら学習を繰り返します。

## 学習モデル
今回作ったコードの中でおそらくいちばん重要な、学習／推論のモデルを組み立てるスクリプトです。
mcz_model.py
https://github.com/kenmaz/momo_mind/blob/master/deeplearning/mcz_model.py

### モデルの検討その１
まずは以下のような畳み込みニューラルネットワークのモデル定義してみました。
https://github.com/kenmaz/momo_mind/blob/master/deeplearning/mcz_model.py#L6

```
- 入力 (28x28 3chカラー)
- 畳み込み層1
- プーリング層1
- 畳み込み層2
- プーリング層2
- 全結合層1
- 全結合層2
```

これはほとんどCIFAR-10のサンプルそのままのやつです。このモデルだと、分類精度だいたい**65~70%**程度しか出ませんでした。

そもそも入力28x28なのはCIFAR-10のサンプルをそのまま持ってきたからですが、28x28といえば、例えばこれ、28x28の画像です。
![スクリーンショット 2016-05-24 3.19.59.png](https://qiita-image-store.s3.amazonaws.com/0/8399/2dcac161-afcc-ccaf-fecc-5a41698b63eb.png)
うーん、誰でしょうね？（**僕はわかりますけど**）

この荒い画像から特徴を探しださなきゃいけない、ってことで人間がやってもいかにも大変そうです。もっと高解像度にしたい。

### モデルの検討その2
ということでもうちょい精度を高めたいなと思い、入力画像の解像度を倍にして、さらに畳み込み＆プーリング層をひとつづつ追加しました。
https://github.com/kenmaz/momo_mind/blob/master/deeplearning/mcz_model.py#L53

```
- 入力 (56x56 3chカラー)
- 畳み込み層1
- プーリング層1
- 畳み込み層2
- プーリング層2
- 畳み込み層3
- プーリング層3
- 全結合層1
- 全結合層2
```
という畳み込みニューラルネットワークのモデルを定義しました。
すると精度は最終的に**85%**程度まで上昇しました。

56x56の画像といえばこんな感じです。
![スクリーンショット 2016-05-24 3.19.36.png](https://qiita-image-store.s3.amazonaws.com/0/8399/8d585381-a067-89b0-0dfc-22cbb7781f07.png)
今度は誰だかわかりましたよね？

このくらいの解像度になってようやく**えくぼが認識できます**。やっぱりこれくらいはほしいですよね。

さて、じゃあもっと解像度を上げて、層を増やせばもっと精度向上できるんだろうな、と思い、
実はさらに層を増やしたり、入力の解像度を高めたりしたバージョンも作っては見たのですが、なかなかクロスエントロピーが収束せず、うまく行きませんでした。原因はよくわかってません。すぎゃーん氏のブログによると112x112の入力で90-95%まで達成できたとのこと。どこで違いが出たのだろう。。

### モデルのグラフ
TensorFlowには学習モデルをグラフで表示してくれる機能が付いているので、雑な説明つきで以下に貼っておきます。

![スクリーンショット 2016-05-24 3.09.13.png](https://qiita-image-store.s3.amazonaws.com/0/8399/86739c06-75b0-6d87-2105-0d6ac72f47fb.png)

下から上に向けてデータが流れていくイメージです。

#中編おわり
そろそろ眠くなってきたので、つづきはまた後日。