犬と猫の写真を判定するウェブサービスを作ってみた


# 概要

[Kaggle のある練習用のコンペティション](https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition)で大量の犬と猫の写真を入手できたので、これらを畳み込みニューラルネットワーク(CNN)に学習させ、[ウェブサービス](https://recognize-dogs-and-cats.herokuapp.com/)にしました。利用者は、自分の好きな写真をアップロードして、そこに犬または猫のどちらが写っているのか判定させることができます。気軽に遊んでみてください。

こちら↓
[Recognize Dogs and Cats](https://recognize-dogs-and-cats.herokuapp.com/)
[ソースコード(GitHub)](https://github.com/elm200/recognize-dogs-and-cats)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/26146/5f5f3117-14d0-5873-9f78-96cdeee8cedf.png)

# 解説

使用した CNN は以下のようなものです。Keras で記述してあります。[この記事](http://qiita.com/agumon/items/ab2de98a3783e0a93e66)のモデルをそのまま使わせていただきました。

```python
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense

model = Sequential()

model.add(Convolution2D(32, 3, 3, input_shape=(128, 128, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(2))
model.add(Activation('softmax'))

```
`model.summary()` の出力結果。

```
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 42, 42, 32)        896       
_________________________________________________________________
activation_1 (Activation)    (None, 42, 42, 32)        0         
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 21, 21, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 7, 7, 64)          18496     
_________________________________________________________________
activation_2 (Activation)    (None, 7, 7, 64)          0         
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 3, 3, 64)          0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 576)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                36928     
_________________________________________________________________
activation_3 (Activation)    (None, 64)                0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 64)                0         
_________________________________________________________________
dense_2 (Dense)              (None, 2)                 130       
_________________________________________________________________
activation_4 (Activation)    (None, 2)                 0         
=================================================================
Total params: 56,450.0
Trainable params: 56,450.0
Non-trainable params: 0.0
_________________________________________________________________
```

写真25000枚(犬:12500枚, 猫:12500枚)のうち、22500枚を訓練に使い、残り2500枚はモデルの精度を検証するために使いました（クロスバリデーション）。


- CPU: Intel Core i3 2370M
- GPU: なし
- メモリー: 12GB

という環境で学習に約7時間ほどかかりました。正答率(accuracy)は85%程度で、まだまだ改善の余地はあると思います。
（後日談: Fine tuning で正答率が上がりました！下記参照)

ウェブサイトにアクセスすると、最初3枚の写真が表示されます。これは、上でクロスバリデーション用に取っておいた2500枚のうち、500枚を使って、ランダムに表示しています。

アップロードした写真は、判定が終わったらすぐに削除するようにしています（いずれにしろ Heroku 上には動的にファイルを置くことはできません）。

# 感想

- Keras 最高! 必要十分で簡潔なインターフェイスが素晴らしい 
- Flask + jQuery のシンプルさがとても新鮮
- CNN の学習は、ちょっとした条件の違いで全く学習が進まないことがあるので、細心の注意が必要

# 追記(2017-05-29)
VGG16 + Fine tuning で正答率が94%まで高まりました！！
計算時間は22時間ほどかかっています。

| 項目       |       内容 |
|:-----------------|:------------------|
| 訓練データセット|2000枚（犬:1000枚, 猫:1000枚)|
| クロスバリデーションデータセット|800枚（犬:400枚, 猫:400枚)|

たった、2000枚しか訓練に使っていないのに、この成績(正答率94%)はすごい…。計算時間はかかりますが、前回と同様、22500枚を訓練に使えば、さらに正答率が上がりそうです。

[Fast.ai](http://course.fast.ai/)の[notebook](https://github.com/fastai/courses/blob/master/deeplearning1/nbs/lesson2.ipynb)によると、VGG16 + Fine tuning で正答率 は97%くらいまでは行けるみたいです。

```python
from keras.models import Input, Model, Sequential
from keras.layers import Dropout, Flatten, Dense
from keras.applications.vgg16 import VGG16

vgg16_model = VGG16(include_top=False, weights='imagenet', input_tensor=Input(shape=(150, 150, 3)))

top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16_model.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(1, activation='sigmoid'))

model = Model(inputs=vgg16_model.input, outputs=top_model(vgg16_model.output))
```

`model.summary()` の出力結果。

```
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 150, 150, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 150, 150, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 150, 150, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 75, 75, 64)        0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 75, 75, 128)       73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 75, 75, 128)       147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 37, 37, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 37, 37, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 18, 18, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 18, 18, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 9, 9, 512)         0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 4, 4, 512)         0         
_________________________________________________________________
sequential_1 (Sequential)    (None, 1)                 2097665   
=================================================================
Total params: 16,812,353
Trainable params: 16,812,353
Non-trainable params: 0
```

# 参考
[VGG16のFine-tuningによる犬猫認識 (2)](http://aidiary.hatenablog.com/entry/20170110/1484057655)
VGG16 をつかった Fine-tuning について非常に詳しく書かれており、たいへんお世話になりました。ありがとうございます。