VGG16を使ったフルーツの画像認識

#１．背景
興味本位でKerasを使った画像認識を実装してみたところ、思いの外簡単だったので、より高精度なモデルを実装したく友人のススメでVGG16を使ってみることにした。
初学者なので、詳しいことは調べながらやってみる。
今回はりんごの王林の画像を使用して、品種まで当てられるのか評価を行う。
あくまでもメモ。

#２．そもそもVGG16ってなに？:thinking:
>VGG16というのは，「ImageNet」と呼ばれる大規模画像データセットで学習された，16層からなるCNNモデルのことです．2014年に発表されました．様々な研究で利用されている，有名な学習済みモデルの1つです．ImageNetで学習されたモデルには他にAlexNet，GoogLeNet，ResNetがあります．
https://www.y-shinno.com/keras-vgg16/

ここででているAlexNet,GoogLeNet,ResNetとの比較は以下が参考になる。

（引用元：http://thunders1028.hatenablog.com/entry/2017/11/01/035609）

>2014年のILSVRCで2位になった、オックスフォード大学のVGGチームのネットワーク。AlexNetをより深くした、畳み込み層とプーリング層から成るどノーマルなCNNで、重みがある層(畳み込み層や全結合層)を16層、もしくは19層重ねたもの。それぞれVGG16やVGG19と呼ばれる。

>小さいフィルターを持つ畳み込み層を2〜4つ連続して重ね、それをプーリング層でサイズを半分にするというのを繰り返し行う構造が特徴。大きいフィルターで画像を一気に畳み込むよりも小さいフィルターを何個も畳み込む(=層を深くする)方が特徴をより良く抽出できるらしい。(理由はよくわかってないが、活性化関数を通る回数が増えるため、表現力が増す？)[2]


GoogleNetのほうが強そうではあるが、わかりやすさ重視でVGGをやってみる。（難しそうなものは次回以降）



#３．VGG16の導入（GoogleColab使用）
早速コードを書いていく。
まずはKerasのimport

```vgg16_fluits.py
!pip install keras
```
次に必要なライブラリをインポートしていく。
VGG16はKerasの中に含まれている。
以下の３行目で重みを指定している。

```
#モデルをimportしてサマリー表示してみる
import numpy as np
from keras.applications.vgg16 import VGG16, preprocess_input, decode_predictions
model = VGG16(include_top=True, weights='imagenet', input_tensor=None, input_shape=None)
model.summary()
```



><details>
<summary>model.summary()結果</summary>
<div>Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_3 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 1000)              4097000   
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
_________________________________________________________________
</div>
</details>

今回使用する画像はりんご（王林）を評価する。
![apple_orin.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208753/b3c5bcb0-8051-db0c-3124-bd353cf1d472.jpeg)


```
  #画像の読み取り
from PIL import Image
#import glob
url = '/content/drive/My Drive/Colab Notebooks/img'
files=url+"/apple_orin.jpg"
image =Image.open(files)
image=image.convert('RGB')
image=image.resize((224,224))

# 読み込んだPIL形式の画像をarrayに変換
data = np.asarray(image)
```
```
#評価
from keras.preprocessing import image

#サンプル数の次元を1つ増やし四次元テンソルに
data = np.expand_dims(data, axis=0)
#上位5を出力
preds = model.predict(preprocess_input(data))
results = decode_predictions(preds, top=5)[0]
for result in results:
    print(result)
```

('n07742313', 'Granny_Smith', 0.9861995)
('n02948072', 'candle', 0.0040857443)
('n07747607', 'orange', 0.001778649)
('n03887697', 'paper_towel', 0.0016588464)
('n07693725', 'bagel', 0.0012920648)


となった。

#４．結果

１位の「Granny_Smith」ってなに？
>ラニースミス (英語：Granny Smith) はリンゴの栽培品種である。1868年にオーストラリアで、名前の由来ともなったマリア・アン・スミスにより、偶発実生で開発された
![commodity-granny-smith.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208753/942892a8-0ac7-ba1c-3e45-f89acdccee96.jpeg)

と、いうことで、画像自体はかなり近いので精度は高いとみてもよさそう。ImageNetに王林のデータがないのだろう。

ImageNetの1000クラス分の順番とラベル、クラス名の情報は以下のJSONファイルにまとめられている。
以下にGranny_Smithあり。
>https://storage.googleapis.com/download.tensorflow.org/data/imagenet_class_index.json


品種まで判定する画像認識を行うには別途学習させる必要があるため、次回以降にやる。

今回はあくまでも使ってみることが目的だったのでOK。

次回以降は品種まで当てられるモデルを作成する。


#５．考察

VGG16のモデルを使用するにあたって、キーとなるのは以下の箇所。

```
model = VGG16(include_top=True, weights='imagenet', input_tensor=None, input_shape=None)
```

##詳細
| 引数 | 説明 |
|:--|:--|
| include_top | 1000クラスに分類するフル結合層を含むかどうか． |
| True：含む（元の1000分類に使う場合はこちら） |
| False：含まない（カスタマイズする場合はこちら） |
| weights | 重みの種類 |
| imagenet：ImageNetを使って学習した重み |
| None：ランダム |
| input_tensor | モデル画像を入力する場合に使用 |
| 任意の画像データ：それを使用 |
| None：使用しない |
| input_shape | 入力画像の形状を指定 |
| 任意の形状：それを使用 |
| None：(224, 224, 3)が使用される |

include_topをFalseにしてVGG16を特徴量抽出に使用してファインチューニングする。（次回）
>参考（にしようとしているもの）
http://aidiary.hatenablog.com/entry/20170131/1485864665