darknet训练自己的数据,官方提供了一个训练VOC的例子,我们参照这个例子,来训练我们自己的数据。
1. 准备数据集
首先我们应该准备好我们自己的数据集用于训练。要训练一个自己的网络,我们需要有训练集和测试集。在这里,我们建立两个文件夹train和val用于存放这两类数据。接下来,我们要做的是生成数据的标记文件,也就是目标在图片中的类别以及位置信息。
1.1 生成标记文件
生成yolo的标记文件应该遵循以下两个规则:
- 每个原图像都对应着一个标记文件,而且文件名都相同,比如图片的名字是1.jpg,那么对应这张图片的标记文件名称应该为1.txt。
- 每个标记文件中,内容应该是这样的<object-class> <x> <y> <width> <height>,其中<x> <y> <width> <height>是相对于图像宽高的比例。
知道了这两个规则之后,我们就可以定义我们的标记文件了。这里我已经有了VOC格式标记的XML文件,那么接下来我们使用Python来生成YOLO格式的标记文件,代码如下:
- import xml.etree.ElementTree as ET
- import pickle
- import os
- from os import listdir, getcwd
- from os.path import join
- TrainLabel_Dir = '/home/ubuntu/data/labels' #XML所在目录
- def convert(size, box):
- dw = 1./size[0]
- dh = 1./size[1]
- x = (box[0] + box[1])/2.0
- y = (box[2] + box[3])/2.0
- w = box[1] - box[0]
- h = box[3] - box[2]
- x = x*dw
- w = w*dw
- y = y*dh
- h = h*dh
- return (x,y,w,h)
- if not os.path.exists('labels/'): #生成的label放在label目录下
- os.makedirs('labels/')
- for rootDir,dirs,files in os.walk(TrainLabel_Dir):
- for file in files:
- file_name = file.split('.')[0]
- out_file = open('labels/%s.txt'%(file_name),'w')
- in_file = open("%s/%s"%(rootDir,file))
- tree = ET.parse(in_file)
- root = tree.getroot()
- size = root.find('size')
- w = int(size.find('width').text)
- h = int(size.find('height').text)
- for obj in root.iter('object'):
- xmlbox = obj.find('bndbox')
- b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
- bb = convert((w,h), b)
- out_file.write("0" + " " + " ".join([str(a) for a in bb]) + '\n') #我这里只有一类,所以类别只是0.
- out_file.close()
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
TrainLabel_Dir = '/home/ubuntu/data/labels' #XML所在目录
def convert(size, box):
dw = 1./size[0]
dh = 1./size[1]
x = (box[0] + box[1])/2.0
y = (box[2] + box[3])/2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
if not os.path.exists('labels/'): #生成的label放在label目录下
os.makedirs('labels/')
for rootDir,dirs,files in os.walk(TrainLabel_Dir):
for file in files:
file_name = file.split('.')[0]
out_file = open('labels/%s.txt'%(file_name),'w')
in_file = open("%s/%s"%(rootDir,file))
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
bb = convert((w,h), b)
out_file.write("0" + " " + " ".join([str(a) for a in bb]) + '\n') #我这里只有一类,所以类别只是0.
out_file.close()
这样我们就生成了YOLO格式的标记文件了。如果按照上面的python代码,生成的标记文件在当前路径的label目录下。
用此方法分别生成train和val的标记文件
1.2 生成原图片绝对路径文件
在YOLO的训练中,还需要一个txt文件来记录所有待训练图片的绝对路径。即train文件夹下所有图片的绝对路径,python代码如下:
- import xml.etree.ElementTree as ET
- import pickle
- import os
- from os import listdir, getcwd
- from os.path import join
- TrainDir = '/home/ubuntu/data/train' #训练文件所在目录
- out_file = open('train.txt','w') #生成的txt文件
- for root,dirs,files in os.walk(TrainDir):
- for file in files:
- out_file.write('%s/%s\n'%(root,file))
- out_file.close()
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
TrainDir = '/home/ubuntu/data/train' #训练文件所在目录
out_file = open('train.txt','w') #生成的txt文件
for root,dirs,files in os.walk(TrainDir):
for file in files:
out_file.write('%s/%s\n'%(root,file))
out_file.close()
用此方法分别生成train和val的txt文件。
1.3 将原图片和标记文件放在一起
YOLO是直接通过替换原图片绝对路径的后缀名来找到对应标记文件的。比如原图片的绝对路径为/home/ubuntu/data/train/1.jpg。则YOLO将会直接认为其对应的标记文件路径为/home/ubuntu/data/train/1.txt。所以,我们将之前1.1生成的标记文件放到对应的原图片目录下。
2. 修改文件
2.1 创建names文件
在YOLO主目录的data文件夹下,创建一个.names文件,文件名任意。比如mydata.names。在该文件中写入所有类别的名称,每一类占一行。比如我这里只检测行人这一类,那么只在第一行写上"person"即可。
2.2 修改data文件
接下来我们要做的是修改YOLO的cfg文件。在darknet的主目录下,进入cfg目录,找到voc.data打开,修改其中的内容
classes= 1 #训练数据的类别数目,我这里只有一类,所以这里是1
train = <path-to-voc>/train.txt #上面1.2步骤生成的train文件路径
valid = <path-to-voc>2007_test.txt #上面1.2步骤生成的val文件路径
names = data/voc.names #上面2.1步骤创建的names文件路径
backup = backup #这是训练得到的model的存放目录,建议自己修改。
2.3 修改cfg文件
如果你想应用yolo_voc.cfg网络来训练你的数据,那么你需要修改这个文件中的一些内容。
- [region]层中classes改成你的类别数,我这里只检测行人,所以我改成了classes=1.
- [region]层上方的[convolution]层中,filters的数量改成(classes+coords+1)*NUM。我这里改成了(1+4+1)*5=30.具体可以参考https://groups.google.com/forum/#!topic/darknet/B4rSpOo84yg
2.4 修改src/yolo.c文件
- 第13行改成 char *voc_names[] = {"person"}; //如果你是一类的话,就改成这样,如果你有多类,自己根据需求改。
- 第322行draw_detections函数中,最后一个参数由20改成你的类别数,我这里是1。
- 第354行demo函数中,倒数第三个参数由20改成你的类别数,我这里是1.
- 第17行改成 char *train_images = "<path-to-voc>/train.txt"; //上面1.2步骤生成的train文件路径
- 第18行改成 char *backup_directory = "/home/Ubuntu/Downloads/darknet-master/backup/"; //这个路径自己指定。
- 第121行改成 char *base = "/home/Ubuntu/Downloads/darknet-master/results/comp4_det_test_"; //这个路径自己指定。
- 第123行改成 list *plist = get_paths("<path-to-voc>/val.txt"); //上面1.2步骤生成的val文件路径
- 第209行改成 char *base = "/home/Ubuntu/Downloads/darknet-master/results/comp4_det_test_"; //这个路径自己指定
- 第210行改成 list *plist = get_paths("<path-to-voc>/val.txt"); //上面1.2步骤生成的val文件路径
2.5 修改src/yolo_kernels.cu文件
第62行draw_detections函数最后一个参数由20改成你的类别数,我这里是1.
2.6 修改src/detector.c文件
第368行改成 list *plist = get_paths("<path-to-voc>/train.txt"); #上面1.2步骤生成的train文件路径
第539行option_find_int函数的最后一个参数由20改成1.
2.7 重新编译darknet yolo
cd <darknet_root>
make clean
make -j16
3. 训练
所有的步骤都已经准备好了。最后就是训练了。为了加快训练速度,我们可以下载官方提供的预训练模型。下载地址为
curl -O http://pjreddie.com/media/files/darknet19_448.conv.23
最后,我们cd到darknet的主目录,输入下面的指令来进行训练
./darknet detector train cfg/voc.data cfg/yolo-voc.cfg darknet19_448.conv.23
这其中voc.data就是2.2中修改后的data文件,yolo-voc.cfg是网络结构文件,darknet19_448.conv.23是下载的预训练模型。
整个的训练时间比较长,慢慢等吧。。。。。