DarkNet-YOLOv3 训练自己的数据集 Ubuntu16.04+cuda8.0

DarkNet-YOLOv3 训练自己的数据集 Ubuntu16.04+cuda8.0

  • 配置DarkNet

参考YOLO官网

(1)安装相关依赖项(可选)

  1. opencv:参考教程
  2. cuda:参考教程

(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

编辑于 2018-05-02 19:57