DarkNet-YOLOv3 训练自己的数据集 Ubuntu16.04+cuda8.0
- 配置DarkNet
参考YOLO官网
(1)安装相关依赖项(可选)
(2)下载源码:
git clone https://github.com/pjreddie/darknet
cd darknet
(3)更改Makefile文件:
vim Makefile
如果用GPU运行:
GPU=1
如果安装了CUDNN:
CUDNN=1
NVCC=/usr/local/cuda-8.0/bin/nvcc
如果安装了opencv:
OPENCV=1
(4)编译
make
配置完以后可以下载作者的预训练模型测试一下:
wget https://pjreddie.com/media/files/yolov3.weights
下载之后用图片进行测试:
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
一切正常的话会输出测试图片的检测结果:
- 用自己打数据集进行训练
(1)数据集处理
我做的项目是检测水面上的物体,一共5类:客船、货船、小船、帆船、浮标,每类大概500张图,并且我用类似labelimg的工具对图片进行了标注,这里附上大神的labelimg的github链接。需要的可以自行下载使用。
用标注工具标注完图片,先将数据集准备成类似VOC的格式,需要三个文件夹中的两个。JPEGImages:用于存放所有的图片,格式为.jpg ; Annotations:用于存放与图片对应的XML文件。
(2)生成训练所需要的txt文件
用YOLO训练时候需要一些txt文件,以我的项目为例,有tarin.txt, val.txt, boat_train.txt, boat_val.txt.还有一个labels文件夹,里面装着每张图片对应的txt文件,名字与图片命名相同。下面分别说明一下这些txt文件的生成方式以及里面的内容。
train.txt:存放用于训练的图片的名字,每行一个名字(不带后缀.jpg)。
val.txt:存放用于验证的图片的名字,每行一个名字(不带后缀.jpg)。
boat_train.txt:存放用于训练的图片的绝对路径,每行一个路径。
boat_val.txt:存放用于验证的图片的绝对路径,每行一个路径。
labels文件夹的txt:每个文件存放的是对应图片的标注信息,每行一个目标,若有多行则表示读应图片上有多个目标。
下面给一段能利用train.txt,val.txt以及xml文件来生成boat_train.txt,boat_val.txt以及labels文件夹下所有txt文件的python代码,我直接用voc_label.py改的,改的比较粗糙:
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
#我的项目中有5个类别,类别名称在这里修改
classes = ["ship","cargoboat","boat","sailboat","buoy"]
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)
def convert_annotation(image_id):
#这里改为xml文件夹的路径
in_file = open('/home/wlin/darknet/boat_detect/Annotations/%s.xml'%(image_id))
#这里是生成每张图片对应的txt文件的路径
out_file = open('/home/wlin/darknet/boat_detect/labels/%s.txt'%(image_id),'w')
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'):
cls = obj.find('name').text
if cls not in classes :
continue
cls_id = classes.index(cls)
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(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
#这里是train.txt文件的路径
image_ids_train = open('/home/wlin/darknet/boat_detect/train.txt').read().strip().split()
#这里是val.txt文件的路径
image_ids_val = open('/home/wlin/darknet/boat_detect/val.txt').read().strip().split()
list_file_train = open('boat_train.txt', 'w')
list_file_val = open('boat_val.txt', 'w')
for image_id in image_ids_train:
#这里改为样本图片所在文件夹的路径
list_file_train.write('/home/wlin/darknet/boat_detect/JPEGImages/%s.jpg\n'%(image_id))
convert_annotation(image_id)
list_file_train.close()
for image_id in image_ids_val:
#这里改为样本图片所在文件夹的路径
list_file_val.write('/home/wlin/darknet/boat_detect/JPEGImages/%s.jpg\n'%(image_id))
convert_annotation(image_id)
list_file_val.close()
(3)两个很重要的文件夹的放置位置
这里指的是JPEGImages(存放样本图片)与labels(与每张图片对应的txt文件),建议这两个文件夹放在同一个目录下,因为好像代码里面是在同一个目录下搜索这两个文件夹,然后就是名字也最好别变,不然可能需要修改相应代码。
(4)修改相应文件
1.在项目目录下建立一个.names文件(也可以把data文件夹中的voc.names复制过来修改并重命名),比如我的叫做boat_voc.names,里面存放类别名,每行存一个。
2.把cfg文夹中的voc.data复制到自己项目目录下,改名为boat_voc.data,并修改:
classes= 5 #类别数
train = /home/wlin/darknet/boat_detect/boat_train.txt #boat_train.txt路径
valid = /home/wlin/darknet/boat_detect/boat_val.txt #boat_val.txt路径
names = /home/wlin/darknet/boat_detect/boat_voc.names #boat_voc.names路径
backup = /home/wlin/darknet/boat_detect/backup/ #建一个backup文件夹用于存放中间结果
3.把cfg文夹中的yolov3-voc.cfg复制到自己项目目录下,并修改:
[net]
# Testing
# batch=1
# subdivisions=1 #训练时候把上面Testing的参数注释
# Training
batch=64
subdivisions=32 #这个参数根据自己GPU的显存进行修改,显存不够就改大一些
... #因为训练时每批的数量 = batch/subdivisions
...
...
learning_rate=0.001 #根据自己的需求还有训练速度学习率可以调整一下
burn_in=1000
max_batches = 30000 #根据自己的需求还有训练速度max_batches可以调整一下
policy=steps
steps=10000,20000 #跟着max_batches做相应调整
...
...
...
[convolutional]
size=1
stride=1
pad=1
filters=30 #filters = 3*(classes + 5)
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=5 #修改类别数
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1 #显存小的话 =0
#这个文件的最下面有3个YOLO层,这里我才放上来了一个,这三个地方的classes做相应修改
#每个YOLO层的上一层的convolutional层的filters也要修改
4.下载预训练模型(权重)
在项目目录下打开终端,运行命令:
wget https://pjreddie.com/media/files/darknet53.conv.74
5.训练
到此,训练所需的文件就准备好了,可以尝试着开始训练,在darknet目录下执行:
./darknet detector train boat_detect/boat_voc.data boat_detect/yolov3-voc.cfg darknet53.conv.74
这个命令可能跟大多数博主的不同,原因是我没有直接更改一些文件,而是把他们复制出来放在我的项目目录里重命名后再修改,我的项目文件夹是boat_detect,根据你们自己的文件夹在相应地方做修改,我觉得这样做能避免下次使用时候重复修改相同的文件,造成混乱。
- 测试自己训练的模型
我的训练集有大概2500图片,显卡是1080,max_batch = 30000,训练了40多个小时把模型训练出来了,最后的模型存在 /backup目录下。
(1)单张图片测试:
在darknet目录下打开终端,运行:
./darknet detector test ./boat_detect/boat_voc.data ./boat_detect/yolov3-voc.cfg ./boat_detect/backup/yolov3-voc_30000.weights ./boat_detect/test_data/test_img.jpg
效果还是挺好的:
(2)视频测试:
在darknet目录下打开终端,运行:
./darknet detector demo ./boat_detect/boat_voc.data ./boat_detect/yolov3-voc.cfg ./boat_detect/backup/yolov3-voc_30000.weights ./boat_detect/test_data/boat_test.mp4
由于原版代码中检测视频结果不能保存,如果要保存结果则需要修改下源码,我找到了一篇博文亲测可行(我只测试过yolo3,V1、V2没测过)。
修改源码后,重新编译:
make clean
make
然后再次用视频做检测时,检测结果就会保存了。
- 遇到的问题与解决方案
./darknet: error while loading shared libraries: libcudnn.so.6.0: cannot open shared object file: No such file or directory
网站上搜索了一下大多数博主的解释是libcudnn.so.6.0的权限问题,需要删除软链接后修改权限再次建立新打软链接,可参考这篇教程。
我跟同学讨论后的解决方案比较粗暴:
(1)改变libcudnn.so.6.0的权限(我这里给了所有权限,可以根据自己需要调整):
cd /usr/local/cuda/lib64/
sudo chmod 777 libcudnn.so.6.0
(2)把libcudnn.so.6.0的路径添加到bashrc中:
sudo vim ~/.bashrc
在最下面添加libcudnn.so.6.0的路径:
export LD_LIBRARY_PATH='/usr/local/cuda/lib64/'
(3)跟新下bashrc,然后就能运行了:
sudo source ~/.bashrc