Tensorflowを再学習させて「〇〇判別機」を作る

Tensorflowを使って簡単な「〇〇判別機」を作ってみます。イメージとしては、例えば犬を「犬」と認識するのではなく、トイプードルなのか、柴犬なのかを判別できるものを作っていきます。
なお、環境としてPython3系を前提としています。Pythonのバージョンは
`python --version`
で確認してください。わからないという方はお気軽にご質問ください。

#1. Tensorflowを使う環境を作る
まずはTensorflowを使う環境を整えましょう。トールさんの記事を参考にします。すでにTensorflowを導入している方は次に進みましょう！

>(初心者向け)TensorFlow用に環境を確認する
https://qiita.com/egplnt/items/36a1d7dcf92bf85ffe83
(初心者向け)TensorFlowをインストールする
https://qiita.com/egplnt/items/c5055f821a4770edbf8a#_reference-e1f18ab33011fa0a4454

#2. Tensorflowの再学習チュートリアルを使うための準備
ターミナルで以下のように打ってください。これであとあと必要なファイルをPCにダウンロードします。
ダウンロードが終わるとホームディレクトリにtensorflowというフォルダができていると思います。

```
git clone https://github.com/tensorflow/tensorflow.git
```
少し時間がかかると思うので、ダウンロードしている間に次に進みましょう。

#3. どんなものを作るかを決める
「何」判別機を作るかを決めましょう。私はうどん好きなので、かけ・釜揚げ・しょうゆ・ざるの４種類のうどんを判別できる「うどん判別機」を作ろうと思います。
自分が好きなもので、４種類くらいに分けられるものを自由に選んでみましょう。違いが大きいほど正確に判別できます。
また、最後にテストをする際に使う画像として、ご自身で撮影された写真があるとより良いです。

（例）
犬…柴犬、トイプードル、ブルドッグ、ダックスフンド
クワガタ…コクワガタ、ノコギリクワガタ、オオクワガタ、ミヤマクワガタ
スパゲッティ…ボンゴレビアンコ、ボロネーゼ、アラビアータ、カルボナーラ
などなど。お腹がすいてきましたね！

#4. Pythonに画像を集めてもらう
判別機を作るには、学習用の画像データが必要です。Google画像検索から一つずつダウンロードしていくと大変なので、Pythonを使って「スクレイピング」（画像を集めてもらう）をしてみましょう。

※このあたりからターミナルで色々打つようになります。うまくいかない場合はpythonのバージョンが違っていたり、必要なモジュールが入っていないことが多いです。エラーでno module named 〇〇のように出てしまったら、「Mac Python3 〇〇インストール」などでググると出てきたりしますが、不安な方はお気軽にご質問ください！

1. beautifulsoup4をインストール

ターミナルを開き、```pip install bs4```でbeautifulsoup4をインストールします。
※うまくいかない方は`pip3 install bs4`

2. スクレイピングプログラムをつくる

Atomを開き、以下のコードをコピー＆ペーストしてください。

```py:get_images.py
# coding: utf-8

import os
import sys
import traceback
from mimetypes import guess_extension
from time import time, sleep
from urllib.request import urlopen, Request
from urllib.parse import quote
from bs4 import BeautifulSoup

MY_EMAIL_ADDR = ''

class Fetcher:
    def __init__(self, ua=''):
        self.ua = ua

    def fetch(self, url):
        req = Request(url, headers={'User-Agent': self.ua})
        try:
            with urlopen(req, timeout=3) as p:
                b_content = p.read()
                mime = p.getheader('Content-Type')
        except:
            sys.stderr.write('Error in fetching {}\n'.format(url))
            sys.stderr.write(traceback.format_exc())
            return None, None
        return b_content, mime

fetcher = Fetcher(MY_EMAIL_ADDR)

def fetch_and_save_img(word):
    data_dir = 'data/'
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)

    for i, img_url in enumerate(img_url_list(word)):
        sleep(0.1)
        img, mime = fetcher.fetch(img_url)
        if not mime or not img:
            continue
        ext = guess_extension(mime.split(';')[0])
        if ext in ('.jpe', '.jpeg'):
            ext = '.jpg'
        if not ext:
            continue
        result_file = os.path.join(data_dir, str(i) + ext)
        with open(result_file, mode='wb') as f:
            f.write(img)
        print('fetched', img_url)


def img_url_list(word):
    """
    using yahoo (this script can't use at google)
    """
    url = 'http://image.search.yahoo.co.jp/search?n=60&p={}&search.x=1'.format(quote(word))
    byte_content, _ = fetcher.fetch(url)
    structured_page = BeautifulSoup(byte_content.decode('UTF-8'), 'html.parser')
    img_link_elems = structured_page.find_all('a', attrs={'target': 'imagewin'})
    img_urls = [e.get('href') for e in img_link_elems if e.get('href').startswith('http')]
    img_urls = list(set(img_urls))
    return img_urls

if __name__ == '__main__':
    word = sys.argv[1]
    fetch_and_save_img(word)
    os.rename('data/', str(word) + '/')
```

get_images.pyという名前をつけて保存しましょう。保存場所はどこでもいいですが、今回はデスクトップにフォルダgakusyu_dataを作り、そこに保存しましょう。
　
3. 画像をダウンロードする
ターミナルでget_images.pyを実行してみましょう。

```cd Desktop/gakusyu_data/
python get_images.py 検索したいワード```
　
get_images.pyの入っているフォルダに、検索したワードと同じ名前のフォルダが生成され、６０枚の画像がダウンロードされているはずです。
以上の作業を何回か繰り返し、必要なデータを集めます。
最終的にはこのようになるでしょう。
<img width="452" alt="32a936cd42a64c5d98e85e53db46b141.png" src="https://qiita-image-store.s3.amazonaws.com/0/206583/f525dc52-0131-7a96-5f5f-41a38c4f6ad5.png">

　　
4. データの選別をする
残念ながらget_images.pyが集めてきてくれたものの中にはあまりふさわしくないデータも含まれています。
例えば、以下の画像かけうどんというより肉の塊です。
<img width="803" alt="91a39fd1383da65b20dc2bceb8e57db5.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/206583/95024f98-f1d1-c0bf-a2b5-3f62c5e681ce.jpeg">


こういったものや、違うものが大きく写り込んでいるもの、複数の何かがごちゃごちゃ写っているものなどは除いておきましょう。この一手間で画像認識の精度が大きく変わります。

#5. 集めたデータを学習させる
それではいよいよ集めたデータでモデルを作っていきます。
まずはFinderから tensorflow/tensorflow/examples/image_retraining に移動します。
そこにgakusyu_dataフォルダをコピペします。
<img width="615" alt="45f268cc678370f280ae85700babbbc3.png" src="https://qiita-image-store.s3.amazonaws.com/0/206583/e54037a0-f3cf-90a5-36a5-2c1d3a3d5f6c.png">

上のようになっていれば大丈夫です。

また、フォルダ名が日本語だとうまくいかないので下のように英語にしておきましょう。
<img width="436" alt="8b2098ffb23a1ca815aab818322d7e48-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/206583/ebcfc54c-e9d8-cd09-8470-20af16875a91.png">

次にターミナルを開き、以下を実行します。


```
cd tensorflow/tensorflow/examples/image_retraining
sudo python retrain.py \
  --bottleneck_dir=bottlenecks \
  --how_many_training_steps=500 \
  --model_dir=inception \
  --summaries_dir=training_summaries/basic \
  --output_graph=retrained_graph.pb \
  --output_labels=retrained_labels.txt \
  --image_dir=gakusyu_data
```
しばらくすれば学習は終了です。これで判別機が完成しました！

#6 実際に判別機を使ってみる
いよいよ完成した判別機を使ってみましょう。
tensorflow/tensorflow/examples/image_retraining に、判別させたい画像を移動します。
ここでは01.jpgという名前にしておきます。画像の拡張子はpngなどでも大丈夫です。
<img width="487" alt="a534699fb16dc0f2847c5c2bf82930a7.png" src="https://qiita-image-store.s3.amazonaws.com/0/206583/6ffaf83b-fe45-fd4f-5c1b-18264df6b51b.png">
（ちなみにこれは新宿にある「慎」という筆者オススメのうどん屋さんの醤油うどんです）

tensorflow/tensorflow/examples/label_image　にある
label_image.pyを
tensorflow/tensorflow/examples/image_retraining
に移動しておきます。
```python label_image.py --image 01.jpg --graph retrained_graph.pb --labels retrained_labels.txt ```
すると、どの種類にどれくらい近いのかをscore(パーセント）で表してくれます。
<img width="190" alt="8cba14ef68b31fc8b9286604ba926b1d.png" src="https://qiita-image-store.s3.amazonaws.com/0/206583/b2d79fbc-9dca-7634-4918-3e6d492f77c3.png">
みごと、醤油うどんだとわかってもらえました！

※上のコマンドでKeyErrorが出るという方は、label_image.pyの78,79行目を
```input_layer = "Mul"
output_layer = "final_result"
```
に変更してみましょう。

参考： https://github.com/tensorflow/tensorflow/issues/12736

これで判別機の作成は終了です。お疲れ様でした！

#7. さらに＋α
ここまできてさらに余裕があるという方は、以下にチャレンジしてみましょう。
・より多くの種類を判別できるようにする
・判別の精度をさらに上げる
・もっと多くのデータを集めてみる
などなど

このチュートリアルだけでなく、インターネットで色々調べることでいい方法を見つけられるかもしれません。