オリジナルのデータセットに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)
学習曲線は怖いくらい順調に下がり、ログをとってないけどほぼ下の図のように降下。汎化性能も抜群
学習曲線図
YOLOv3はネットワークの構造上、大きい物体を学習しておけば、推論時には小さい物体も検知できるので、学習時には大きい物体のみくっきり写ってる「良質かつ大量」のデータセットで学習
yolov3は構造上小さい物体も検知できる
3.学習結果
予測した画像は下の感じ。かなりいい感じに検出できてる明るい画像
夜の暗い画像
学習時に気をつけたことは
・optimizerはSGDではなくAdamに変更
・callbackメソッドでval_lossの調整が意外に効いた
・公開されてる学習済みモデルで転移学習はコスパ的にかなり秀逸
今回はdarknet53の重みで一から学習せずにYOLOv3で転移学習してみた。faster-RCNNより小さい物体の検出対策がやりやすいので好きになった。
一から学習するより公開済みの学習ずみモデルで転移学習すれば、ほとんどのモデルで予想以上の成果でそう。
参考サイト