初めての深層学習～うちの子はどっちに似てるの？？～

約半年前から、諸事情により急に機械学習しろと言われて困っています。（会社って怖いところですねぇ）
機械学習も深層学習もよく分からなくてもきっと何かできるはずっ！
ということで、今日は自分がずっと気になっていた深層学習にレッツトライ。
注）ここまでのACのレベルから比べるとグッと下がっているのでご容赦を。

# 今回したかったこと
うちには子供が居るが、こいつら両親のどっちに似てるんだろう？それを出したりできないかな？？
というモチベーションが発生。きっかけはFBで子供の写真を上げると、たまに私だとタグ付けされるようになったんですよね。そんなに似てたっけ？？毎日顔をつき合わせてると全く分からないんですよね。
今回はいろんな人の顔の認識とかはハードル高いので、私と旦那の顔を学習させてモデルを作って、子供の顔を分類してみるというのをやってみます。

## 参考にしたもの
google先生にご活躍いただいて、今回は以下のページを参考にしました。
**<画像分類関連>**
参考1) [ディープラーニングでザッカーバーグの顔を識別するAIを作る](https://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)
参考2) [TensorFlowによるももクロメンバー顔認識](https://qiita.com/kenmaz/items/4b60ea00b159b3e00100)
参考3) [Tensorflowによる画像のクラス分類](http://person.hatenablog.jp/entry/2017/11/24/224104)
**＜必要ライブラリインストール＞**
参考4) [UbuntuにOpenCVとChainerをインストール](https://qiita.com/sheep6box/items/37fb63c41d93c088013a)
参考5) [OpenCV 3.2 Installation Guide on Ubuntu 16.04](https://github.com/BVLC/caffe/wiki/OpenCV-3.2-Installation-Guide-on-Ubuntu-16.04)
**＜動かす際に困ったとき＞**
参考6) [TensorFlow の "AttributeError: 'module' object has no attribute 'xxxx'" エラーでつまづいてしまう人のための移行ガイド](https://qiita.com/shu223/items/ef160cbe1e9d9f57c248)
参考7) [深層学習の学習【その2】[追記]](http://kuroyagi.hatenablog.com/entry/2017/02/28/001233)
参考8) [Python2からPython3.0での変更点](https://qiita.com/CS_Toku/items/353fd4b0fd9ed17dc152)
**＜TensorFlowのサンプル直す時に仕組みを理解するのに役立ったページ＞**
参考9) [【本当の初心者向け】ニューラルネットとTensorFlow入門のためのオリジナルチュートリアル１](http://s0sem0y.hatenablog.com/entry/2017/07/07/101949)

## 手順の概要
細かい深層学習のところは正直数週間レベルでは理解しきれなかったので割愛。
とりあえず、[参考1](https://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)と[参考2](https://qiita.com/kenmaz/items/4b60ea00b159b3e00100)を動かしてみるという感じで進めました。
大まかな手順は以下のとおり。後半に動作環境とかそれぞれ苦悩とか書き残しておきます。

	1. 画像の収集
	2. 収集した画像から顔付近を切り出す（私、旦那、子供３人分）
	3. 私と旦那の画像で２値分類してくれるようなモデルを作成
	4. 3で作ったモデルで子供の画像を評価
	5. 4の結果をグラフとかにまとめてみる

3の部分は[参考1](https://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)と[参考2](https://qiita.com/kenmaz/items/4b60ea00b159b3e00100)、両方試してみております。
ただ、参考1の方は学習用のデータを28*28pixelにリサイズしてるんだけど、その画像は私自身が見ても「た、たぶん旦那？？」というレベルの識別度合だったので、最終的な結果は参考2の方の56*56pixelのバージョンで算出してます。56*56の方がとりあえず誰だか判別は可能でした。

今回使った顔写真の枚数

|人|学習|テスト|
|---|---:|---:|
|パパ|57枚|6枚|
|ママ|64枚|8枚|

。。。後述してますが、意外と大人の写真がなくてこんな枚数です。

## 結果
事前情報として、子供の簡単な情報を書いておくと以下のとおり。

|ID|性別|年代|写真の枚数|
|--------|--------|------|---:|
|子1|男の子|小2|89枚|
|子2|女の子|年長|149枚|
|子3|女の子|年少|144枚|

長男は年齢も上がってきて直近数年は写真が減ってきて、下二人が増えてますね。
ちなみに子供たちは両親というより、3人がとても似てるとよく言われてます。ブレンド具合が一緒なんだろうかね？？

結論からいうと、子1は旦那似。子2、子3は私似っぽいかなぁ。
写真によって、どちらというのも結構ぶれていたので顔の角度とか表情とかに依存する部分もありそう。
子供って時期によって似てると言われる方が変わったりするので、年齢別で写真集めてやってみても面白そうですね～！

今回は顔写真別に、パパ：〇%,ママ：△%って形で出てきて、〇＋△＝100%となります。その結果をヒストグラムにしてみました。
### 子1の結果
![c1_56.png](https://qiita-image-store.s3.amazonaws.com/0/216557/e24813c4-7bf1-6341-2f41-2b416925ccff.png)
パパとママ足して100%になるデータを書いてるので赤と青で左右対称になるんですが、子1はパパが高確率になる頻度が高く、今回作ったモデルにおいては旦那に似てるという感じ。
パパ側の数値では、平均が69.15%、標準偏差が41.41。

### 子2の結果
![c2_56.png](https://qiita-image-store.s3.amazonaws.com/0/216557/db598c0e-df4c-218a-0f53-4cdb3a3a9dc8.png)
こちらは、子1とは逆っぽい傾向でママ寄り。
ママの平均が71.54%、標準偏差が40.39。

ちなみに、子2だけ参考1のモデルでは旦那寄りの結果が出ていて傾向が異なったのでメモ。普通に見てる直感からすると、私に似てるかな～って感じなので上図のモデルの方がより感覚に近いかもしれない。

### 子3の結果
![c3_56.png](https://qiita-image-store.s3.amazonaws.com/0/216557/1b0eb6af-7347-3338-cd57-06711b692e9b.png)
この子も子2と似たような傾向だが、子1よりはパパとママ両方に振れてるかなぁ。。。
ママの平均が63.13%、標準偏差が43.68。


## 手順詳細
### 0. 今回の実行環境
4コア、メモリ8GのVMをWin10上に作成してます。

|項目|使ったバージョンとか|
|---|---|
|OS|ubuntu16.04 LTS|
|python|3.5.4|
|TensorFlow|1.4.0|
|OpenCV|3.2.0|

### 1. 画像の収集
子供の写真：私物のスマホからここ２年くらいのを取得
両親の写真：結婚式とかの写真を漁って取得
子供の写真は良かったんだけど、両親。。。特に最近の写真がないのですよ。。。子供は撮るけど自分自身が入ることってあんまりないんですよね。。。仕方ないので色々なところオンラインストレージに散らばってる写真を収集しました。

### 2. 収集した画像から顔付近を切り出す（私、旦那、子供３人分）
[参考2](https://qiita.com/kenmaz/items/4b60ea00b159b3e00100)の顔切り出し部分のプログラムを動かしてみることに。
OpenCV使ってるんですが、ここの依存ライブラリを入れるところが一番時間かかった気がする。。。週末夜中の光回線が死ぬほど遅くて。。。
#### OpenCVインストール
 [参考4](https://qiita.com/sheep6box/items/37fb63c41d93c088013a)と [参考5](https://github.com/BVLC/caffe/wiki/OpenCV-3.2-Installation-Guide-on-Ubuntu-16.04)を見つつインストール。
cmakeでつまづいたり、makeできたけどpython3が上手く認識されてなくてそれ用のオブジェクトが出力されなかったりして参考サイトを見つつ色々いじってましたが、とりあえず自分のpyenvの環境で動くことまで確認。
#### プログラム動かして顔部分の切り出し
2点修正して実行
- 参考プログラムとpythonのメジャーバージョンが異なったので修正
- 口の認識を入れると壊滅的に抽出してくれなかったのでそこの処理をスキップさせるように修正

目視で7割くらいは顔の部分が抽出されてて、3割は花柄とか塗り絵の絵とか色々混ざっておりました。手作業よりは圧倒的に楽チン。

しかして、サンプルとバージョンそろえればよかった。。。と思いつつ何となくそのまま進めてしまいました。。。（意外と何とかなる）

### 3. 私と旦那の画像で２値分類してくれるようなモデルを作成
とにもかくにもモデル作らないと始まらないので。。。
- [参考1](https://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)の学習部分
- [参考2](https://qiita.com/kenmaz/items/4b60ea00b159b3e00100)のモデルの検討2

で二つモデル作成。

参考1はmax_step=100だったので、あっさり学習終了。accuracyは75%程度。
参考2はmax_step=10万！？で、気が付かず実行して一向に終わらず。。。ざっと計算したら使ってるマシンだと27時間くらいかかるらしい。お遊び程度なので、今回は3000にして1時間かからないくらいで終わらせました。こっちはaccuracy計算してなかったので自分でテストして93%くらいでした。

動かすにあたって、ソースの修正点は以下のとおり。
- pythonのバージョン違いだったので修正（主にprintのかっこと、切り捨て除算が//に変更されてたあたり）
- TensorFlowもバージョン違いだったので修正（[参考6]((https://qiita.com/shu223/items/ef160cbe1e9d9f57c248))の通り直すと動く）

### 4. 3で作ったモデルで子供の画像を評価
ここは、参考1も参考2もWebアプリ対象で1枚読み込んで評価を返す形の作りになっていて、3人分合計約400枚ほど一気に実行すると途中で落ちる。。。落ちる箇所は決まって

```
saver.restore(sess, ckpt_path)
```

ここ。そして、10枚めで落ちることもあれば300枚で落ちることもあるしなんなんでしょうか。。。
色々調べると、Segmentation Faultらしい？？画像の枚数分呼び出してるのはダメな気がするし、頭で一度呼べばいいんじゃないかな。。。
とりあえず、バッチ向けに書き換えました。
CSVに出力したかったので、特定の人物の顔写真（切り抜き済み）が入ってるディレクトリとモデルのパス、CSVのファイルオブジェクトもらって評価結果をファイルに書き込んでます。
mcz_inputとmcz_modelは参考2の方のソース。下記も参考2の方のソースを修正してる感じです。参考2ではimageを直接渡していたのをplace holderに変えてるとかが差分。

```py3:eval.py
import sys
import numpy as np
import cv2
import tensorflow as tf
import os
import mcz_input
import mcz_model

def evaluation(imgdir, ckpt_path, wf):
    tf.reset_default_graph()

    images_placeholder = tf.placeholder("float32", shape=(None, mcz_input.DST_INPUT_SIZE * mcz_input.DST_INPUT_SIZE * 3))
    keep_prob = tf.placeholder("float32")
    logits = mcz_model.inference_deep(images_placeholder, keep_prob, mcz_input.DST_INPUT_SIZE, mcz_input.NUM_CLASS)

    sess = tf.InteractiveSession()
    saver = tf.train.Saver()
    sess.run(tf.global_variables_initializer())
    if ckpt_path:
        saver.restore(sess, ckpt_path)

    # ファイル一覧を取得してそのファイルに対して処理をする
    files = os.listdir(imgdir)
    for imgpath in files:
        jpeg = tf.read_file(os.path.join(imgdir,imgpath))
        image = tf.image.decode_jpeg(jpeg, channels=3)
        image = tf.cast(image, tf.float32)
        image.set_shape([mcz_input.IMAGE_SIZE, mcz_input.IMAGE_SIZE, 3])
        image = tf.image.resize_images(image, [mcz_input.DST_INPUT_SIZE, mcz_input.DST_INPUT_SIZE])
        image = tf.image.per_image_standardization(image)
        image = tf.reshape(image, [-1, mcz_input.DST_INPUT_SIZE * mcz_input.DST_INPUT_SIZE * 3])
        print(image)

        softmax = logits.eval(feed_dict={images_placeholder:image.eval() ,keep_prob: 1.0 })

        result = softmax[0]
        rates = [round(n * 100.0, 1) for n in result]
        print(rates)

        pred = np.argmax(result)
        print (mcz_input.MEMBER_NAMES[pred])

        members = []
        for idx, rate in enumerate(rates):
            name = mcz_input.MEMBER_NAMES[idx]
            members.append({
                'name_ascii': name[1],
                'name': name[0],
                'rate': rate
            })
        wf.write('{0},{1:.3f},{2:.3f}\n'.format(imgpath,members[0]['rate'],members[1]['rate']))

    sess.close()

if __name__ == '__main__':
    ckpt_path = sys.argv[1] # モデルのパスだけ引数でもらう
    children = ['ko1','ko2','ko3']
    datapath = './data/children/'

    for child in children:
        wf = open(child + '_result.csv','w')
        evaluation(os.path.join(datapath,child), ckpt_path ,wf)
        wf.close()
```

### 5. 4の結果をグラフとかにまとめてみる
ここは簡単にpandasつかってcsv読み込んでプロットしてるだけです。

```py3
import matplotlib.pyplot as plt
import pandas as pd

children = ['ko1','ko2','ko3']

for child in children:
    print(child + 'の結果')
    data = pd.read_csv(child + '_result.txt',names=('picname', 'papa', 'mama'))
    print(data.describe())
    data.plot.hist(y=['papa','mama'],bins=20, alpha=0.5)
    plt.show()
```

# 終わりに
あんまり知識なくても、ライブラリもそろいつつあり、世の中の人も色々試しています。なので、深層学習やる時に[あの難しい本](https://www.amazon.co.jp/dp/476490487X/)を頑張って理解しなくても、それっぽく動かせることがわかったのでした。
結局大変だったのは写真集めとその写真を目視で分類するところだったりして、機械学習・深層学習系のプロダクト作るとき肝になる部分て正解データ作成だよなぁ。。。と改めて思ったりしました。

もう少し踏み込んで、どの辺を評価して私だと判定してるのか、とか分かるようになるとよかったんですが時間切れでした。また勉強したら何か書けるといいな～。

最後に。。。
顔写真の分類ね、好きな人でやった方がいいと参考サイトに書いてあったけどさ、自分の写真とか、いくら大好きでも旦那の写真をうん十枚もならべてみるのは何だか「うわーーーーっ！」って気分だったので、**本当に**大好きな芸能人とか恋人の写真でやるのが良いと思いました。
（おわり）