独自データセットを使用したyolov3による画像判別例
更新日:2022年10月20日(初版作成日:2021年8月14日)
目的
ベルトコンベア(そんなイメージ)に乗った複数の曲がったきゅうりと真っ直ぐなきゅうりと大きすぎるきゅうりを自動判別する。
データの準備
きゅうりの画像を100枚撮る。
撮影例)
バラバラな名前、*.JPGとなっているファイル名を揃える。
1. globで*.JPGを全て取得。
2. file_001.jpgからfile_100.jpgに変更する。
(数値.zfill(3)で三桁に揃える)
import os
import glob
from pathlib import Path
print("change all jpg file name")
img_path = Path('.')
file_counter = 0
for file in img_path.glob('PIC*.JPG'):
file_counter = file_counter + 1
print(file)
new_file_name = 'file_' + str(file_counter).zfill(3) + '.jpg'
os.rename(file, new_file_name)
サイズを調整する。
1. file_001.jpgからfile_100.jpgまで読み込む。
2. file_001.jpgを768×432に縮小して、file_001_out.jpgとする。
3. file_100まで繰り返す。
import os
print("change all jpg file size")
for file_num in range(100):
print(file_num)
jpg_file_name = 'file_' + str(file_num+1).zfill(3) + '.jpg'
jpg_file_name_out = 'file_' + str(file_num+1).zfill(3) + '_out.jpg'
my_command = 'convert ' + jpg_file_name + ' -resize 768x432 ' + jpg_file_name_out
# print(my_command) # check your command before you do this.
os.system(my_command)
画像に問題なければ、移動して、名前を変換する。
% mv *_out.jpg ./my_cucumber_img
% rename ‘s/_out//’ *.jpg
以下では、この100個の画像を使用する。
アノテーションテキスト(labels)の作成
前節で作成した100画像分、アノテーションテキストを作成する。
まず、アノテーションテキスト作成ソフトウェア
labelImg
をインストールする(リンク先に各OSのインストール方法がある)。
labelImgのインストールされたディレクトリ
(環境によって異なる)
/usr/local/lib/python3.9/site-packages/labelImg
移動して、dataディレクトリを作成し、
predefined_classes.txtを作成する。
curved
straight
big
画像ディレクトリに戻りlabelImgを実行する。
% labelImg
(下図のような画面が出る)
1. 左のメニューから”Open Dir”を選択して、画像ディレクトリを開く。
2. 出力を”yolo”フォーマットにするために、メニューの8番目のトグルスイッチをクリックして”yolo”が出てくるようにする。
3. メニューの9番目の”Create RectBox”を選択して、きゅうりを囲む矩形を作る。
4. 囲みを作ると右下のようなボックスが出るので、labelを入力する。曲がったきゅうりには”curved”、真っ直ぐなきゅうりには、”straight”、大きなやつには”big”とラベルを付与する。
5. メニューの7番目をクリックして保存する。
作成例)
0 0.483539 0.109954 0.547325 0.206019
1 0.265432 0.427083 0.251029 0.446759
1 0.574074 0.931713 0.769547 0.136574
2 0.572016 0.636574 0.716049 0.560185
備考)
曲がったきゅうり、”curved”、”0″
真っ直ぐなきゅうり、”straight”、”1″
大きすぎるきゅうり、”big”、”2″
darknet+yolo の環境生成
darknet+yolov3を使用するディレクトリを作る。
以下、このディレクトリ内で処理を行う。
% mkdir test_darknet
% cd test_darknet
次に、darknet+yolov3をダウンロード、コンパイルする。
(Warningメッセージがでるが、致命的なものではないので無視する。)
% git clone https://github.com/AlexeyAB/darknet
% cd darknet
% make
確認する(usageが出ればOK)。
% ./darknet
usage: ./darknet <function>
この時点では、test_darknetフォルダに、darknetフォルダが一つだけある。
ここに、自前のデータ、自前の設定ファイルを置くフォルダ、my_dataを生成する。
% mkdir my_data
次の図は、最終的なmy_dataのディレクトリ・ファイル構成である。
上図の説明)
darknetは、インストールされたデータや設定ファイルが入っている。
my_dataは、これから準備するファイル・ディレクトリである。分かりやすいように、順に番号を付ける。
0. backup・・・空ディレクトリ(weightsデータ保存ディレクトリ)
1. darknet53.conv.74・・・畳み込みニューラルネットワークデータ
2. ./images/・・・訓練画像、テスト画像
3. ./labels/・・・アノテーションテキスト
4. ./labels/classes.txt・・・クラス名のテキスト
5. obj.data・・・データ設定ファイル
6. obj.names・・・クラス名のテキスト
7. test.txt・・・評価用画像の相対パス(実行ファイル darknetから)
8. train.txt・・・訓練用画像の相対パス(実行ファイル darknetから)
9. yolov3.cfg・・・YOLO設定ファイル
上から(darknet53.conv.74から)順に準備していく。
1. 畳込みニューラルネットワークのデータdarknet53.conv.74をダウンロードする。
% cd my_data
% wget https:pjreddie.com/media/files/darknet53.conv.74
2. 画像ディレクトリを作って、予め作成した画像をここにコピーする。
(今回は100個の画像を作成したので、100個の画像がここに全て入る。)
% mkdir images
% cd images
% cp your_image_directory/*.jpg .
3. ラベルディレクトリを作って、予め作成したアノテーションテキストをここにコピーする。
(今回は100個の画像を作成したので、100個の.txtがここに全て入る。)
% mkdir labels
% cd labels
% cp your_anotation_text_directory/*.txt .
4. ラベルディレクトリにclasses.txtを作成する。
curved
straight
big
5. ひとつ上の階層(my_data)に戻ってobj.dataファイルを作る。
ここで、classesは、分類数。train、validは、訓練データ、テストデータ。namesは分類名、backupは訓練データの保存箇所である。この”../”の意味は、訓練実行コマンドであるdarknetからの相対パスである。
classes = 3
train = ../my_data/train.txt
valid = ../my_data/test.txt
names = ../my_data/obj.names
backup = ../my_data/backup
6. obj.namesファイルを作る(classes.txtと同じ)。
curved
straight
big
7. test.txtファイルを作る。test.txtファイルは、画像の中でテスト用に使用するものの相対パスを列挙したものである(全体の20%)。
../my_data/images/file_005.jpg
../my_data/images/file_038.jpg
../my_data/images/file_016.jpg
../my_data/images/file_099.jpg
../my_data/images/file_067.jpg
../my_data/images/file_058.jpg
../my_data/images/file_070.jpg
../my_data/images/file_047.jpg
../my_data/images/file_090.jpg
../my_data/images/file_091.jpg
../my_data/images/file_045.jpg
../my_data/images/file_097.jpg
../my_data/images/file_054.jpg
../my_data/images/file_081.jpg
../my_data/images/file_030.jpg
../my_data/images/file_035.jpg
../my_data/images/file_021.jpg
../my_data/images/file_020.jpg
../my_data/images/file_034.jpg
../my_data/images/file_008.jpg
8. train.txtファイルを作る。train.txtは、訓練用の画像の相対パスを列挙したものである(全体の80%)。
../my_data/images/file_011.jpg
../my_data/images/file_039.jpg
../my_data/images/file_004.jpg
../my_data/images/file_010.jpg
../my_data/images/file_006.jpg
...
../my_data/images/file_022.jpg
../my_data/images/file_036.jpg
../my_data/images/file_037.jpg
../my_data/images/file_023.jpg
../my_data/images/file_009.jpg
train.txt(全体)
../my_data/images/file_011.jpg
../my_data/images/file_039.jpg
../my_data/images/file_004.jpg
../my_data/images/file_010.jpg
../my_data/images/file_006.jpg
../my_data/images/file_012.jpg
../my_data/images/file_013.jpg
../my_data/images/file_007.jpg
../my_data/images/file_003.jpg
../my_data/images/file_017.jpg
../my_data/images/file_002.jpg
../my_data/images/file_028.jpg
../my_data/images/file_014.jpg
../my_data/images/file_001.jpg
../my_data/images/file_015.jpg
../my_data/images/file_029.jpg
../my_data/images/file_072.jpg
../my_data/images/file_066.jpg
../my_data/images/file_098.jpg
../my_data/images/file_073.jpg
../my_data/images/file_065.jpg
../my_data/images/file_071.jpg
../my_data/images/file_059.jpg
../my_data/images/file_064.jpg
../my_data/images/file_048.jpg
../my_data/images/file_060.jpg
../my_data/images/file_074.jpg
../my_data/images/file_100.jpg
../my_data/images/file_075.jpg
../my_data/images/file_061.jpg
../my_data/images/file_049.jpg
../my_data/images/file_077.jpg
../my_data/images/file_063.jpg
../my_data/images/file_088.jpg
../my_data/images/file_089.jpg
../my_data/images/file_062.jpg
../my_data/images/file_076.jpg
../my_data/images/file_053.jpg
../my_data/images/file_084.jpg
../my_data/images/file_085.jpg
../my_data/images/file_046.jpg
../my_data/images/file_052.jpg
../my_data/images/file_044.jpg
../my_data/images/file_050.jpg
../my_data/images/file_078.jpg
../my_data/images/file_087.jpg
../my_data/images/file_093.jpg
../my_data/images/file_092.jpg
../my_data/images/file_086.jpg
../my_data/images/file_079.jpg
../my_data/images/file_051.jpg
../my_data/images/file_069.jpg
../my_data/images/file_041.jpg
../my_data/images/file_055.jpg
../my_data/images/file_082.jpg
../my_data/images/file_096.jpg
../my_data/images/file_083.jpg
../my_data/images/file_040.jpg
../my_data/images/file_068.jpg
../my_data/images/file_056.jpg
../my_data/images/file_042.jpg
../my_data/images/file_095.jpg
../my_data/images/file_080.jpg
../my_data/images/file_094.jpg
../my_data/images/file_043.jpg
../my_data/images/file_057.jpg
../my_data/images/file_024.jpg
../my_data/images/file_018.jpg
../my_data/images/file_019.jpg
../my_data/images/file_025.jpg
../my_data/images/file_031.jpg
../my_data/images/file_027.jpg
../my_data/images/file_033.jpg
../my_data/images/file_032.jpg
../my_data/images/file_026.jpg
../my_data/images/file_022.jpg
../my_data/images/file_036.jpg
../my_data/images/file_037.jpg
../my_data/images/file_023.jpg
../my_data/images/file_009.jpg
備考)簡単な分割プログラム
100個画像を80個と20個にランダムに分ける。
my_dataディレクトリで実行すると、imagesディレクトリを参照しつつ、train.txtとtest.txtが生成される。
# make_train_and_test.py
# make_train_and_test.py
import glob
import random
from pathlib import Path
print("making train.txt and test.txt ...")
f_train = open('train.txt', 'w')
f_test = open('test.txt', 'w')
img_path = Path('./images')
train_ratio = 0.8
total_number = 100
max_train_counter = total_number * train_ratio
max_test_counter = total_number * (1.0 - train_ratio)
total_counter = 0
train_counter = 0
test_counter = 0
#
search_txt = 'file*.jpg'
for file in img_path.glob(search_txt):
total_counter = total_counter + 1
print(total_counter)
print(file)
#
if train_counter < max_train_counter:
if random.random() $lt train_ratio:
train_counter = train_counter + 1
f_train.write('../my_data/' + str(file) + '\n')
else:
if test_counter < max_test_counter:
test_counter = test_counter + 1
f_test.write('../my_data/' + str(file) + '\n')
else:
f_train.write('../my_data/' + str(file) + '\n')
else:
test_counter = test_counter + 1
f_test.write('../my_data/' + str(file) + '\n')
f_train.close()
f_test.close()
print("Done.")
9. yolov3.cfgを作成する。
まず、オリジナルのcfgファイルをコピーする。
% cp ../darknet/cfg/yolov3.cfg .
今回の設定に合うように書き換える。
8行目、9行目:今回の画像サイズに合わせる(32の倍数にする)。
width=608
height=608
(書き換え後)
width=800
height=800
20行目:バッチサイズを(クラス数(3) x 2000)にする。
max_batches = 500200
(書き換え後)
max_batches = 6000
22行目:ステップ数を(バッチサイズ x 0.8、バッチサイズ x 0.9)にする。
steps=400000,450000
(書き換え後)
steps=4800,5400
603行目:フィルター数を((クラス数(3) + 5)x 3 )にする。
filters=255
(書き換え後)
filters=24
610行目:クラス数を(クラス数(3))にする。
classes=80
(書き換え後)
classes=3
689行目:フィルターを((クラス数(3) + 5)x 3 )にする。
filters=255
(書き換え後)
filters=24
696行目:クラス数を(クラス数(3))にする。
classes=80
(書き換え後)
classes=3
776行目:フィルターを((クラス数(3) + 5)x 3 )にする。
filters=255
(書き換え後)
filters=24
783行目:クラス数を(クラス数(3))にする。
classes=80
(書き換え後)
classes=3
darknet+yolov3 による訓練
実行ファイルdarknetがある、”/test_darknet/darknet”ディレクトリに移動して、次のコマンドで実行する。
% ./darknet detector train ../my_data/obj.data ../my_data/yolov3.cfg ../my_data/darknet53.conv.74
実行すると、いくつかの処理情報と以下のようなメッセージが定期的に出力される(MacBook Pro 2019)。
備考)
MacBook Pro 2019
CPU 1.4 GHz Quad-Core intel Core i5
MEM 8 GB
…
Region 82 Avg IOU: 0.257473, Class: 0.502334, Obj: 0.519767, No Obj: 0.591954, .5R: 0.000000, .75R: 0.000000, count: 4
Region 94 Avg IOU: 0.191529, Class: 0.533095, Obj: 0.499006, No Obj: 0.473622, .5R: 0.125000, .75R: 0.000000, count: 8
Region 106 Avg IOU: nan, Class: nan, Obj: nan, No Obj: 0.426729, .5R: nan, .75R: nan, count: 0
1: 827.000122, 827.000122 avg, 0.000000 rate, 1408.221523 seconds, 64 images
Loaded: 0.000072 seconds
Region 82 Avg IOU: 0.228399, Class: 0.323098, Obj: 0.402954, No Obj: 0.591033, .5R: 0.142857, .75R: 0.000000, count: 7
Region 94 Avg IOU: 0.427593, Class: 0.680186, Obj: 0.456276, No Obj: 0.473622, .5R: 0.400000, .75R: 0.200000, count: 5
Region 106 Avg IOU: nan, Class: nan, Obj: nan, No Obj: 0.420948, .5R: nan, .75R: nan, count: 0
…
順に、”バッチ数”、”当該バッチでのloss”、”全体でのloss(平均)”、”学習率”、”当該バッチでの計算時間”、”学習画像枚数”である。
“バッチ数”はyolov3.cfgで設定した6000のことで、この数値が6000まで繰り返される。
次に、”loss”というのは、正解の取りこぼしのこで、小さければ小さいほど良い。従って、学習が進めば、小さくなる。
“lossの平均”は少し独特の計算がされている(lossの平均 = 0.9 x lossの平均 + loss)。
次は学習率で、最初は値が小さいため丸めの影響で、0.0から始まる。
次の計算時間は、1バッチの計算に1408秒かかったという意味である。ざっくり3バッチ1時間と考えると、6000バッチ全計算時間は2000時間になって、約3ヶ月かかることになる。
最後の項目は、使用した通算画像数とその読込時間である。
darknet/example/super.cから引用)float loss = train_network(net, train); if (avg_loss < 0) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs);
デフォルト設定では、100バッチ処理毎にバックアップファイルが生成される。
このファイルを使用して、再実行することもできる。
(以下は、500バッチ処理のバックアップデータを使用した場合)
% ./darknet detector train ../my_data/obj.data ../my_data/yolov3.cfg ../my_data/backup/yolov3_500.weights
訓練が終了すると、test_darknet/my_data/backup/yolov3_final.weightsファイルが生成される。
GPU環境設定
3ヶ月待つことはできない場合は、GPU環境を使用する(Xavier、google colbなど)(約100倍速くなる)。
まず、darknetの実行ファイル、オブジェクトファイルを一旦削除する。
% cd test_darknet/darknet
% make clean
test_darknet以下を、GPU環境にコピーする(ネットワーク経由、USB等の記憶媒体など)。
makeの前に、MakefileのGPU=0をGPU=1に、CUDNN=0をCUDNN=1に、OPENCV=0をOPENCV=1に変更する。
% cd test_darknet/darknet
% make
再度、darknet+yolov3 による訓練に戻る。
以下は、ログの例である(NVIDIA Jetson AGX Xavier)。1408秒から17秒になって、約100倍速くなっている。
1バッチ17秒なので、6000バッチで、102000秒。3600秒で割って、約28なので、28時間。
結局、全体(6000ステップ)では、大体1日かかる。
備考)
NVIDIA Jetson AGX Xavier
GPU 512-core Volta GPU with Tensor Cores
CPU 8-core ARM v8.2 64-bit CPU, 8MB L2 + 4MB L3
MEM 16GB
...
Region 82 Avg IOU: 0.186076, Class: 0.638941, Obj: 0.412670, No Obj: 0.426827, .5R: 0.000000, .75R: 0.000000, count: 11
Region 94 Avg IOU: 0.186517, Class: 0.641289, Obj: 0.297971, No Obj: 0.459332, .5R: 0.000000, .75R: 0.000000, count: 4
Region 106 Avg IOU: nan, Class: nan, Obj: nan, No Obj: 0.465523, .5R: nan, .75R: nan, count: 0
1: 1377.132324, 1377.132324 avg, 0.000000 rate, 17.598136 seconds, 64 images
Loaded: 0.000125 seconds
Region 82 Avg IOU: 0.140658, Class: 0.325652, Obj: 0.396659, No Obj: 0.427702, .5R: 0.000000, .75R: 0.000000, count: 7
Region 94 Avg IOU: 0.130643, Class: 0.676824, Obj: 0.459017, No Obj: 0.459783, .5R: 0.000000, .75R: 0.000000, count: 2
Region 106 Avg IOU: nan, Class: nan, Obj: nan, No Obj: 0.465326, .5R: nan, .75R: nan, count: 0
...
...
Region 82 Avg IOU: 0.942668, Class: 0.999847, Obj: 0.999941, No Obj: 0.003058, .5R: 1.000000, .75R: 1.000000, count: 9
Region 94 Avg IOU: 0.936328, Class: 0.999706, Obj: 0.978264, No Obj: 0.000252, .5R: 1.000000, .75R: 1.000000, count: 4
Region 106 Avg IOU: nan, Class: nan, Obj: nan, No Obj: 0.000000, .5R: nan, .75R: nan, count: 0
Region 82 Avg IOU: 0.946644, Class: 0.999929, Obj: 0.999419, No Obj: 0.003355, .5R: 1.000000, .75R: 1.000000, count: 9
Region 94 Avg IOU: 0.942201, Class: 0.999955, Obj: 0.999366, No Obj: 0.000294, .5R: 1.000000, .75R: 1.000000, count: 3
Region 106 Avg IOU: nan, Class: nan, Obj: nan, No Obj: 0.000000, .5R: nan, .75R: nan, count: 0
6000: 0.027853, 0.026027 avg, 0.000010 rate, 27.014417 seconds, 384000 images
Saving weights to ../my_data2/backup/yolov3.backup
Saving weights to ../my_data2/backup/yolov3_final.weights
darknet/src/yolo_layer.cから引用)printf("Region %d Avg IOU: %f, Class: %f, Obj: %f, No Obj: %f, .5R: %f, .75R: %f, count: %d\n", net.index, avg_iou/count, avg_cat/class_count, avg_obj/count, avg_anyobj/(l.w*l.h*l.n*l.batch), recall/count, recall75/count, count);
Region : ニューラルネットワーク層
Avg IOU : Intersection over unit area、検出されたバウンディングボックスと正解の "共通部分" / "全体の平均"
Class : クラス確率の平均
Obj : オブジェクト数の平均
No Obj : オブジェクト数のバッチ毎の平均
.5R : IOU50%の平均
.75R : IOU75%の平均
count : オブジェクト検出数
テスト
別途撮影した画像を使用する。
判定は次のコマンドで実行する。
% ./darknet detector test ../my_data/obj.data ../my_data/yolov3.cfg ../my_data/backup/yolov3_final.weights ../my_data/cucumber1.jpg -thresh 0.5
ここで、最後から2番目の画像が、テスト用の画像指定で、最後のパラメータは、判定基準指定である(今回は、0.5(50%以上の判定で))。
それぞれ、次のような結果を得る。
また、webカメラによるリアルタイム判定も可能である。
% ./darknet detector demo ../my_data/obj.data ../my_data/yolov3.cfg ../my_data/backup/yolov3_final.weights
備考)
fps(frame per second)が稼げないので、画面サイズを小さくしている。
参考)
You Only Look Once: Unified, Real-Time Object Detection (arXiv)
YOLO9000: Better, Faster, Stronger (yolo v2, arXiv)
YOLOv3: An Incremental Improvement (arXiv)
Darknet (github)
Darknet (neural network framework)
YOLO: Real-Time Object Detection
How to train YOLOv3 on the custom dataset
[YOLO] 独自データセットを使用したyolov3(内部リンク)
改訂履歴
改訂履歴
2022年10月20日:体裁の変更