DeepLearning
ssd
Keras
物体検出
FineTuning

PreTrainedなモデルで新たなSSD300を作る♬

前回の記事でVGG16ベースのPreTrained_SSD300を作成した。

そこでは、単に世にあるVGG16ライクなSSD300モデルの前段にVGG16をInput_layerとして採用した。
ここでは、その入力を最適化するとともにその学習手法を考える。
また、VGG16のみでなく、他のPreTrainedなモデルを利用して、同様なSSD300モデルを作成しようと思う。
あくまでも仮説だが、メリットは少なくとも以下の二つがあると思う。
①大量なカテゴリーに対する物体認識としての精度がある程度保証される
②学習時間を大幅に削減できる可能性がある
 少なくとも最初に物体認識でPre-Trainingする必要が無い

ということで、まず全体的な構造を見てみよう

PreTrained_SSD300の構造

1. SSDモデルの入力部分のコードは以下のとおり

# Block 1
input_shape = (input_shape[1], input_shape[0], 3)

input = Input(input_shape)
vgg16 = VGG16(input_shape=input_shape, include_top=False, weights='imagenet')
FeatureExtractor = Model(inputs=vgg16.input, outputs=vgg16.get_layer('block3_pool').output)

pool3 = FeatureExtractor(input)

# Block 4
conv4_1 = Conv2D(512, (3, 3),activation='relu',padding='same',name='conv4_1')(pool3)

2. この部分のmodel.summary()は以下のとおり

model.summary()
Layer (type)                    Output Shape         Param #     Connected to
=============================================================================================
input_2 (InputLayer)            (None, 300, 300, 3)  0
_____________________________________________________________________________________________
model_1 (Model)                 multiple             1735488     input_2[0][0]
_____________________________________________________________________________________________
conv4_1 (Conv2D)                (None, 37, 37, 512)  1180160     model_1[1][0]
_____________________________________________________________________________________________

3. 普通のSSD300の対応するmodel.summary()は以下のとおり

SSD300のmodel.summary()
Layer (type)                    Output Shape         Param #     Connected to
=============================================================================================
input_1 (InputLayer)            (None, 300, 300, 3)  0
_____________________________________________________________________________________________
conv1_1 (Conv2D)                (None, 300, 300, 64) 1792        input_1[0][0]
_____________________________________________________________________________________________
conv1_2 (Conv2D)                (None, 300, 300, 64) 36928       conv1_1[0][0]
_____________________________________________________________________________________________
pool1 (MaxPooling2D)            (None, 150, 150, 64) 0           conv1_2[0][0]
_____________________________________________________________________________________________
conv2_1 (Conv2D)                (None, 150, 150, 128 73856       pool1[0][0]
____________________________________________________________________________________________
conv2_2 (Conv2D)                (None, 150, 150, 128 147584      conv2_1[0][0]
_____________________________________________________________________________________________
pool2 (MaxPooling2D)            (None, 75, 75, 128)  0           conv2_2[0][0]
_____________________________________________________________________________________________
conv3_1 (Conv2D)                (None, 75, 75, 256)  295168      pool2[0][0]
_____________________________________________________________________________________________
conv3_2 (Conv2D)                (None, 75, 75, 256)  590080      conv3_1[0][0]
_____________________________________________________________________________________________
conv3_3 (Conv2D)                (None, 75, 75, 256)  590080      conv3_2[0][0]
_____________________________________________________________________________________________
pool3 (MaxPooling2D)            (None, 38, 38, 256)  0           conv3_3[0][0]
_____________________________________________________________________________________________
conv4_1 (Conv2D)                (None, 38, 38, 512)  1180160     pool3[0][0]
_____________________________________________________________________________________________

4.VGG16.pyの構造と設計的考察

一つ目は、

conv4_1 (Conv2D)                (None, 37, 37, 512)  1180160    model_1[1][0]

conv4_1 (Conv2D)                (None, 38, 38, 512)  1180160     pool3[0][0]

の比較である。
わずかであるが、Tensorのサイズが異なる。PreTrainなモデルでは(None, 37, 37, 512)であり、通常のものは (None, 38, 38, 512)である。
どうせなら、ここは一致させておいた方が良い。
ということで、ここはあの1個まで調整できるUpsampling?であるDeconvolutionを使おう。
そのコードは以下のようなものになるが、次元はどうしようかな?
つまり、滑らかにつなげるなら254だし、元々のVGG16モデルを考えると512でも良さそう。
※こういうのは両方やってみていい方をとるべきなんだろね

conv4_0 = Conv2DTranspose(256, 2, 2, activation='relu',   border_mode='valid', name='conv4_0')(pool3)

二つ目は以下のリンクを見てもらうと

FeatureExtractor = Model(inputs=vgg16.input, outputs=vgg16.get_layer('block3_pool').output)

この'block3_pool'の意味が分かると思う。この場合、この部分までのLayerを再利用している。それ以降のblock4,block5はSSDのモデルと被っているのでこの場合は、使っていない。
逆にいうと、物体認識としては半分しか使っていないから本来精度は出ない。
だから、実験的な意味ではblock5までしっかり使おうよというのがあると思う。
【参考】
keras / keras / applications / vgg16.py

三つめは、とにかく

conv4_1 (Conv2D)                (None, 38, 38, 512)  1180160    model_1[1][0]

になりさえすれば、上はどんなモデルが乗っていてもとりあえず動くということです。
そして、できればVGG16のようにFeature Blockの一部をDetection部分に入れて準備するのがいいのでしょう。
ここでは、複雑なこと考えたくないので(時間がかかりすぎるので)簡単に上に乗せるだけにしようと思います。

簡単な4つのモデル

ここでは以下4つのモデルを作成してみました。
※以下はいずれも物体認識でFineTuningの記事とコーディングしたモデルです。
今回は上記の考察を考慮して、以下のようなモデルになると思います。
4つのモデルの変更箇所を以下に記載します。
ssd300VGG16_alt.py

# Block 1 
input_shape = (input_shape[1], input_shape[0], 3) 
input = Input(input_shape) 

vgg16 = VGG16(input_shape=input_shape, include_top=False, weights='imagenet') 
FeatureExtractor = Model(inputs=vgg16.input, outputs=vgg16.get_layer('block3_pool').output) 

pool3 = FeatureExtractor(input) 
conv4_0 = Conv2DTranspose(512, (2, 2), name='conv4_0', activation='relu', border_mode='valid')(pool3) 

ssd300VGG19_alt.py

vgg19 = VGG19(input_shape=input_shape, include_top=False, weights='imagenet') 
FeatureExtractor = Model(inputs=vgg19.input, outputs=vgg19.get_layer('block3_pool').output) 

ssd300ResNet53_alt.py

resnet50 = ResNet50(input_shape=input_shape,include_top=False, weights='imagenet')
FeatureExtractor = Model(inputs=resnet50.input, outputs=resnet50.get_layer('add_7').output)

ssd300InceptionV3_alt.py
上記と比較すると、出力が異なるのでDecovolutionが異なりますが、基本はほぼ同じです。

inceptionV3 = InceptionV3(input_shape=input_shape, include_top=False, weights='imagenet') 
FeatureExtractor = Model(inputs=inceptionV3.input, outputs=inceptionV3.get_layer('mixed2').output)

pool3 = FeatureExtractor(input)
conv4_0 = Conv2DTranspose(512, (4, 4), name='conv4_inceptionv3', activation='relu', border_mode='valid')(pool3) #for inceptionV3

学習

・物体認識部分はPreTrainだから、再学習は必要なく物体認識精度が保証される

「再学習は必要ない」は、どうも思惑が外れた

つまり、PreTrain部分を固定して学習しようとすると、非常に小さなaccからスタートして一向に上がってこないという現象が発生。その結果、やはり全部を学習するということとした。
それでも、PreTrain部分が一定のパラメータになっているので、学習は進みやすいように感じる。
比較のために、SSD300_smallなるもの:これは以前「ぶっちゃけ変なモデル。。。^^;」でベースにした、比較的小さいけど、検出精度の高いVGG16ライクなモデルを使って学習してみた。
※これ、実はオリジナルのSSD300と比較するとconv3_3が無いことと、MaxPoolingをAveragePoolingを使っていること、そしてBatchNormalizationを使っていることがことなるが、ほぼ似たような構成だとわかる
ssd300small_alt.py

「物体認識精度が保証される」も、どうも思惑が外れたようだ

よく見ると、PreTrainで利用しているネットワークが案外小さい部分しか使っていない。
これでは、全体としてパラメータフィッティングされているのだから、保証というところまでは言えないようだ。
もちろん、学習当初の発散みたいな現象はあまり(固定すると発生した)なかったが。

・学習時間を大幅に削減できる可能性

上の書いたように、ほとんどPreTrainなWeightsの効果が今一つということで、結局全体を学習した方が精度もよく、早いという結論。
つまり、パラメータフィッティングはしないが、結局Tensorの計算を実施する必要がある。まあ、一応バックプロパゲーションの計算はその部分はしていないはずだが、それでも20%程度の削減にしかなっていない。(1060で1200s⇒1000s位)

実は物体検出のPGを大きく物体認識部と検出部と考えれると、実はあるモデルで物体検出全体のパラメータが決まっていて、かつ入力部分の物体認識部をPreTrainなモデルに変更した場合、やり方によっては全くTraining無しに正しく物体検出してくれる可能性が大いにあると思うが、今のところそのアイディアを実現していない。
※Layer毎にパラメータを反映するということができるといいなぁ~
model.load_weights('weights_SSD300.hdf5', by_name=True)でやれるのかと思ったら、Weights数が異なると怒られて、已矣哉!

結果

合計5つのモデルのパラメータフィッティングを実施しているが、一番いいのがVGG19でvaccで55%程度のところ、そのほかはやっと45%に到達するかというところでこれはほんとに時間がかかる。
VGG19はよく見るとBlock3までのところは、VGG16と変わらないので、実際フィッティングできたとしてもあまり面白くない。
ということで、ここではInceptionV3とsmallの結果を示したいと思う。

まず、smallから、これはVGG16と似ていると思いましたが、45%程度でaccが飽和してしまって、上がりません。Base_IrなどのパラメータやそもそもAdamでなくてSGDとかでやれば変わるかもしれませんが、現状は以下の通りです。
recognized_picture002__001.png
recognized_picture001__005.png
そこから、30epoch(10時間位)回すとどうにかこうにか(もうほとんど上がりませんが)50.5%になりました。そして、上記よりは少しマシかな??
recognized_picture002__003.png
recognized_picture001__005.png

一方、InceptionV3では50%超えたところ(51%辺り)で、足踏みしていますが、もう少し上がるような気がしますが、12時間以上回したので、このあたりで一度上げておきます。
recognized_picture007.png
recognized_picture008.png
recognized_picture006.png
こちらもさらに35epoch(10時間位)回すと、64%超えしましたが、物体検出は以下のようにまだまだです。
recognized_picture002__004.png
recognized_picture001__005.png

まとめ

・まだまだ精度はいまいちだが、5つのモデルについてPretrainなモデルや既知のネットワークを利用したSSD300モデルを構築した。
・学習の仕方(既知のWeightの使い方)やネットワークの構築はまだまだ改良の余地がありそうである
・mAP vs fptについては計測したいと思う