YOLOv3 制作并训练自己的数据集
本文参考了:dspeia的制作教程,Rani_zZ的制作教程,Darknet官网中的YOLO教程。
首先对YOLO进行简要说明。YOLO是一个one-stage网络,其直接回归所检测目标的边框参数(左上点x,y,宽高width,height),置信度值以及属于各类的概率。YOLOv3将整张图片切割成了7x7块,并在每一块中都检测三个目标,也即每一块中所回归出的结果应当是3x(5+类的数量)。
1.制作自己的数据集
目前,网上制作数据集的教程几乎都用的是labelImg,其标注结果的格式与VOC数据集完全一致,因此可以直接使用YOLO作者制作的数据读取文件。具体过程不再赘述。
在标注完成后,将制作好的数据集放到你的数据集文件夹内(我的是/home/xxx/dataset/VOC2007),其文件夹构成如图:
其中,JPEGImages里是你的所有图片,Annotation里是与你的图片一一对应的XML标注文件(如果某个图不包含物体,请在标注时随便圈一下再删除,随后保存,否则不会生成XML,会报错!)
接下来,在Main里创建一个训练与测试列表。在VOC2007文件夹内创建一个split.py,将下列代码复制进去:
import os
import random
trainval_percent = 0.1
train_percent = 0.9
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')
for i in list:
name = total_xml[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftest.write(name)
else:
fval.write(name)
else:
ftrain.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
接着,命令行执行该文件
$ python split.py
执行后你应该能在Main里看到4个文件,里面分别存储了被作为四类数据的文件的名称。
2.下载并编译Darknet和YOLO
按照官网的教程,下载并编译测试Darknet,同时测试CUDA,cuDNN和OpenCV。CUDA和cuDNN安装与OpenCV安装见我之前的博客。
接着测试YOLO,同样跟随官网教程。
3.将数据集链接至Darknet中。
网上的一些教程将数据直接放在Darknet文件夹中,但我个人觉得这个方法很不适合后续维护。我的习惯是将数据都放在一个文件夹内方便查找,因此可以建立软链接实现这一过程。
在darknet文件夹下新建一个文件夹:
mkdir VOCdevkit
创建从你的数据文件夹到该文件夹的软链接:
ln -s /path/to/your/dataset/fold/VOC2007/ /path/to/your/darknet/fold/VOCdevkit/VOC2007
这样就可以方便的访问数据了。
4.转换数据格式
labelImg使用的是VOC的数据格式,而YOLO则需要使用txt数据。因此,需要对数据格式进行转换。
在darknet/scripts文件夹下有一个文件voc_label就是干这个用的。
将该文件复制至darknet文件夹内,然后修改该文件,将上面部分修改的适配你的数据集,比如我只需要检测car,则改为:
sets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
classes = ["car"]
文件最下面两行可以删除:
os.system("cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt > train.txt")
os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt")
然后执行该文件
$ python voc_label.py
你应该会看到文件夹里生成了几个txt,而打开数据集会看到label文件夹,里面存放了符合YOLO格式的训练数据。例如:
0 0.11328125 0.574305555556 0.034375 0.0263888888889
0 0.154296875 0.611111111111 0.02734375 0.025
0 0.173828125 0.645833333333 0.02734375 0.025
0 0.235546875 0.643055555556 0.02734375 0.025
0 0.300390625 0.60625 0.03046875 0.0208333333333
0 0.237109375 0.671527777778 0.03046875 0.0208333333333
0 0.283203125 0.638194444444 0.03046875 0.0208333333333
0 0.276171875 0.675694444444 0.03046875 0.0208333333333
0 0.344140625 0.610416666667 0.03046875 0.0208333333333
0 0.362109375 0.574305555556 0.03046875 0.0208333333333
0 0.344921875 0.678472222222 0.03046875 0.0208333333333
0 0.50859375 0.121527777778 0.0171875 0.0597222222222
0 0.46875 0.0291666666667 0.0171875 0.0583333333333
0 0.517578125 0.251388888889 0.01484375 0.05
0 0.53515625 0.385416666667 0.0234375 0.0569444444444
0 0.6328125 0.288194444444 0.028125 0.0597222222222
0 0.5890625 0.44375 0.028125 0.0597222222222
0 0.590625 0.527083333333 0.028125 0.0597222222222
0 0.63125 0.495138888889 0.028125 0.0597222222222
0 0.7171875 0.450694444444 0.0296875 0.0291666666667
0 0.763671875 0.459027777778 0.03359375 0.0291666666667
0 0.717578125 0.490972222222 0.03359375 0.0291666666667
0 0.669140625 0.605555555556 0.03359375 0.0416666666667
0 0.669140625 0.605555555556 0.03359375 0.0416666666667
0 0.698046875 0.56875 0.06484375 0.0819444444444
0 0.8125 0.624305555556 0.075 0.0541666666667
0 0.941015625 0.643055555556 0.07265625 0.0444444444444
0 0.76484375 0.675 0.0328125 0.0361111111111
0 0.907421875 0.678472222222 0.03359375 0.0319444444444
0 0.99140625 0.588888888889 0.015625 0.0222222222222
这里第一个数据是类的序号,因为我只检测car所以只有0.第2-5项对应边框的四个值,这里都已经基于边框的总宽高进行了归一化处理。
5.准备训练
下载ImageNet上的预训练权重:
wget https://pjreddie.com/media/files/darknet53.conv.74
修改data/voc.name(记得备份),里面放你的所有类的名称(回车分隔,最后一行无回车)。
car
修改cfg/voc.data(记得备份)
classes= 1 #classes为训练样本集的类别总数,我的算法只检测车,因此是1
train = /darknet/2007_train.txt #train的路径为训练样本集所在的路径,上一步中生成
valid = /darknet/2007_val.txt #valid的路径为验证样本集所在的路径,上一步中生成
names = data/voc.names #names的路径为data/voc.names文件所在的路径
backup = backup
修改cfg / yolov3-voc.cfg(记得备份)最上面部分,注释掉test对应部分,使用train部分。这里subdivisions是指将数据分为多少份输入,例如16则是每次输入4张图同时训练,最后64张图的所有结果视为一个batch,共同优化。这里需要根据GPU显存自行调整。
[net]
# Testing
# batch=1
# subdivisions=1
# Training
batch=64
subdivisions=16
......
[convolutional]
size=1
stride=1
pad=1
filters=18 #---------------修改为3*(5+classes)即3*(5+1)=18
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=1 #---------------修改为标签类别个数,1类
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=0 #1,如果显存很小,将random设置为0,关闭多尺度训练;(转自别的blog,还不太明白)
......
[convolutional]
size=1
stride=1
pad=1
filters=18 #---------------修改同上
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=1 #---------------修改同上
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=0
......
[convolutional]
size=1
stride=1
pad=1
filters=18 #---------------修改同上
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=1 #---------------修改同上
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=0
6.开始训练
执行下面的代码进行训练,其中-gpus是多显卡执行的命令,若只有单显卡可不加。
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg scripts/darknet53.conv.74 -gpus 0,1
训练过程中的执行结果应当如下:
……
Loaded: 0.000050 seconds
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.005261, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.002821, .5R: -nan, .75R: -nan, count: 0
Region 106 Avg IOU: 0.306019, Class: 0.971707, Obj: 0.377151, No Obj: 0.003877, .5R: 0.285714, .75R: 0.000000, count: 14
299: 18.237528, 28.675016 avg, 0.000008 rate, 0.065416 seconds, 299 images
……
这里只要有一个Region的IOU有显示就是正确的。随着训练过程进行,IOU应当会越来越高,例如对于我的任务最终都在75%左右。训练好的模型会被存储在darknet/backup中,名字如yolov3-voc_900.weights,这里的数字是执行epoch的数目。YOLO默认最多只会存储到900,后面虽然程序没停,但这个backup不会再增加。所以不要以程序停止作为训练结束的标志。
训练过程中可以查看GPU显存占用情况。nvidia-smi可以,但是只能查看一次。如果需要一直监控可执行
watch -n 0.1 nvidia-smi
其中-n后面的数字是多少秒刷新一次。
中间部分2727MiB/11777MiB就是你当前使用的gpu显存和总显存数。如果发现gpu不太满,可以减小上面subdivision数字的大小,从而提高训练速度。
上一篇: 有限状态机在游戏中的应用
下一篇: Contos7安装yarn