【初心者必読】VGG16を動かしたいなら、まずはColabが正解です。

#〜本記事でわかること〜

・VGG16の実装方法


#１．はじめに

「わたしの備忘録」 兼 「初心者向け」の内容です。

・Deep Learningって分かんないけど、とにかく動かしたい！
・原理とかは後回し、実装方法だけ知りたい！

って方は、ぜひ読んでみてください！
(Qiitaの[旧アカウント](https://qiita.com/deeplearning408/items/45cefe559cbce0f8f52e)から持ってきました…)

#２．動作環境
動作環境は以下の通り。

・Windows 10
・Google Colab
・TensorFlow ver.2.3

今回はGoogle Colabにて
コーディングしていくので、
OSとかは特に関係ないです。

ネット環境だけ整ってれば、とりあえずオーケー！
便利な時代になりました。


#３．前準備

本記事で取り上げるVGG16というモデルは、
深層学習の中でも「__画像の分類__」が得意です。

なので、まずは皆さんに
自前のデータを用意してほしいんです。

わたしはマーベル作品が大好きなので、
「キャプテンアメリカ」
「アイアンマン」
「ハルク」
「ソー」
の画像を用意しました！

画像の枚数は適当でいいのですが、
手始めに各50枚ほど用意してみましょう。

用意できましたら、
VGG16が学習しやすいように
フォルダを作っていきます。

というわけで、
以下のようにフォルダを作ってください。

```
    image/
       ├ train/
       │    ├ captain/
       │    ├ ironman/
       │    ├ hulk/
       │    └ thor/   
       │
       ├ valid/
       │    ├ captain/
       │    ├ ironman/
       │    ├ hulk/
       │    └ thor/   
       │
       └ test/
            ├ captain/
            ├ ironman/
            ├ hulk/
            └ thor/   
```
    
そうしましたら、
「train」・「valid」・「test」フォルダの
各「captain」・「ironman」・「hulk」・「thor」フォルダに
画像を入れていきます。

各ヒーローごとに50枚あるので、
「train」に30枚、「valid」に10枚、「test」に10枚
入れていきます。

なので、全体の画像配置は
train：150枚
valid： 50枚
test ： 50枚
になりますね。

前準備はこれで終了。

#４．実装
###４−１　まずは、Google Driveへ

Google Colabでコーディングしていくのですが
そのためにも、まずGoogle Driveへ移動します。

みなさん、現代っ子のはずなので、
Googleアカウントは、さすがに持ってますよね？？
（無ければこの際に作っちゃいましょ！）

Googleアカウントにログインできましたら、
Google Driveへアクセス。

先程作った「image」フォルダをアップロードします。


###４−２　実際にコードを書いてみよう

現時点では、おそらくGoogle Colabが
インストールされていないと思うので、

下に示した図のようにインストールしていきます。


###４−３　学習開始！

それでは、学習していきましょう。
まずは、Google DriveとGoogle Colabを接続します。

```python
from google.colab import drive
drive.mount('/content/drive')
```

そして、VGG16のモデルを作成していきます。

```python
import matplotlib
import numpy as np
%matplotlib inline
from tensorflow.python.keras.callbacks import TensorBoard
from tensorflow.python.keras.applications.vgg16 import VGG16

vgg16 = VGG16(include_top=False, 
              input_shape=(224, 224, 3),
              weights='imagenet')
```

クラスの設定をします。
ここは皆さんの用意したデータによって変えてください。

```python
import random
classes = ['captain', 'ironman', 'hulk', 'thor']
```

モデルの生成とコンパイルをします。
compileのパラメータに「loss」や「optimizer」や「metrics」がありますが
これらをイジることで、解きたいタスクを変えたり、学習を収束させやすくできます。
今回は以下の値で学習させていきましょう。

```python
from tensorflow.python.keras.models import Sequential, Model
from tensorflow.python.keras.layers import Dense, Dropout, Flatten, GlobalAveragePooling2D
from tensorflow.keras.optimizers import SGD

def build_transfer_model(vgg16):
  model = Sequential(vgg16.layers)
  for layer in model.layers[:15]:
    layer.trainable = False
  model.add(Flatten())
  model.add(Dense(256, activation='relu'))
  model.add(Dropout(0.5))
  model.add(Dense(len(classes), activation='softmax'))
  return model

model = build_transfer_model(vgg16)

model.compile(loss='categorical_crossentropy',
              optimizer=SGD(lr=1e-5, momentum=0.9),
              metrics=['accuracy']
)
```

ジェネレータとイテレータを生成します。
カンタンに言うと、画像の拡張やスケール変換をしています。
この処理を挟むことで、1枚の画像からでも複数枚の画像を生成することができるのです。

人間の目で見れば、「ただ左右反転したり、斜めにしてみた画像じゃん？」ってなるんですが、
学習モデルにとっては、ピクセル単位で処理していくため、違う画像に見えるわけですね。

```python
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.applications.vgg16 import preprocess_input

idg_train = ImageDataGenerator(rescale=1/255.,
                               shear_range=0.1,
                               zoom_range=0.1,
                               horizontal_flip=True,
                               preprocessing_function=preprocess_input
)

idg_valid = ImageDataGenerator(rescale=1/255.)

img_itr_train = idg_train.flow_from_directory(directory='/content/drive/My Drive/image/train/',
                                              target_size=(224, 224),
                                              color_mode='rgb',
                                              classes=classes,
                                              batch_size=1,
                                              class_mode='categorical',
                                              shuffle=True
)

img_itr_validation = idg_valid.flow_from_directory(directory='/content/drive/My Drive/image/valid/',
                                                   target_size=(224, 224),
                                                   color_mode='rgb',
                                                   classes=classes,
                                                   batch_size=1,
                                                   class_mode='categorical',
                                                   shuffle=True
)
```

モデル保存用のディレクトリの準備をします。

```python
import os
from datetime import datetime

model_dir = os.path.join('/content/drive/My Drive/image/',
                         datetime.now().strftime('%y%m%d_%H%M')
)

os.makedirs(model_dir, exist_ok = True)
print('model_dir:', model_dir)

dir_weights = os.path.join(model_dir, 'weights')
os.makedirs(dir_weights, exist_ok = True)
```

学習の途中経過を保存したり、保存方法を設定します。

```python
from tensorflow.python.keras.callbacks import CSVLogger, ModelCheckpoint
import math
file_name='vgg16_fine'
batch_size_train=1
batch_size_validation=1

steps_per_epoch=math.ceil(img_itr_train.samples/batch_size_train
)
validation_steps=math.ceil(img_itr_validation.samples/batch_size_validation
)

cp_filepath = os.path.join(dir_weights, 'ep_{epoch:02d}_ls_{loss:.1f}.h5')

cp = ModelCheckpoint(cp_filepath,
                     monitor='loss',
                     verbose=0,
                     save_best_only=False,
                     save_weights_only=True,
                     mode='auto',
                     save_freq=5
)
csv_filepath = os.path.join(model_dir, 'loss.csv')
csv = CSVLogger(csv_filepath, append=True)
```

そして、ついに学習です。「epochs」を50に設定しているので、学習回数は50回です。

※厳密には学習回数とepochは同じ意味ではないのですが、
今回は支障がないので名前を統一しています。

```python
hist=model.fit_generator(
    img_itr_train,
    steps_per_epoch=steps_per_epoch,
    epochs=50,
    verbose=1,
    validation_data=img_itr_validation,
    validation_steps=validation_steps,
    shuffle=True,
    callbacks=[cp, csv]
)

model.save(file_name+'.h5')
```

コーディングが終わったので、
上からソースコードを実行していきます。

以下が途中経過の一例になります。
これが「Epoch 50/50」まで続きます。
それが学習終了の合図です。

```
Epoch 1/50
80/80 [==============================] - 18s 228ms/step - loss: 1.2106 - accuracy: 0.5000 - val_loss: 1.2738 - val_accuracy: 0.3500
Epoch 2/50
80/80 [==============================] - 22s 276ms/step - loss: 1.0407 - accuracy: 0.5875 - val_loss: 1.1765 - val_accuracy: 0.5000
Epoch 3/50
80/80 [==============================] - 34s 419ms/step - loss: 0.9078 - accuracy: 0.6375 - val_loss: 1.2011 - val_accuracy: 0.5500
Epoch 4/50
80/80 [==============================] - 31s 387ms/step - loss: 0.9747 - accuracy: 0.5625 - val_loss: 1.2541 - val_accuracy: 0.5000
```

###４−４　結果は・・・？？

さて、学習は終了しましたが、
まだ不完全です。

それもそのはず。
大学生に例えれば「講義」を受けて「課題」を
解いたにすぎないからです。

ある一定の評価（＝単位）をもらうには、
「試験（テスト）」が残っています。

といわけで、次のコードを実行しましょう。

```python
import matplotlib.pyplot as plt
from tensorflow.python.keras.preprocessing.image import load_img, img_to_array, array_to_img

idg_test = ImageDataGenerator(rescale=1.0/255)

img_itr_test = idg_test.flow_from_directory(directory='/content/drive/My Drive/image/test/',
                                            target_size=(224,224),
                                            batch_size=1,
                                            class_mode='categorical',
                                            shuffle=True
)

score=model.evaluate_generator(img_itr_test)
print('\n test loss:',score[0])
print('\n test_acc:',score[1])

plt.figure(figsize=(10,15))

for i in range(4):
  files=os.listdir('/content/drive/My Drive/image/test/' + classes[i] + '/')
  for j in range(5):
    temp_img=load_img('/content/drive/My Drive/image/test/' + classes[i] + '/' + files[j], target_size=(224,224))
    plt.subplot(4,5,i*5+j+1)
    plt.imshow(temp_img)
    temp_img_array=img_to_array(temp_img)
    temp_img_array=temp_img_array.astype('float32')/255.0
    temp_img_array=temp_img_array.reshape((1,224,224,3))
    img_pred=model.predict(temp_img_array)
    plt.title(str(classes[np.argmax(img_pred)]) + '\nPred:' + str(math.floor(np.max(img_pred)*100)) + "%")
    plt.xticks([]),plt.yticks([])

plt.show()
```

結果は、こんな感じ。

```
 test loss: 1.0708633661270142
 test_acc: 0.550000011920929
```
全体の精度にして、__55％__となりました。
まぁ、まずまずといったところでしょうか。

それではもっと詳しく見ていきます。
各画像の予測結果はこんな感じ。

![Screenshot from 2020-11-30 15-34-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553432/cccd82c0-9cb1-7332-2bfa-0381ddcd698e.png)



うん、アイアンマンの正答率がいい感じ。
アイアンマンが好きな私としては嬉しい結果です〜♪

画像枚数を増やしたり、パラメータや学習回数などをいじれば
もっと精度は向上するでしょうが・・・
今日のところはお開きとしたいです。


#５．おわりに

いかがでしたか？？
原理などは一切説明しませんでしたが、
少しでもAIに関して興味をもってもらえると嬉しいです。

本記事では、VGG16の実装方法を紹介しましたが、
実はこのモデル、結構古いんですよね・・・。
（2015年に論文が執筆されています。詳しくは[こちら](https://arxiv.org/abs/1409.1556)。）

今は新しく精度の高いモデルも
たくさん出ているので、興味がある方は
ぜひ試してみてください！

では、よいAIライフを〜 ^^