Yolov3でKITTIデータを学習してみる

前回はYolov3のAlexeyAB/Darknetをインストールしたので今回は学習してみる

blog.akashisn.info

環境

  • Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
  • 64GB RAM
  • GeForce RTX 2080 Ti 11GB
  • Ubuntu 18.04 LTS

学習データの準備

学習データとしてドイツのカールスルーエ工科大学とアメリカ・シカゴにある豊田工業大学シカゴ校のチームによる、自動車ビジョンのための画像データを使用する

www.cvlibs.net

  • Download left color images of object data set (12 GB) (data_object_image_2.zip)
  • Download training labels of object data set (5 MB) (data_object_label_2.zip)

上記からこの2つのファイルをダウンロードする。

これらを同じディレクトリで展開する

$ mkdir raw
$ cd raw
$ unzip data_object_image_2.zip
$ data_object_label_2.zip
$ ls
training testing

学習画像はtraining/image_2に、ラベルはtraining/label_2にある

ラベルデータのフォーマットは

github.com

hirotaka-hachiya.hatenablog.com

このようになっているのでYolov3のラベルフォーマット

<ラベル> <矩形の中心座標x/画像幅> <矩形の中心座標y/画像高さ> <矩形の幅/画像幅> <矩形の高さ/画像高さ>

に変換する必要がある。

convert detectnet labeling format to yolov3 labeli ...

変換するスクリプトを書いた。

Pillowを使ってるので適当にインストールしてください。

$ python detectnet2yolo.py [画像ディレクトリ]  [ラベルディレクトリ]

と実行すると、カレントディレクトリにyolo_labelディレクトリを作成してその中に変換したラベルデータとclasses.txtを出力する

$ python detectnet2yolo.py training/image_2 training/label_2

Yoloでは画像とラベルは同一のディレクトリ内にある必要があるので

$ cd ..
$ mkdir train_data
$ cp raw/training/image_2/* data/
$ cp raw/yolo_label/* data/

このようにする。

次に、学習データとテストデータを振り分ける

generate yolo training and validation(test)

$ python generate_validation.py [データディレクトリ]

と実行すると、カレントディレクトリにtrain.txttest.txtを出力する

$ python generate_validation.py data/

darknetの設定

github.com

READMEを参照しながら設定していく

https://github.com/AlexeyAB/darknet/blob/master/cfg/yolov3.cfg

これを以下のように修正してcfg/yolo-obj.cfgにコピーする

$ diff --git a/cfg/yolov3.cfg b/cfg/yolov3.cfg
index 4a0ecc3..b01d980 100644
--- a/cfg/yolov3.cfg
+++ b/cfg/yolov3.cfg
@@ -1,10 +1,10 @@
 [net]
 # Testing
-batch=1
-subdivisions=1
+# batch=1
+# subdivisions=1
 # Training
-# batch=64
-# subdivisions=16
+batch=64
+subdivisions=16
 width=416
 height=416
 channels=3
@@ -17,9 +17,9 @@ hue=.1
 
 learning_rate=0.001
 burn_in=1000
-max_batches = 500200
+max_batches = 18000
 policy=steps
-steps=400000,450000
+steps=14400,16200
 scales=.1,.1
 
 [convolutional]
@@ -600,14 +600,14 @@ activation=leaky
 size=1
 stride=1
 pad=1
-filters=255
+filters=42
 activation=linear
 
 
 [yolo]
 mask = 6,7,8
 anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
-classes=80
+classes=9
 num=9
 jitter=.3
 ignore_thresh = .7
@@ -686,14 +686,14 @@ activation=leaky
 size=1
 stride=1
 pad=1
-filters=255
+filters=42
 activation=linear
 
 
 [yolo]下
 mask = 3,4,5
 anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
-classes=80
+classes=9
 num=9
 jitter=.3
 ignore_thresh = .7
@@ -773,17 +773,18 @@ activation=leaky
 size=1
 stride=1
 pad=1
-filters=255
+filters=42
 activation=linear
 
 
 [yolo]
 mask = 0,1,2
 anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
-classes=80
+classes=9
 num=9
 jitter=.3
 ignore_thresh = .7
 truth_thresh = 1
 random=1
 
+
batch=64
subdivisions=16

これについては

stackoverflow.com

batchsubdivisionsで割り切れればいいよって情報と

github.com

subdivisionsが大きすぎると学習がうまく行かなくなったっていう情報があるのでよくわからない。

でもbatch64のままで、 GPUのメモリと相談してsubdivisions8,16.32と調整するのがいいみたい。

次にcfg/obj.dataを以下で作成する

classes = 9
train = train.txt
valid = test.txt
names = train_data/classes.txt
backup = weights/

次に事前学習データをダウンロードする

$ wget https://pjreddie.com/media/files/darknet53.conv.74

次に学習結果を保存するディレクトリを作成する

$ mkdir weights

最後にラベルをリポジトリのdata/labelsからコピーしてくる

$ mkdir data
$ cp -r (path to darknet)/data/labels data/

結果的にこのようなディレクトリ構成にする

$ tree .
.
├── cfg
│   ├── obj.data
│   └── yolo-obj.cfg
├── darknet53.conv.74
├── data
│   └── labels
│     ...
├── train_data
│   ├── 000000.png
│   ├── 000000.txt
│   ├── 000001.png
│   ├── 000001.txt
│     ...
├── raw
│   ├── training
│     ...
│   ├── testing
│     ...
│   └── yolo_label
│     ...
├── test.txt
├── train.txt
└── weights

学習する

$ darknet detector train cfg/obj.data cfg/yolo-obj.cfg darknet53.conv.74 -map

17時間後

detections_count = 11067, unique_truth_count = 5168  
class_id = 0, name = Car, ap = 94.26%        (TP = 2584, FP = 117) 
class_id = 1, name = Van, ap = 98.39%        (TP = 312, FP = 21) 
class_id = 2, name = Truck, ap = 99.73%      (TP = 103, FP = 2) 
class_id = 3, name = Pedestrian, ap = 72.65%     (TP = 313, FP = 86) 
class_id = 4, name = Person_sitting, ap = 76.04%     (TP = 21, FP = 4) 
class_id = 5, name = Cyclist, ap = 86.48%        (TP = 138, FP = 18) 
class_id = 6, name = Tram, ap = 99.09%       (TP = 65, FP = 4) 
class_id = 7, name = Misc, ap = 95.11%       (TP = 105, FP = 7) 
class_id = 8, name = DontCare, ap = 36.97%       (TP = 512, FP = 584) 

 for conf_thresh = 0.25, precision = 0.83, recall = 0.80, F1-score = 0.82 
 for conf_thresh = 0.25, TP = 4153, FP = 843, FN = 1015, average IoU = 67.70 % 

 IoU threshold = 50 %, used Area-Under-Curve for each unique Recall 
 mean average precision (mAP@0.50) = 0.843033, or 84.30 % 
Total Detection Time: 8.000000 Seconds

mAPが84.3%なのでまぁまぁいい感じに学習できた

テストしてみる

raw/testing/image_2にテスト用のデータがあるのでこれを入力してみる

$ darknet detector test cfg/obj.data cfg/yolo-obj.cfg weights/yolo-obj_best.weights raw/testing/image_2/000090.png
...
raw/testing/image_2/000090.png: Predicted in 11.234000 milli-seconds.
Car: 100%
Car: 100%
Car: 100%
Car: 39%
Car: 69%
DontCare: 30%

f:id:akashisn:20191216225430j:plain

ちゃんと学習できてる