アプリとサービスのすすめ

アプリやIT系のサービスを中心に書いていきます。たまに副業やビジネス関係の情報なども気ままにつづります

学習済みのYOLOv3でオリジナルデータに転移学習(finetune)【物体検出】

オリジナルのデータセットにYOLOv3を使って物体検出した。
一から学習せずに、COCOデータセットの学習済みYOLOv3で転移学習してみたのでその備忘録


目次
1.オリジナルデータセットのclasses.txtと学習済みモデルの作成
2.訓練
3.学習結果


1.オリジナルデータセットのclasses.txtと学習済みモデルの作成

オリジナルデータセットはとあるコンペの公開画像を使用。

データセットformat(classes.txt)


[image_path, x1, y1, x2, y2, clas_id] で入力

anno_path = 'dataset_path'
fname = [path for path in os.listdir(anno_path)]
sorted_file =[path for path in natsorted(fname)]

df=[]
for idx, f in enumerate(sorted_file):
    fname, save,  b, cls_id = convert_annotation(anno_path, f)
    img = cv2.imread(fname)
    df.append([save, b[0], b[1], b[2], b[3], cls_id])

with open('classes.txt', 'w+') as f:
    for d in df:
        f.write(','.join(map(str, d)) + '\n')


classes.txt

Car
Pedestrian
Truck
Signal
Signs
Bicycle




学習済みモデル作成手順



kerasのYOLOv3を参考にdarknet53のweightの代わりに、COCO datasetの学習済みのYOLOv3のweightを使用

# Keras versionのYOLOv3をgit clone

$ git clone https://github.com/tanakataiki/keras-yolo3

# 学習済みmodelをdownloads
$ wget https://pjreddie.com/media/files/yolov3.weights

# 変換
$ python convert.py yolov3.cfg yolov3.weights yolo.h5

サイトに書いてあるとおり。






train.pyで使用

DARKNET_PATH = 'yolov3.h5'

def create_tiny_model(input_shape, anchors, num_classes, load_pretrained=True, freeze_body=2,
            weights_path=DARKNET_PATH):
    '''create the training model, for Tiny YOLOv3'''
    K.clear_session() # get a new session
    image_input = Input(shape=(None, None, 3))
    h, w = input_shape
    num_anchors = len(anchors)

    y_true = [Input(shape=(h//{0:32, 1:16}[l], w//{0:32, 1:16}[l], \
        num_anchors//2, num_classes+5)) for l in range(2)]

    model_body = tiny_yolo_body(image_input, num_anchors//2, num_classes)
    print('Create Tiny YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))

    if load_pretrained:
        model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)
        print('Load weights {}.'.format(weights_path))
        if freeze_body in [1, 2]:
            # Freeze the darknet body or freeze all but 2 output layers.
            num = (20, len(model_body.layers)-2)[freeze_body-1]
            for i in range(num): model_body.layers[i].trainable = False
            print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers)))

    model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
        arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.7})(
        [*model_body.output, *y_true])
    model = Model([model_body.input, *y_true], model_loss)

    return model




2.訓練

optimizerとcallbackメソッド

Adam(lr=1e-3)

ReduceLROnPlateau

ModelCheckpoint

model.compile(optimizer=Adam(lr=1e-3), loss={'yolo_loss': lambda y_true, y_pred: y_pred})

reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)
checkpoint = ModelCheckpoint('drive/My Drive/ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
        monitor='val_loss', save_weights_only=True, save_best_only=True, period=1)


学習曲線は怖いくらい順調に下がり、ログをとってないけどほぼ下の図のように降下。汎化性能も抜群


学習曲線図

f:id:trafalbad:20200118163451j:plain


YOLOv3はネットワークの構造上、大きい物体を学習しておけば、推論時には小さい物体も検知できるので、学習時には大きい物体のみくっきり写ってる「良質かつ大量」のデータセットで学習


yolov3は構造上小さい物体も検知できる

f:id:trafalbad:20200118163430p:plain




3.学習結果

予測した画像は下の感じ。かなりいい感じに検出できてる


明るい画像



f:id:trafalbad:20200118163531j:plain



f:id:trafalbad:20200118163528j:plain


夜の暗い画像


f:id:trafalbad:20200118163550j:plain


f:id:trafalbad:20200118163554j:plain


学習時に気をつけたことは

大きい物体がくっきり写ってる良質かつ大量のデータセットで学習(小さい物体は学習させてない)


optimizerはSGDではなくAdamに変更

callbackメソッドでval_lossの調整が意外に効いた

公開されてる学習済みモデルで転移学習はコスパ的にかなり秀逸


今回はdarknet53の重みで一から学習せずにYOLOv3で転移学習してみた。faster-RCNNより小さい物体の検出対策がやりやすいので好きになった。



一から学習するより公開済みの学習ずみモデルで転移学習すれば、ほとんどのモデルで予想以上の成果でそう。


参考サイト


A Closer Look at YOLOv3

Kaggle 2018 Google AI Open Images - Object Detection Track