ディープラーニングでザッカーバーグの顔を識別するAIを作る④（WEB構築編）

第1部：[ディープラーニングでザッカーバーグの顔を識別するAIを作る①（学習データ準備編）](http://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)
第2部：[ディープラーニングでザッカーバーグの顔を識別するAIを作る②（AIモデル構築編）](http://qiita.com/AkiyoshiOkano/items/959006f9298df72a841a)
第3部：[ディープラーニングでザッカーバーグの顔を識別するAIを作る③（データ学習編）](http://qiita.com/AkiyoshiOkano/items/9d49570b19e8a397e9b4)

この記事は第4部です。作るものは「ザッカーバーグの顔を識別するAI」です。GoogleのディープラーニングライブラリのTensorFlowを使います。今回作ったもののサンプル動画等は[こちら](http://akikiyo.com/zuck-detect-ai)。

![スクリーンショット 2017-05-07 12.33.41.png](https://qiita-image-store.s3.amazonaws.com/0/65436/086019d1-0055-9e09-6163-70ff603f9043.png)
![zgif2.gif](https://qiita-image-store.s3.amazonaws.com/0/65436/f6c94c68-a236-4262-ce27-e38b06229069.gif)

今までの第1~3部の中で、集めた顔データをTensorFlowを用いて学習してもらったので、今回はその学習結果を用いて実際に任意の画像の顔を抽出し、抽出した顔がどれくらい学習した顔に近いかを判定できるようにしたいと思います。そして最後の仕上げに、PythonのWEBアプリケーションフレームワークのFlaskを使い、WEBのインターフェスから一連の識別を実行できるようにしていきます。最後までやると、「お〜〜、これはAIっぽい！」となってくるかと思います。僕はなりましたw

### まずは作業ディレクトリ構造を考える(準備)
後々のWEB連携のことも考え、ディレクトリ構成は下記のような感じになりました。

```txt:ディレクトリ構造
/tensoflow
  main.py
  model.ckpt(学習結果が保存されたファイル)
  eval.py(任意の画像の判例結果を返す処理を記載)
  web.py(Flaskを用いてWEB関連処理を記載)
  /opencv(opencv公式githubからクローンしてきた)
  /static(Flaskで使う画像を置いておく)
    /images
      /default
      /cut_dace
      /face_detect
  /data
    /train
      /zuckerbuerg
      /elonmusk
      /billgates
      data.txt
    /test
      /zuckerbuerg
      /elonmusk
      /billgates
      data.txt
 後はtensorflowをインストール時にできたファイル等
```
今回、``eval.py``と``web.py``というファイルを主に編集しておきます。入力画像の顔認識部分や顔を切り取った画像の保存は後々のWEB構築の時に備えて、``/static/images``というディレクトリを作り、そこに入力された画像や加工した画像を保存していきます。顔部分の認識や切り抜きには[第1部](http://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)で使ったOpenCVも使っていくので、[OpenCV](https://github.com/opencv/opencv)からクローンしてきたフォルダも配置しました。

#④学習結果を用いて任意の画像を判定できるようにする

前回の第3部で実際にTensorFlowを用いて顔データを学習してもらったので、新たに``main.py``の最後の``save_path = saver.save(sess, "model.ckpt")``の1行で指定している``model.ckpt``ファイルが出来上がっていると思います。こちらファイルにデータ学習後の調整されたパラメーターなどが保存されているので、こちらの``model.ckpt``ファイルを用いることで、実際にTensorFlowが学習したAIモデルを簡単に使用することができます。

それでは、まずは任意の画像から顔が判定できるようにTensorFlow関連の処理を書いていきましょう。
([TensorFlowによるももクロメンバー顔認識（後編）](http://qiita.com/kenmaz/items/8a9c382af3f8397710e9)の記事を参考にさせて頂きました。ありがとうございますm(_ _)m)

```python:eval.py
#!/usr/bin/env python
#! -*- coding: utf-8 -*-

import sys
import numpy as np
import cv2
import tensorflow as tf
import os
import random
import main

# OpenCVのデフォルトの顔の分類器のpath
cascade_path = './opencv/data/haarcascades/haarcascade_frontalface_default.xml'
faceCascade = cv2.CascadeClassifier(cascade_path)

# 識別ラベルと各ラベル番号に対応する名前
HUMAN_NAMES = {
  0: u"ザッカーバーグ",
  1: u"イローンマスク",
  2: u"ビルゲイツ"
}

#指定した画像(img_path)を学習結果(ckpt_path)を用いて判定する
def evaluation(img_path, ckpt_path):
  # GraphのReset(らしいが、何をしているのかよくわかっていない…)
  tf.reset_default_graph()
  # 画像を開く
  f = open(img_path, 'r')
  # 画像読み込み
  img = cv2.imread(img_path, cv2.IMREAD_COLOR)
  # モノクロ画像に変換
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  face = faceCascade.detectMultiScale(gray, 1.1, 3)
  if len(face) > 0:
    for rect in face:
      # 加工した画像に何でもいいので適当な名前をつけたかった。日付秒数とかでいいかも
      random_str = str(random.random())
      # 顔部分を赤線で書こう
      cv2.rectangle(img, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0, 0, 255), thickness=2)
      # 顔部分を赤線で囲った画像の保存先
      face_detect_img_path = './static/images/face_detect/' + random_str + '.jpg'
      # 顔部分を赤線で囲った画像の保存
      cv2.imwrite(face_detect_img_path, img)
      x = rect[0]
      y = rect[1]
      w = rect[2]
      h = rect[3]
      # 検出した顔を切り抜いた画像を保存
      cv2.imwrite('./static/images/cut_face/' + random_str + '.jpg', img[y:y+h, x:x+w])
      # TensorFlowへ渡す切り抜いた顔画像
      target_image_path = './static/images/cut_face/' + random_str + '.jpg'
  else:
    # 顔が見つからなければ処理終了
    print 'image:NoFace'
    return
  f.close()

  f = open(target_image_path, 'r')
  # データを入れる配列
  image = []
  # 画像読み込み
  img = cv2.imread(target_image_path)
  # 28px*28pxにリサイズ
  img = cv2.resize(img, (28, 28))
  # 画像情報を一列にした後、0-1のfloat値にする
  image.append(img.flatten().astype(np.float32)/255.0)
  # numpy形式に変換し、TensorFlowで処理できるようにする
  image = np.asarray(image)
  # 入力画像に対して、各ラベルの確率を出力して返す(main.pyより呼び出し)
  logits = main.inference(image, 1.0)
  # We can just use 'c.eval()' without passing 'sess'
  sess = tf.InteractiveSession()
  # restore(パラメーター読み込み)の準備
  saver = tf.train.Saver()
  # 変数の初期化
  sess.run(tf.initialize_all_variables())
  if ckpt_path:
    # 学習後のパラメーターの読み込み
    saver.restore(sess, ckpt_path)
  # sess.run(logits)と同じ
  softmax = logits.eval()
  # 判定結果
  result = softmax[0]
  # 判定結果を%にして四捨五入
  rates = [round(n * 100.0, 1) for n in result]
  humans = []
  # ラベル番号、名前、パーセンテージのHashを作成
  for index, rate in enumerate(rates):
    name = HUMAN_NAMES[index]
    humans.append({
      'label': index,
      'name': name,
      'rate': rate
    })
  # パーセンテージの高い順にソート
  rank = sorted(humans, key=lambda x: x['rate'], reverse=True)

  # 判定結果と加工した画像のpathを返す
  return [rank, face_detect_img_path, target_image_path]

# コマンドラインからのテスト用
if __name__ == '__main__':
  evaluation('testimage.jpg', './model2.ckpt')

```

上記の処理で**[判定結果のパーセンテージ、顔部分を赤線で囲った画像、顔部分を切り抜いた画像]**という配列が返されると思います。後はこの、①画像をアップロード→②顔を切り抜いてAIが判定→③判定結果を出力という一連の作業をWEBインターフェースでできるようにするだけです。

TensorFlowを用いたディープラーニングはここで終わりなので、以下はWEBの構築となっていきます。

#⑤画像の判定をWEBで実行できるようにする

今回はpython用のWEBアプリケーションフレームワークFlaskを使っていきます。Flaskは非常に簡単にPythonでWEBアプリケーションが作れるフレームワークです。今回はFlaskの使い方は省略します。
（参考:[ウェブアプリケーションフレームワーク Flask を使ってみる](http://qiita.com/ynakayama/items/2cc0b1d3cf1a2da612e4)）

本当に簡単に「画像をアップロード→eval.pyに画像を渡す→結果を表示」というシンプルなものです。

```python:web.py
# -*- coding: utf-8 -*-

import tensorflow as tf
import multiprocessing as mp

from flask import Flask, render_template, request, redirect, url_for
import numpy as np
from werkzeug import secure_filename
import os
import eval

# 自身の名称を app という名前でインスタンス化する
app = Flask(__name__)
app.config['DEBUG'] = True
# 投稿画像の保存先
UPLOAD_FOLDER = './static/images/default'

# ルーティング。/にアクセス時
@app.route('/')
def index():
  return render_template('index.html')

# 画像投稿時のアクション
@app.route('/post', methods=['GET','POST'])
def post():
  if request.method == 'POST':
    if not request.files['file'].filename == u'':
      # アップロードされたファイルを保存
      f = request.files['file']
      img_path = os.path.join(UPLOAD_FOLDER, secure_filename(f.filename))
      f.save(img_path)
      # eval.pyへアップロードされた画像を渡す
      result = eval.evaluation(img_path, './model2.ckpt')
    else:
      result = []
    return render_template('index.html', result=result)
  else:
  	# エラーなどでリダイレクトしたい場合
  	return redirect(url_for('index'))

if __name__ == '__main__':
  app.debug = True
  app.run(host='0.0.0.0')
```

ViewとCSSファイルはこちら
<**index.html**>メインのビュー
https://github.com/AkiyoshiOkano/zuckerberg-detect-ai/blob/First_commit/templates/index.html
<**layout.html**>共通レイアウト部分
https://github.com/AkiyoshiOkano/zuckerberg-detect-ai/blob/First_commit/templates/layout.html
<**style.css**>CSSファイル
https://github.com/AkiyoshiOkano/zuckerberg-detect-ai/blob/First_commit/static/style.css

最終的なディレクトリ構造はこんな感じ。

```txt:ディレクトリ構造
/tensoflow
  main.py(tensorflow学習モデルと訓練部分)
  model.ckpt(学習結果が保存されたファイル)
  eval.py(任意の画像の判例結果を返す処理を記載)
  web.py(Flaskを用いてWEB関連処理を記載)
  /opencv(opencv公式githubからクローンしてきた)
  /static(Flaskで使う画像やcssを配置)
    /images
      /default(アップした画像)
      /cut_dace(顔を切り抜いた画像)
      /face_detect(顔を赤線で囲った画像)
    style.css
  /templates(FlaskのView部分)
    index.html
    layout.html
  /data
    /train
      /zuckerbuerg
      /elonmusk
      /billgates
      data.txt
    /test
      /zuckerbuerg
      /elonmusk
      /billgates
      data.txt
 後はtensorflowをインストール時にできたファイル等
```
TensorFlowはHerokuで動かせるらしいので、時間がある時にやってみようかと思います。

以上で、TensorFLowを使ったザッカーバーグ検出器の作成手順は終了です!参考にさせて頂いた方々ありがとうございました。僕の記事も誰かの参考になれば幸いですm(_ _)m
![zgif2.gif](https://qiita-image-store.s3.amazonaws.com/0/65436/22398699-372c-f9ca-598f-4ff5b23d5736.gif)

#開発後書き

今回初めての機械学習(ディプラーニング)でしたが、書籍等は一切使わずにWEB上の情報だけである程度形にでき、かつ、そこそこディープラーニングを理解できたのでネット上の情報は偉大だなと思いました。ただ、今回触れたのは「機械学習(ディープラーニング)の本当に最初の1歩目だろうな〜」とは思います。（「未経験者」から「初心者」にはなれたくらいだろうか。。）

実際に学習させたAIへの考察ですが、**女性の画像はかなりの確率でビルゲイツ氏かイーロンマスク氏が95%以上**になり、これは今回の学習させたデータが全員男性の顔なので、女性の顔が渡されるとうまく判定できないけど、とりあえず各ラベルの確率を合わせて100%にして出力しないといけないからこういう結果になるのではないかなーと推測しました。難しい。

![スクリーンショット 2017-05-07 1.05.31.png](https://qiita-image-store.s3.amazonaws.com/0/65436/dc618611-4578-9c69-2eec-6efd1585bdc6.png)

ただ、顔データを学習させた**ザッカーバーグ、イローンマスク、ビルゲイツの3人はほぼ外すことなく識別する**ので、ディープラーニングの精度ってすげーと結構驚きました。「おお、ホントに当ててくるぞw」みたいな。 (イローンマスクだけたまに外すかも？)

![zuck5.png](https://qiita-image-store.s3.amazonaws.com/0/65436/2737987a-711a-63f9-b5f1-29e2cac8f240.png)
![elon5.png](https://qiita-image-store.s3.amazonaws.com/0/65436/0c4da99c-af5f-78a3-325a-698eff3dce90.png)
![gates3.png](https://qiita-image-store.s3.amazonaws.com/0/65436/316d29f2-73a7-a79a-7556-054e6211418f.png)

後はディープラーニングの裏側で起きている複雑な数式や微分積分などをあんまりわかってなくても、とりあえず組み込み関数で動かすことができるという、TensorFlowの便利さにも感動しました。もっと勉強したらできることの幅が大きく広がりそうなので、今回を機に機械情報の情報もキャッチアップできるように頑張りたいと思います。初めてのディープラーニング体験で至らない点も多かったと思いますが、最後まで読んでくれた方がいましたらありがとうございました。（間違って解釈している点などはご指摘頂けると幸いですm(_ _)m）

2017年の**GWの自由研究**という名目で開発ロードムービーまで作ってしまったので、もし時間があったら見てみてください（笑）。色んな人物の画像で判定した判定結果なども見れます。開発ムービーは[こちら](https://www.youtube.com/watch?v=42x-WatWRZ8&t=2s)より。

**「ディープラーニングでザッカーバーグの顔を識別するAIを作る」<記事全編>**
第1部：[ディープラーニングでザッカーバーグの顔を識別するAIを作る①（学習データ準備編）](http://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)
第2部：[ディープラーニングでザッカーバーグの顔を識別するAIを作る②（AIモデル構築編）](http://qiita.com/AkiyoshiOkano/items/959006f9298df72a841a)
第3部：[ディープラーニングでザッカーバーグの顔を識別するAIを作る③（データ学習編）](http://qiita.com/AkiyoshiOkano/items/9d49570b19e8a397e9b4)
第4部：[ディープラーニングでザッカーバーグの顔を識別するAIを作る④（WEB構築編）](http://qiita.com/AkiyoshiOkano/items/dc19bffc88c549393699)

GitHub:https://github.com/AkiyoshiOkano/zuckerberg-detect-ai