FindYourCandyで転移学習を使ったのでそれ関連の話

これは6/14に開催される[Google Cloud Community fes](https://www.meetup.com/GDG-Cloud-Tokyo/events/240052509/)で話す資料のチラ出しです。チラ出しって言うか、だいたい全部です。

## そもそもFindYourCandyって何よ？

こんなやつです。

![fyc.png](https://qiita-image-store.s3.amazonaws.com/0/1046/fd57ab13-9f46-1694-ea25-a53002455acd.png)


ロボットアームと機械学習を組み合わせたデモでして、色々なところで露出させていただいていますので、FindYourCandy自体はここでは説明しません。適宜資料をご参考ください。

- Google Cloud Next'17の[動画](https://youtu.be/3zV8rQb-Rt0?t=870)
- Google I/O'17の[動画](https://youtu.be/UyU1k6eebp4?t=159)
- ブレインパッド社による[動画](https://www.youtube.com/watch?v=-oaleXJxn7Q)
- TFUG\#5で発表された技術部分の説明は[こちら](長南さんの資料)
- 文字ベースでもう少し細かい説明を知りたい場合は[こちら](マイナビ連載)
- 中身をコードで直接見たい方は[こちら](github)

FindYourCandyは要素的には色々組み合わせたものですが、今回は特に「転移学習」についてフォーカスした話題となります。

## TFUG\#5での発表でFindYourCandyの裏側の話
![chonan1.png](https://qiita-image-store.s3.amazonaws.com/0/1046/d8a6c46c-b575-cae3-20af-dbe591c0c8d1.png)


一緒に開発した方に主に技術部分にフォーカスして[発表してもらいました](https://sho-soar.github.io/slides/tfug5/#/)。その中で、以下のようなスライドが出てきます。

<img width="773" alt="chonan2.png" src="https://qiita-image-store.s3.amazonaws.com/0/1046/290b3a11-c931-812f-1fa7-9164c2d8b27a.png">


「Inception-V3の転移学習」このスライドで何を言わんとしているか分かった方は今回の対象読者ではないので、ブラウザのタブをすっと閉じてください。転移学習って聞いたことがあるけどなんだろう、転移学習は分かるけどどう使っているのか想像がつかないのでもう少し詳しく知りたい、そういう方は読み進めてください。

## 画像認識をやりたい（それも深層学習で）

って思ったらどうしますか？しかも深層学習で。深層学習で画像といえばとりあえずCNNっぽい風潮がありますよね。CNNそのものの話はそこら中で見つかると思いますので、CNN自体の説明は割愛します。

![cnn.png](https://qiita-image-store.s3.amazonaws.com/0/1046/3bd86fea-b39c-51d4-8ae9-b8f36adad7a5.png)

出展：[What i s visual recognition?](https://www.clarifai.com/technology)より

もちろんTensorFlowでも実現でき、特にv1.0以降ではLayersという新しいAPI層ができて、CNNの各層を簡単につくれるようになりました。[チュートリアル](https://www.tensorflow.org/tutorials/layers)を見ると雰囲気が分かるでしょう。ですが、Kerasを使うとさらに簡単に実現できます。とりあえず試す時に簡単なのは正義ですね。ここからはKerasを使う前提で話を進めます。

## Kerasって何よ？
[Keras](https://keras.io/)は、TensorFlow（とTheano）をバックエンドに持つことができるディープラーニングのライブラリです。TensorFlowがかなりローレベルなAPIを提供しているため、いわゆるデータサイエンティストにとってはやや使いづらい、という印象が強く、高レベルなAPIが求められていました。TensorFlowの高レベルAPIをめぐる話としてはそれはそれで色々ありますが、最終的にTensorFlow自体が高レベルAPI自体を提供することと、KerasがTensorFlowと統合されることが発表されました。この辺りの詳細は本日の主題ではないので、TensorFlow Dev Summitの[セッションの動画](https://www.youtube.com/watch?v=UeheTiBJ0Io)をご覧ください。

## Kerasを使えば簡単にできますね
さて、KerasでCNNはどうやってCNNを実現するのでしょうか？以下のような感じです。

```python
from keras.models import Sequential

from keras.layers import Dense, Activation
from keras.layers import MaxPooling2D, Flatten, Dropout, Conv2D

import numpy as np

def cnn_small_model():
    model = Sequential()

    # Convolution層
    model.add(Conv2D(28, (3,3), input_shape=(150, 150, 3)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2,2)))

    # 平滑化
    model.add(Flatten())

    # 全結合層
    model.add(Dense(56))
    model.add(Activation('relu'))

    # ドロップアウト
    model.add(Dropout(0.5))

    # 出力層
    model.add(Dense(1))
    model.add(Activation('sigmoid'))

    # ネットワークのコンパイル
    model.compile(
        loss='binary_crossentropy',
        optimizer='rmsprop',
        metrics=['accuracy']
    )

    return model

model = cnn_small_model()

# モデルのサマリ
model.summary()
```

結果的に以下のようなネットワークができます。

![cnn_model.png](https://qiita-image-store.s3.amazonaws.com/0/1046/03d9feb9-f3fe-8327-a67f-267c90292885.png)



畳み込層、活性化層、プーリング層、平坦化層、全結合層って感じです。ディープではないですが、立派なCNNです。そうそう、CNNとは何かについても詳しくは扱いません。この後、このネットワークに対して画像を入力して学習を実行します。KerasにはImageDataGeneratorという画像をいい感じにアレコレできる便利なクラスも提供されています。また別の機会にご紹介したいと思います。

## CNNパラメータ多くてタイヘンダ−問題
おお、簡単にできるんだ−、やってみよう、と思うかと思いますが、いざやってみると色々と大変である事に気づきます。パラメータが多いのです。

- そもそもどんなネットワークを作るのか？
- 各層のパラメータにはどんな値を持たせるのか？
- etc...


という感じで、なかなかの初心者泣かせな側面があります。違う！学習をしたいんじゃないんだ！目的としてはまずCNNを使った画像認識を行いたいんだ！それも精度が良いものを使いたい。そんな時には学習済みネットワークを使うと良いでしょう。

## 学習済みモデル
学習済みモデルとは、その名の通り、すでに学習された後のモデルです。画像認識のコンテストなどで優秀な成績を収めたものが多数公開されており、それを利用することで先人の知恵を借りるようなものだと思っていただければ良いでしょう。

有名なモデルはいくつかありますが、例えばTensorFlowで使える学習済みモデルとしては、Inception-v3というモデルがあります。Inception-v3は[github上で公開](https://github.com/tensorflow/models/tree/master/inception)されていて、誰でも利用することができます。

![](https://github.com/tensorflow/models/blob/master/inception/g3doc/inception_v3_architecture.png?raw=true)
Inception-v3の図

Inception-v3はImageNetと言う画像認識のためのデータセットを使ったモデルで、画像認識コンテストで高い精度を叩き出したものです。その恩恵に預かることができるのです。なんて素敵なんでしょう。

## Kerasで学習済みモデルを使う
Kerasで学習済みモデルを利用するのは非常に簡単です。以下はほぼ[Kerasのドキュメント](https://keras.io/applications/)通りのコードですが、これで、与えた画像（`img_path`）が、何であるかをもっともらしい順に上位3番目まで出力してくれます。

```python
from keras.applications.inception_v3 import InceptionV3
from keras.preprocessing import image
from keras.applicatons.inception_v3 import preprocess_input, decode_predictions
import numpy as np

model = InceptionV3(weights='imagenet')

img_path = 'elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])
```

Inception v3を使うための準備は事実上以下の2行だけです。

```python
from keras.applications.inception_v3 import InceptionV3
model = InceptionV3(weights='imagenet')
```

たったこれだけでInception-v3を使えるようになります。非常に便利。

※ただし、このコードを実行するとまずInception-v3のモデルのダウンロードが始まるため注意してください。（Inception-v3は非常に大きなモデルです）

## 学習済みモデルは万能というわけではない

さあこれで全て解決するか、というとそういうわけではありません。Inception-v3は1000個のカテゴリにしか分類できません。それも一般的な概念1000個のみです。学習済みのネットワークなのでこの1000個のカテゴリは変更したりはもちろんできません。

FindYourCandyの場合、お菓子の種類などで分類したいのですが、Inception-v3をただ使うだけではやりたいことが実現できません。やはりCNNで一からモデルを作る必要があるのでしょうか？ そんな時に出てくるのが転移学習です。

## 転移学習
転移学習は、学習済みネットワークに、独自ネットワークを追加して、追加した独自ネットワークの部分だけを学習させるというものです。学習済みネットワークがすでに良い特徴を捉えており、追加のネットワーク部分でその特徴を基に少し手を加えるイメージです。メリットデメリットとしては以下のものがあります。

- メリット
  - すでによく学習された高い精度のモデルの恩恵にあずかれる
  - 1からモデルを作るより遥かに効率的
  - 適当っぽいけど案外うまくいくことが多い
  - すでに学習済みのネットワークなので、数万枚の学習データが必要とはならない。数十とか数百単位で良い
- デメリット
  - 必ずしもうまくいくかはやってみなければ分からない
  - 追加するネットワークの「どんなものをどれくらい」については試行錯誤するしかない

FindYourCandyの場合ですと、Inception-v3に通した後、全結合層が2層のネットワークを追加しています。

![chonan3.png](https://qiita-image-store.s3.amazonaws.com/0/1046/48931bab-6fc2-022d-4d32-e5d0d413e498.png)


## FindYourCandyの場合

で、最後にFindYourcandyでは、デモの都合上CloudMLを使う必要があったため、以下のような構成になっています。ちょっと記憶があいまいなのですが、CloudMLでInception-v3を読み込んで...ということをしようとするとProtobufのバージョンか何かでエラーがでるという不具合に遭遇してしまいまして、それを回避するための苦肉の策の構成です。（なお、FindYourCandyを開発していたのが、1月後半から2月後半と、TF自体のバージョンがV1.0に上がるが、CloudML側がついてきていない、という期間だったためで、現在はこの問題は解消されています）

- ローカル環境で画像をInception-v3に通して特徴量化
- 特徴量化したあとのデータをクラウドにアップロード
- アップロードした特徴量を入力とした全結合相が2層のネットワークの学習
- 学習後のモデルをローカルにダウンロード

苦肉の策だったのですが、画像自体をクラウドにアップロードしなくても良いため、けっこう良いアプローチなのではないかな、と今更ながらに思っています。

![train.png](https://qiita-image-store.s3.amazonaws.com/0/1046/f77ec4b7-c58c-4f44-1ea9-71df7d82da3a.png)


また、FindYourCandyはKerasではなく、素のTensorFlowで上記のことを実現しています。これもまあバージョンや時期的な話で、TensorFlowとKerasが統合された今なら間違いなくKerasでやってみるんじゃないかな、と思います。

## まとめ

- Keras楽ちんだよ
- アレコレ悩む前に転移学習を使ってみると良いよ
- FindYourCandyでも転移学習を使っているよ

という話でした。