物体検出で遊んでみた♬～独自学習やってみた～

こういうのはやはり自分で学習してみないとということで、独自学習やってみました

まず、やり方は以下のサイトのとおり、
[物体検出アルゴリズム(SSD : Single Shot MultiBox Detector)を学習させてみる](https://qiita.com/slowsingle/items/64cc927bb29a49a7af14)

のとおり、データをダウンロードして展開して使いました。
※ここで2007はそれなりの大きさのデータですが、2012はかなり大きめ（２GB以上）
　2007のデータでさえ、ブラウザ変更してやっとダウンロードできました

どうにかダウンロードして、学習開始。。。
しかし、案外時間かかります。
ということで、モデルを見てみましょう
###物体検出のモデル
まず、前半はこんな感じです。

```
def SSD300v2(input_shape, num_classes=21):
    """SSD300 architecture.

    # Arguments
        input_shape: Shape of the input image,
            expected to be either (300, 300, 3) or (3, 300, 300)(not tested).
        num_classes: Number of classes including background.

    # References
        https://arxiv.org/abs/1512.02325
    """
    input_layer = Input(shape=input_shape)

    # Block 1
    conv1_1 = Conv2D(64, (3, 3),name='conv1_1', padding='same',　activation='relu')(input_layer)
    conv1_2 = Conv2D(64, (3, 3), name='conv1_2', padding='same', activation='relu')(conv1_1)
    pool1 = MaxPooling2D(name='pool1', pool_size=(2, 2), strides=(2, 2), padding='same', )(conv1_2)

    # Block 2
    conv2_1 = Conv2D(128, (3, 3), name='conv2_1', padding='same', activation='relu')(pool1)
    conv2_2 = Conv2D(128, (3, 3), name='conv2_2', padding='same', activation='relu')(conv2_1)
    pool2 = MaxPooling2D(name='pool2', pool_size=(2, 2), strides=(2, 2), padding='same')(conv2_2)

    # Block 3
    conv3_1 = Conv2D(256, (3, 3), name='conv3_1', padding='same', activation='relu')(pool2)
    conv3_2 = Conv2D(256, (3, 3), name='conv3_2', padding='same', activation='relu')(conv3_1)
    conv3_3 = Conv2D(256, (3, 3), name='conv3_3', padding='same', activation='relu')(conv3_2)
    pool3 = MaxPooling2D(name='pool3', pool_size=(2, 2), strides=(2, 2), padding='same')(conv3_3)

    # Block 4
    conv4_1 = Conv2D(512, (3, 3), name='conv4_1', padding='same', activation='relu')(pool3)
    conv4_2 = Conv2D(512, (3, 3), name='conv4_2', padding='same', activation='relu')(conv4_1)
    conv4_3 = Conv2D(512, (3, 3), name='conv4_3', padding='same', activation='relu')(conv4_2)
    pool4 = MaxPooling2D(name='pool4', pool_size=(2, 2), strides=(2, 2), padding='same')(conv4_3)

    # Block 5
    conv5_1 = Conv2D(512, (3, 3), name='conv5_1', padding='same', activation='relu')(pool4)
    conv5_2 = Conv2D(512, (3, 3), name='conv5_2', padding='same', activation='relu')(conv5_1)
    conv5_3 = Conv2D(512, (3, 3), name='conv5_3', padding='same', activation='relu')(conv5_2)
    pool5 = MaxPooling2D(name='pool5', pool_size=(3, 3), strides=(1, 1), padding='same')(conv5_3)

```

これ見ると、やはり過学習になりやすそう。。。ということで一回目転移学習(以下のとおりblock3まで固定)でも過学習になりました。

そこで、学習APの主要部分を見てみましょう。

```
path_prefix = './VOCdevkit/VOC2007/JPEGImages/'
gen = Generator(gt, bbox_util, 4, path_prefix,
                train_keys, val_keys,
                (input_shape[0], input_shape[1]), do_crop=False)

model = SSD300v2(input_shape, num_classes=NUM_CLASSES)
#model.load_weights('weights_SSD300.hdf5', by_name=True)
model.load_weights('./checkpoints/weights.01-4.35.hdf5', by_name=True)

freeze = ['input_1', 'conv1_1', 'conv1_2', 'pool1',
          'conv2_1', 'conv2_2', 'pool2',
          'conv3_1', 'conv3_2', 'conv3_3', 'pool3']#,
#           'conv4_1', 'conv4_2', 'conv4_3', 'pool4']

for L in model.layers:
    if L.name in freeze:
        L.trainable = False
        
def schedule(epoch, decay=0.9):
    return base_lr * decay**(epoch)

csv_logger = keras.callbacks.CSVLogger('./checkpoints/training.log', separator=',', append=True)
weights_save=keras.callbacks.ModelCheckpoint('./checkpoints/weights.{epoch:02d}-{val_loss:.2f}.hdf5',
                                             verbose=1,
                                             save_weights_only=True)
learnRateSchedule=keras.callbacks.LearningRateScheduler(schedule)

callbacks = [weights_save, csv_logger, learnRateSchedule]

base_lr = 0.0001 #3e-4
optim = keras.optimizers.Adam(lr=base_lr, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.)
#optim = keras.optimizers.Adam(lr=base_lr)
model.compile(optimizer=optim,
              loss=MultiboxLoss(NUM_CLASSES, neg_pos_ratio=2.0).compute_loss)
```
主要な部分は以上のとおり、案外簡単ですね。
もちろんこれ以外にRegion指定などの部分がありますが、それは後で考えることにします。
※それはSSDの根幹で仕組みに依存するので後回し

このコード見る限りは、ほぼ今まで見てきたフィッティングのコードと同じ。。。

```
model = SSD300v2(input_shape, num_classes=NUM_CLASSES)
model.load_weights('./checkpoints/weights.01-4.35.hdf5', by_name=True)
```
ここは文字通り、modelです。
そして、

```
freeze = ['input_1', 'conv1_1', 'conv1_2', 'pool1',
          'conv2_1', 'conv2_2', 'pool2',
          'conv3_1', 'conv3_2', 'conv3_3', 'pool3']#,
#           'conv4_1', 'conv4_2', 'conv4_3', 'pool4']

for L in model.layers:
    if L.name in freeze:
        L.trainable = False
```
ここは転移学習しますよというところ。つまり、freeze部分はL.trainable=Falseということ（block3まで固定）でパラメータ固定で学習します。ということです。

ということで、この転移学習したところ、

```
Epoch 5/10
4009/4009 [==============================] - 992s 247ms/step - loss: 2.1328 - val_loss: 2.7515
Epoch 00005: saving model to ./checkpoints/weights.05-2.75.hdf5
Epoch 6/10
4009/4009 [==============================] - 990s 247ms/step - loss: 1.9498 - val_loss: 2.7812
Epoch 00006: saving model to ./checkpoints/weights.06-2.78.hdf5
Epoch 7/10
4009/4009 [==============================] - 989s 247ms/step - loss: 1.7763 - val_loss: 2.8130
Epoch 00007: saving model to ./checkpoints/weights.07-2.81.hdf5
Epoch 8/10
4009/4009 [==============================] - 989s 247ms/step - loss: 1.6258 - val_loss: 2.8545
```
ということで、過学習の兆候が出てしまいました。

これで、物体検出すると以下のとおりの結果です。
![recognized_picture009.png](https://qiita-image-store.s3.amazonaws.com/0/233744/f374f073-e4a2-ac49-54a8-53b49c0efd3f.png)
結果はボロボロです。

ということで、ゼロから再学習することとしました。
つまり、コードを以下のとおり変更します。

```
freeze = ['input_1', 'conv1_1', 'conv1_2', 'pool1',
          'conv2_1', 'conv2_2', 'pool2',
          'conv3_1', 'conv3_2', 'conv3_3', 'pool3']#,
#           'conv4_1', 'conv4_2', 'conv4_3', 'pool4']
"""
for L in model.layers:
    if L.name in freeze:
        L.trainable = False
"""
```
そして、もちろん

```
#odel.load_weights('./checkpoints/weights.01-4.35.hdf5', by_name=True)
```
と転移学習を止めます。

###ゼロから学習。。。
その結果は、以下のとおり。。。。

![recognized_picture009.png](https://qiita-image-store.s3.amazonaws.com/0/233744/886d56a5-7e63-9194-6a8b-8e15f60a3da2.png)

あれ、笑っちゃうくらい悪いんじゃないの？？
もちろん、全編過学習なので、モデルを変更しないと。。。

今宵はここまでに致しとうござりまする。。。

明日は、きっと。。。