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

[二番煎じ感](http://d.hatena.ne.jp/sugyan/20160112/1452558576)が半端ないですが、やっちゃったものは仕方ないので書きます。

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

# 作ったもの
ec2上で稼働してます。ただしt2.microなので重いです。

ももクロ画像分類器
http://momomind.kenmaz.net

コード
https://github.com/kenmaz/momo_mind

![スクリーンショット 2016-05-23 3.02.48.png](https://qiita-image-store.s3.amazonaws.com/0/8399/469401df-3e50-de83-d386-48d38f92da5f.png)

# 背景
私（[@kenmaz](https://twitter.com/kenmaz))は、仕事ではここ数年はiOSアプリ(objc/swift)ばっかり書いています。その前はJava/Ruby/PHPでWebアプリ、サーバーアプリ、開発ツール等を書いていたりもしました。ということで機械学習や人工知能に関する知識はほぼゼロでした。

# 自分語り＆参考図書
そんな私がなぜ、題記のようなアプリを作ろうと思ったか、そのあたりを幾つかの本の紹介とともに書いていきます。

## 「人工知能は人間を超えるか」
昨年末に話題になった「人工知能は人間を超えるか」という本がありますが、これが大変おもしろかったので、実際に深層学習を学んで試してみたくなりました。
>https://www.amazon.co.jp/人工知能は人間を超えるか-角川ＥＰＵＢ選書-松尾-豊-ebook/dp/B00UAAK07S

シンギュラリティとか怖い！とかおもったけど、SFってことでいいんですよね？

## 「ITエンジニアのための機械学習理論入門」
まずは「ITエンジニアのための機械学習理論入門」を読みました。こちらも本屋には平積みされてて人気の本ぽいです。
>http://gihyo.jp/book/2015/978-4-7741-7698-7

機械学習の基礎から、最小二乗法、最尤推定法など関連する数学や統計についての解説までカバーしています。深層学習についてはそこまで言及されていませんが、パーセプトロン、ロジスティクス回帰による分類アルゴリズム、ベイズ推定あたりの話題は、機械学習を学んでいる感じがあって、手応えがありました。

## 「機械学習 はじめよう」
こちらはweb連載です。↑と同じ作者による連載かな？こちらも合わせて読みました。
>http://gihyo.jp/dev/serial/01/machine-learning

## 「物理のための数学」
で、ここで微分・積分ねえ、はいはい・・やったはずですけどねえ、、という状態になってしまったので、ちょっと寄り道して学習院大学の田崎晴明氏が無償で公開している
「物理のための数学」というドキュメントを読むことにしました。高校／大学１年レベルの数学の教科書としていいぞ、とどこかでオススメされていたものです。
>http://www.gakushuin.ac.jp/~881791/mathbook/

正月に帰省した際に印刷して読んでました。今考えると、まあ別に読まなくても問題なかったかな。。

## 「深層学習 (機械学習プロフェッショナルシリーズ)」
数学はもういいかな、と思い再度上記「機械学習理論入門」を読みなおしましたが、そろそろディープラーニングってやつをかじりたいな、と思い手にとったのが「深層学習 (機械学習プロフェッショナルシリーズ) 」でした。
>http://www.amazon.co.jp/深層学習-機械学習プロフェッショナルシリーズ-岡谷-貴之/dp/4061529021

タイトルどおり、こってり深層学習について学べます。ニューラルネットワーク、バックプロパゲーション、自己符号化、畳込みニューラルネット、再帰型ニューラルネット、などなど。。。自分は専門化ではありませんが、深層学習関連の重要なトピックが網羅されているような気がします。さらに一周目ではよくわからなかった箇所も、実際にコードを書いた後に読み直すと「あ〜、このよくわからなかった数式、さっき書いたあのコードの部分と関連してくるわけか〜」みたいな現象がよく起きるので、学んでる感があって良いです。

一番衝撃的だったのは「なぜ隠れ層を追加することで精度が向上するのか、研究者の間でも実はよくわかってないところがある」的な記述で、ロマンを感じました。

さて、
このあたりで、もう本を読むのはいいや、なんか書きたいぜ、と思い、ちょうどそのころ話題になってたTensorFlowを使って何か作りたいなと考え始めました。

こっからがようやく本題です（前置き長い）。

# 何を作るか

タイトルを見ただけでわかるかと思いますが、今回作ったものはすぎゃーん氏によるブログ記事の内容とほとんど同じものです。
>TensorFlowによるディープラーニングで、アイドルの顔を識別する
<http://d.hatena.ne.jp/sugyan/20160112/1452558576

さすがにまったく同じものを作るのはアレなので、同じような機能のものだとしても、せめて分析対象はももクロじゃなくて別のものにすべきかな・・と思ったんですが、重度のモノノフエンジニアの自分としては、すぎゃーん氏がやってることが楽しそうで仕方なかったので、結局同じネタですすめることにしました。二番煎じですいません。

# プログラム概要
４つのコンポーネントからなっています。

```
crawler
  webからももクロの画像を集めてくるRubyスクリプト

face_detect
  crawlerが集めてきた画像から顔部分を切り取るPythonスクリプト

deeplearning
  顔画像を訓練／テストデータとして学習を行うTensorFlow+Pythonスクリプト

web
  学習結果のモデルを使って顔認識機能をwebアプリとして提供するpythonスクリプト
```

Pythonはよくわからないので、当初はRubyで大部分を書こう！と思ってましたが、openCV（そして当然TensorFlowも）を扱うにはどう考えてもPythonのほうが有利だな、と思ったので途中からPythonをメイン言語として切替えました。crawlerだけRubyでほかは全部Pythonなのはそのせいです。素直にPython使いましょう。

## crawler

https://github.com/kenmaz/momo_mind/blob/master/crawler/url_fetch.rb
まずは`url_fetch.rb`で、ももクロメンバーの名前等をキーワードとしてbing画像検索を行い、画像URLを取得します。特になんてことは無いです。反省点としては、bing画像検索の検索結果は、ちょっと昔のももクロの画像しかヒットせず、最新の画像がなかなか集められないのが難点でした。メンバーのアメブロあたりから画像を引っ張ってきたほうがいいかなー、と途中で思いましたが、とりあえず現状はbing経由のみです。

https://github.com/kenmaz/momo_mind/blob/master/crawler/download.rb
url一覧が取得できたら、`download.rb`でガンガン画像をダウンロードします。夜実行して朝起きたら沢山溜まってました。

たまに同じURLが存在して画像が重複してしまうので、画像バイナリのハッシュ値を保存時のファイル名にするなどして、なるべく重複が発生しないように注意したりもしました。

以上、なんの変哲もないクローラーでした。

## face_detect
次は収集した画像から、学習用データとして、顔っぽい部分を抜き出します。
https://github.com/kenmaz/momo_mind/blob/master/face_detect/detect.py

`detect.py`では openCVが標準で提供している `haarcascades`というモデルを使って画像から顔部分だけを検出して、画像として切り取っていきます。ただしこのモデルを使って顔検出をしようとすると以下の様な問題が発生します。

- 顔がちょっとでも傾いていると顔として検出されない
- 顔じゃない画像が、顔として誤認識されてしまう

こちらの問題については、またしてもすぎゃーん氏によるブログポストを大いに参考にさせてもらいました。
>Heroku + OpenCVで簡易顔検出API
>http://d.hatena.ne.jp/sugyan/20151203/1449137219

だいたい上記と同じですが、処理の内容は以下のとおり。

- 画像自体を5度ずつ回転させて顔を検出 => 顔の傾きの問題を解決
- 同じ画像を回転させるだけなので、ひとつの顔に対して重複して顔が検出される => それぞれの顔画像に対して、目の位置の検出、口の位置の検出を追加で行い、一番それっぽい画像をピックアップする

目と口の検出に関しては、以下のとおり評価をしています。
- 画像の左上と右上にそれぞれ１つずつ目が存在する
- 左右の目の水平位置がほぼ同じ
- 画像の中央下部に１つ口が存在する

-50度から50度まで5度刻みで画像を回転させつつ、上記処理を繰り返すためだいぶ重い処理になります。

このスクリプトは学習データの生成だけではなく、最終的にwebアプリとして公開する際に、ユーザからアップロードされた画像から、顔検出する際にも使います。このまま処理を流用するのはさすがに重すぎなので、webアプリとして使うときは、-5度、0度、5度、の角度だけを使うようにしました。なので顔の検出に失敗することも多々有りますが貧乏やむなし。

回転した画像同士で、同一の顔を重複してしまう問題については、各顔の座標を回転移動すれば重複した顔画像をグルーピングできます。高校の時やりましたねー。

>回転移動の１次変換
>http://www.geisya.or.jp/~mwm48961/kou2/linear_image3.html

## 人力による訓練・テストデータの準備
これでようやくももクロメンバーの顔画像が大量に集まりましたが、これらを学習用データとして使えるようにするには、それぞれの顔がももクロの誰の顔なのかを学習データとして提供して上げる必要があります。今からまさにその分類を自動で行うプログラムを作ろうとしてるわけですが、卵と鶏問題ってことで、まず人間様が最初の学習用データを作ってやる必要があります。

すぎゃーん氏は学習データ作成のためのwebツールを作っていたようですが、私はめんどくさかったのでとりあえずMacのFinderで画像をばーっと並べて、cmd+クリックで画像を複数選択→フォルダ作成、みたいな感じので作業で済ますことにしました。

フォルダ構成はこんな感じ。

```
/out
  /train
    /reni
      ..150枚ほどの画像..
    /kanako
      ..150枚ほどの画像..
    /shiori
      ..150枚ほどの画像..
    /arin
      ..150枚ほどの画像..
    /momoka
      ..150枚ほどの画像..
  test
    /reni
      ..150枚ほどの画像..
    /kanako
      ..150枚ほどの画像..
    /shiori
      ..150枚ほどの画像..
    /arin
      ..150枚ほどの画像..
    /momoka
      ..150枚ほどの画像..
```
訓練とテストの画像はどれくらいの配分がいいか迷いましたが、それぞれ150枚くらい用意しました。最初は訓練データを充実させたほうがいいっしょ！ってことで、訓練：テスト = 7:3 くらいの配分にしてみましたが、そうすると感覚的にテストデータの選び方次第ではテストデータが偏ってしまうことがあったためです。

計1,500枚の画像をがんばって人力分類したわけですが、これがももクロメンバーの顔画像でほんとに良かった、とおもいます。嫁さんはSMAPのメンバーとかでやってみたら？と言ってましたが、1500枚のおじさんの顔画像をがんばって分類する作業とか、ほんといやですよね。ほんとももクロで良かった。

![スクリーンショット 2016-05-23 2.42.41.png](https://qiita-image-store.s3.amazonaws.com/0/8399/5598a2d7-55ea-d5c5-9015-bfcbd36618cc.png)
*kanakoフォルダの様子*

# 前編おわり
前編はここまで。誰も望んでいないかもしれませんが、後編もきっと書きます。
*技術に関係ないことばっかり書いちゃったので、検閲が厳しいと噂のQiita運営にBANされないか不安である*

続きはこちら
中編： http://qiita.com/kenmaz/items/ef0a1308582fe0fc5ea1#_reference-6ae9a51ee7a7a346d3c1