LoginSignup
2
5

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-05-06

前回の記事で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については計測したいと思う

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5