LoginSignup
1
4

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-04-12

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

まず、やり方は以下のサイトのとおり、
物体検出アルゴリズム(SSD : Single Shot MultiBox Detector)を学習させてみる

のとおり、データをダウンロードして展開して使いました。
※ここで2007はそれなりの大きさのデータですが、2012はかなり大きめ(2GB以上)
 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
結果はボロボロです。

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

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

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

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

明日は、きっと。。。

1
4
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
1
4