【Finetuningの極意】動物会話のアプリをFinetuning（＋中間層利用）で小規模かつ高速に作成♬

VGG16などを使ったFinetuning技法を利用して動物会話アプリを再作成してみました。
どうしても機械学習は大量データと長時間の学習がつきものと考えがちです。
しかし、実際そういう面もありますが（人間の）脳や学習経験を考えてみると、あまり学習時間をかけていないように思います。それというのも、経験というのを最大限生かすこと、そしてそれらの情報を再利用する能力が能では高いことが分かります。
＞人間のこのような能力（たぶん、これを知能という）はデザインで云うアフォーダンスを成り立たせているようです
・[アフォーダンス＠Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%95%E3%82%A9%E3%83%BC%E3%83%80%E3%83%B3%E3%82%B9)
そして、機械学習として確立している技法でこの手のものは、Finetuning技術です。
ということで、Finetuningを最大限利用することを考えつつ、経験の再利用を意識して進めようと思います。
また、今回は小回りが利くように小規模マシンでも使えるように・学習できるようにを目指して簡単に作れるかにも着目して作成しました。
###とりあえずの目標
・アプリ【動物会話】をFinetuningで効率よく作成する
・小規模（CPU）環境で学習してみる
・多段階の識別構造を作成して効率を考える
・多段階Finetuning構造を制御するメタ学習と利用について考察する
・多品種・多目的・異種技法（画像,音,皮膚感覚,味覚,自然言語処理，計測&制御．．．）を取り入れた多段階Finetuningの集合体としての網の可能性Objective・Neuro網（今回導入した用語）と利用を考える
###やったこと
アプリ【動物会話】をFinetuningで効率よく作成する
・Finetuningの仕組み
・Finetuningのモデルとして中間層を利用する
・小規模モデルで再学習してみる
・block毎にモデル化してみる
###・Finetuningの仕組み
画像分類などにおいて、学習済のモデルを利用し、それに若干の新規のネットワークモデルを付加することにより、新たな画像等についてほとんどのWeightを再利用して再学習することにより分類する手法である。
コードで見ると以下のとおり、
【参考】
・[DL-FineTuning/akb_VGG16.py](https://github.com/MuAuan/DL-FineTuning/blob/master/akb_VGG16.py)
オリジナルは以下を参考としています
・[VGG16のFine-tuningによる17種類の花の分類](http://aidiary.hatenablog.com/entry/20170131/1485864665)

```py
# VGG16モデルと学習済み重みをロード
# Fully-connected層（FC）はいらないのでinclude_top=False）
input_tensor = Input(shape=x_train.shape[1:])  #(img_rows, img_cols, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

# FC層を構築
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(num_classes, activation='softmax'))

# VGG16とFCを接続
model = Model(input=vgg16.input, output=top_model(vgg16.output))

# 最後のconv層の直前までの層をfreeze
#trainingするlayerを指定　VGG16では18,15,10,1など 20で全層固定
for layer in model.layers[1:18]:  
    layer.trainable = False
```
###・Finetuningのモデルとして中間層を利用する
もう少し進んだものとして、以下があります。
【参考】
・[中間レイヤーの出力を得るには？@KerasDocumentation](https://keras.io/ja/getting-started/faq/#_7)
これを利用すると、当初のモデルの一部を利用して、小さなモデル（しかもPretrained）で学習できることが分かります。
つまり、以下のようなコードで実現できます。
ここでは、vgg16のうちblock3までを利用して、上記と同じFC層をつけて学習ができます。

```py
# VGG16モデルと学習済み重みをロード
# Fully-connected層（FC）はいらないのでinclude_top=False）
input_tensor = Input(shape=x_train.shape[1:]) 
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

from keras.models import Model
layer_name1 = 'block3_pool'
intermediate_layer_model = Model(inputs=vgg16.input,
                                 outputs=vgg16.get_layer(layer_name1).output)
# FC層を構築
top_model = Sequential()
top_model.add(Flatten(input_shape=intermediate_layer_model.output_shape[1:]))  #vgg16 
top_model.add(Dense(15*num_classes, activation='relu'))  #256 #20*num_classes
top_model.add(Dropout(0.5))
top_model.add(Dense(num_classes, activation='softmax'))

# VGG16とFCを接続
#model = Model(input=vgg16.input, output=top_model(vgg16.output))
model = Model(input=vgg16.input, output=top_model(intermediate_layer_model.output))

# 最後のconv層の直前までの層をfreeze
#trainingするlayerを指定　VGG16では18,15,10,1など 20で全層固定
for layer in model.layers[1:1]:  
    layer.trainable = False
```
###・小規模モデルで再学習してみる
学習データは、Train on 170 samples, validate on 59 samplesであり、
学習時間は全層Finetuningしても、全部で１分位です。

```
  model = Model(input=vgg16.input, output=top_model(intermediate_layer_model.output))
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         (None, 128, 128, 3)       0
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 128, 128, 64)      1792
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 128, 128, 64)      36928
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 64, 64, 64)        0
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 64, 64, 128)       73856
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 64, 64, 128)       147584
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 32, 32, 128)       0
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 32, 32, 256)       295168
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 32, 32, 256)       590080
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 32, 32, 256)       590080
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 16, 16, 256)       0
_________________________________________________________________
sequential_1 (Sequential)    (None, 3)                 2949303
=================================================================
Total params: 4,684,791
Trainable params: 4,684,791
Non-trainable params: 0
_________________________________________________________________
```
学習も50epochで以下のように使えるレベルの精度が出ています。

```
Train on 170 samples, validate on 59 samples
Epoch 47/50
170/170 [==============================] - 1s 5ms/step - loss: 0.4210 - acc: 0.8235 - val_loss: 0.3920 - val_acc: 0.9153
Epoch 48/50
170/170 [==============================] - 1s 5ms/step - loss: 0.3996 - acc: 0.8353 - val_loss: 0.3937 - val_acc: 0.9153
Epoch 49/50
170/170 [==============================] - 1s 5ms/step - loss: 0.4695 - acc: 0.8118 - val_loss: 0.4307 - val_acc: 0.8983
Epoch 50/50
170/170 [==============================] - 1s 5ms/step - loss: 0.4508 - acc: 0.8353 - val_loss: 0.4199 - val_acc: 0.9153
```
ということで、これだとパラメータサイズがGithubに掲載できるサイズになるので、以下にweight込みで諸々置いておきましたので、ご興味がある方はカラス会話アプリ遊んでみてください。
####コードは以下に置きました
・[hirakegoma/FineTuning/](https://github.com/MuAuan/hirakegoma/tree/master/FineTuning)
###・block毎にモデル化してみる
こうなると、もっと中間層のパラメータも取得してみたくなるし、ネットワークモデルも自由に作りたくなる。
今後、個別の学習済modelをObjective・Neuroとして構造化しようと考えているので、そのためにもくっつけたりはがしたり自由度が欲しい。
ということで、vgg16は以下のコードでばらばらになりました。
コード全体は以下のとおり
・[hirakegoma/FineTuning/VGG16_finetuning_block3_top3.py](https://github.com/MuAuan/hirakegoma/blob/master/FineTuning/VGG16_finetuning_block3_top3.py)
こちらも、計算時間は全部で１分位です。

【参考】
・[Kerasでfine-tuning](https://qiita.com/p-baleine@github/items/5572b5b9c08649280700)

```py
# VGG16モデルと学習済み重みをロード
# Fully-connected層（FC）はいらないのでinclude_top=False）
input_tensor = Input(shape=x_train.shape[1:]) 
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

from keras.models import Model
layer_name1 = 'block3_pool'
intermediate_layer_model = Model(inputs=vgg16.input,
                                 outputs=vgg16.get_layer(layer_name1).output)

inputs0 = Input(shape=intermediate_layer_model.output_shape[1:])
# Block 4
block4_conv1 = Conv2D(512, (3, 3),name='block4_conv1', activation='relu', padding='same')(inputs0)
block4_conv2 = Conv2D(512, (3, 3),name='block4_conv2', activation='relu', padding='same')(block4_conv1)
block4_conv3 = Conv2D(512, (3, 3),name='block4_conv3', activation='relu', padding='same')(block4_conv2)
#bn4 = BatchNormalization(axis=3)(conv4_3)
block4_pool = MaxPooling2D(pool_size=(2, 2),name='block4_pool')(block4_conv3)
#drop4 = Dropout(0.5)(pool4)
block4_model = Model(inputs=inputs0, outputs=block4_pool)

inputs1 = Input(shape=block4_model.output_shape[1:])
# Block 5
block5_conv1 = Conv2D(512, (3, 3),name='block5_conv1', activation='relu', padding='same')(inputs1)
block5_conv2 = Conv2D(512, (3, 3),name='block5_conv2', activation='relu', padding='same')(block5_conv1)
block5_conv3 = Conv2D(512, (3, 3),name='block5_conv3', activation='relu', padding='same')(block5_conv2)
#bn5 = BatchNormalization(axis=3)(conv5_3)
block5_pool = MaxPooling2D(pool_size=(2, 2),name='block5_pool')(block5_conv3)
#drop5 = Dropout(0.5)(pool5)
block5_model = Model(inputs=inputs1, outputs=block5_pool)

inputs2 = Input(shape=block5_model.output_shape[1:])
# FC層を構築
# top_model
x = Flatten()(inputs2)
x = Dense(15*num_classes, activation="relu")(x)
x = Dropout(0.5)(x)
predictions = Dense(num_classes, activation="softmax")(x)
top_model = Model(inputs=inputs2, outputs=predictions)
#以下はなんとなくだけどこれで動きます
model = Model(inputs=intermediate_layer_model.input, outputs=top_model(block5_model(block4_model(intermediate_layer_model.output))))

# 最後のconv層の直前までの層をfreeze
#trainingするlayerを指定　VGG16では18,15,10,1など 20で全層固定
for layer in model.layers[1:1]:  
    layer.trainable = False
```
###まとめ
・動物会話のアプリを題材にVGG16を利用してFinetuningを実施した
・小規模モデルで学習し、小さなモデルで会話アプリができることが分かった
・構造をblock単位でばらばらにモデル化し、Weightなどを取得した

今後当面は以下を実施します
・Objective・Neuroを意識して構造化したモデルを構築する
・多段モデルを作成する
###おまけ

```
model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         (None, 128, 128, 3)       0
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 128, 128, 64)      1792
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 128, 128, 64)      36928
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 64, 64, 64)        0
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 64, 64, 128)       73856
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 64, 64, 128)       147584
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 32, 32, 128)       0
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 32, 32, 256)       295168
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 32, 32, 256)       590080
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 32, 32, 256)       590080
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 16, 16, 256)       0
_________________________________________________________________
model_2 (Model)              (None, 8, 8, 512)         5899776
_________________________________________________________________
model_3 (Model)              (None, 4, 4, 512)         7079424
_________________________________________________________________
model_4 (Model)              (None, 3)                 368823
=================================================================
Total params: 15,083,511
Trainable params: 15,083,511
Non-trainable params: 0
_________________________________________________________________
intermediate_layer_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         (None, 128, 128, 3)       0
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 128, 128, 64)      1792
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 128, 128, 64)      36928
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 64, 64, 64)        0
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 64, 64, 128)       73856
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 64, 64, 128)       147584
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 32, 32, 128)       0
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 32, 32, 256)       295168
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 32, 32, 256)       590080
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 32, 32, 256)       590080
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 16, 16, 256)       0
=================================================================
Total params: 1,735,488
Trainable params: 1,735,488
Non-trainable params: 0
_________________________________________________________________
block4_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_2 (InputLayer)         (None, 16, 16, 256)       0
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 16, 16, 512)       1180160
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 16, 16, 512)       2359808
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 16, 16, 512)       2359808
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 8, 8, 512)         0
=================================================================
Total params: 5,899,776
Trainable params: 5,899,776
Non-trainable params: 0
_________________________________________________________________
block5_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_3 (InputLayer)         (None, 8, 8, 512)         0
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 8, 8, 512)         2359808
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 8, 8, 512)         2359808
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 8, 8, 512)         2359808
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 4, 4, 512)         0
=================================================================
Total params: 7,079,424
Trainable params: 7,079,424
Non-trainable params: 0
_________________________________________________________________
top_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_4 (InputLayer)         (None, 4, 4, 512)         0
_________________________________________________________________
flatten_1 (Flatten)          (None, 8192)              0
_________________________________________________________________
dense_1 (Dense)              (None, 45)                368685
_________________________________________________________________
dropout_1 (Dropout)          (None, 45)                0
_________________________________________________________________
dense_2 (Dense)              (None, 3)                 138
=================================================================
Total params: 368,823
Trainable params: 368,823
Non-trainable params: 0
_________________________________________________________________
Train on 170 samples, validate on 59 samples
Epoch 7/10
170/170 [==============================] - 1s 8ms/step - loss: 0.1337 - acc: 0.9529 - val_loss: 0.5312 - val_acc: 0.8475
Epoch 8/10
170/170 [==============================] - 1s 8ms/step - loss: 0.1697 - acc: 0.9235 - val_loss: 0.3897 - val_acc: 0.8983
Epoch 9/10
170/170 [==============================] - 1s 8ms/step - loss: 0.1005 - acc: 0.9588 - val_loss: 0.3644 - val_acc: 0.9153
Epoch 10/10
170/170 [==============================] - 1s 8ms/step - loss: 0.0764 - acc: 0.9706 - val_loss: 0.3956 - val_acc: 0.8983
```