初めてのDeep Learning 〜準備編〜

はじめまして。@best_not_bestです。
先日、社内勉強会で[Chainer](https://research.preferred.jp/2015/06/deep-learning-chainer/)の話をさせていただいた所、意外と良い反応が返ってきましたので、詳細をこの記事にまとめたいと思います。

## やりたいこと
皆さん、好きな芸能人の1人や2人いますよね？（いる前提で話します。）でも、その方に直接お会い出来る可能性はきっと低いのではないのでしょうか。身近に似ている人がいれば・・・、そしてその方とお知り合いになれれば・・・。

## 注意
以下で社内のサイトをスクレイピングしていますが、本記事はその行為を推奨するものではありません。あくまでネタとしてお読みください。
本記事を実際に行った場合の如何なる被害について、一切の責任を負いかねます。
各自の社内の情報セキュリティルールを守り、[はたらくを楽しみましょう](http://www.inte.co.jp/)。

## 環境
- MacBook Pro 15-inch
- OS X Yosemite 10.10.5
- Python 2.7.9
 - chainer 1.3.0
 - lxml 3.4.4
 - selenium 2.47.1
 - numpy 1.9.2

## 手順
1. 社員の画像を集める
2. 集めた社員の画像の顔部分を切り抜く
3. 学習用画像（好きな芸能人）を集める
4. 学習用画像の顔部分を切り抜く
5. Python + Chainerで4.を学習させ、判別器を作成
6. 2.の画像を判別器で判別させる

## 実践

### 1. 社員の画像を集める

皆さんの会社のイントラサイトには社員検索機能があるかと思います。そこから適当な社員を検索し、社員画像のURLを調べましょう。`http://hogehoge.co.jp/image/12345.jpg`のようにURLに社員IDが入っていればしめたものです。
会社によってはIDがMD5等でハッシュ化されているかもしれません。とにかく、社員IDと画像URLの関連性を見つけてください。（見つけられなかった場合は諦めましょう・・・。）

次に社員IDの一覧が取れないか探します。検索フォームに何も入力しないで検索ボタンを押したら一覧が出るかもしれません。その一覧ページに対してスクレイピングを行います。

```py:abstraction_id.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import lxml.html
from selenium import webdriver

TARGET_URL = 'http://hogehoge.co.jp/list.html'
driver = webdriver.PhantomJS()
driver.get(TARGET_URL)
root = lxml.html.fromstring(driver.page_source)
links = root.cssselect('p.class')
for link in links:
    if link.text is None:
        continue
    if link.text.isdigit():
        print link.text
```

以下のコマンドで実行します。

```
$ python abstraction_id.py > member_id.txt
```

`target_url = 'http://hogehoge.co.jp/list.html'`の部分はローカルにあるファイルパスでも構いませんので、ページを保存してからのスクレイピングも可能です。
`root.cssselect()`には社員IDが記載されているHTMLの要素名を記載ください。
今回はHTMLの複数の箇所で該当の要素がありましたので、条件判定を行っています。
これは社員IDが数字のみの場合に判定されますが、適宜正規表現に置き換えてください。

取得したID一覧を用いて画像をローカルに取得します。

```py:image_crawler.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

from urllib2 import Request, urlopen, URLError, build_opener
import os
import time

ID_LIST = './member_id.txt'
URL_FMT = 'http://hogehoge.co.jp/image/%s.jpg'
OUTPUT_FMT = './photos/%s.jpg'
opener = build_opener()

for id in open(ID_LIST, 'r'):
    url = URL_FMT % id.strip()
    output = OUTPUT_FMT % id.strip()

    req = Request(url)
    try:
        response = urlopen(req)
    except URLError, e:
        if hasattr(e, 'reason'):
            err = e.reason
        elif hasattr(e, 'code'):
            err = e.code
    else:
      file = open(output, 'wb')
      file.write(opener.open(req).read())
      file.close()

    time.sleep(0.1)
```

以下のコマンドで実行します。

```
$ python image_crawler.py
```

念のため`time.sleep()`は入れておきましょう。
`OUTPUT_FMT`が格納先ディレクトリになりますので、適宜選択ください。

### 2. 集めた社員の画像の顔部分を切り抜く

OpenCVを使って切り抜いていきます。以下の記事を参考にさせていただきました。
[py-opencv 画像の一部を切り抜いて保存する - Symfoware](http://symfoware.blog68.fc2.com/blog-entry-1524.html)

```py:cutout_face.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import numpy
import os
import cv2

CASCADE_PATH = '/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml'
INPUT_DIR_PATH = './photos/'
OUTPUT_DIR_PATH = './cutout/'
OUTPUT_FILE_FMT = '%s%s_%d%s'
COLOR = (255, 255, 255)

files = os.listdir(INPUT_DIR_PATH)
for file in files:
    input_image_path = INPUT_DIR_PATH + file

    # ファイル読み込み
    image = cv2.imread(input_image_path)
    # グレースケール変換
    try:
        image_gray = cv2.cvtColor(image, cv2.cv.CV_BGR2GRAY)
    except cv2.error:
        continue

    # カスケード分類器の特徴量を取得する
    cascade = cv2.CascadeClassifier(CASCADE_PATH)

    # 物体認識（顔認識）の実行
    facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))

    if len(facerect) > 0:
        # 認識結果の保存
        i = 1
        for rect in facerect:
            print rect
            x = rect[0]
            y = rect[1]
            w = rect[2]
            h = rect[3]

            path, ext = os.path.splitext(os.path.basename(file))
            output_image_path = OUTPUT_FILE_FMT % (OUTPUT_DIR_PATH, path, i, ext)
            cv2.imwrite(output_image_path, image[y:y+h, x:x+w])

            i += 1
```

以下のコマンドで実行します。

```
$ python cutout_face.py
```

`INPUT_DIR_PATH`が前項の格納先ディレクトリ、`OUTPUT_DIR_PATH`が切り出したファイルの格納先ディレクトリになりますので、適宜選択ください。
`ImportError: No module named cv2`とエラーが出た場合は

```
import cv2
```

を

```
import sys
sys.path.append('/usr/local/lib/python2.7/site-packages')
import cv2
```

と書き換えることで回避出来るかと思います。

ほとんどの画像で顔部分の切り抜きが出来るかと思いますが、中には以下のようにネクタイ部分を顔と認識してしまう場合もあります。今後の課題です。
![110001_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/17452/67c8a934-b41a-af8c-07f1-641f8b468afa.jpeg)

## 次回
今回はここまでとなります。（中途半端ですいません・・・）
[インテリジェンス Advent Calendar 2015](http://qiita.com/advent-calendar/2015/intelligence)の21日目の記事に続きます！

## 追記

解決しました！
→ [初めてのDeep Learning 〜解決編〜 - Qiita](http://qiita.com/best_not_best/items/7946f55a4978ac1a8b2e)