Darknet YOLOv3-tiny ubuntu配置,训练自己数据集(行人检测)及调参总结

时间:2024-05-23 20:51:14

YOLO 官网:https://pjreddie.com/darknet/yolo/

论文:https://pjreddie.com/media/files/papers/YOLOv3.pdf

一.介绍

由于项目需求,需在树莓派下进行行人检测。树莓派内存较小,计算能力不足。opencv自带的HOG+SVM算法精度不够,而基于回归的深度学习检测算法YOLO精度高,计算要求小。

考虑使用YOLOv3-tiny算法进行行人检测,本文主要包括Ubuntu下的YOLOv3配置,训练自己的行人数据集以及调参总结。

参考了很多博客,亲测实验,如果有误请及时指正。

二.ubuntu下配置

其在Ubuntu下配置基本按照官网提供的步骤即可。

下载源码及CPU下编译:

git clone https://github.com/pjreddie/darknet
cd darknet
make

下载tiny权重文件及测试:

wget https://pjreddie.com/media/files/yolov3-tiny.weights
./darknet detect cfg/yolov3-tiny.cfg yolov3-tiny.weights data/dog.jpg

其权重文件和测试结果图片都在根目录下。

三.训练行人检测器

相信大家都能跑通官方权重,但其检测器是20个类别,在实际使用中并不需要全部类别,且类别多精度会下降。我们需要单独训练自己的检测器,比如人脸检测,路标检测等,我这里是做行人检测。

3.1标注数据集

我们采用PASCAL VOC格式的数据集,其标注格式较为复杂,幸好有前人写过自动生成的python程序,即labelImg工具,具体使用请转:https://github.com/tzutalin/labelImg

3.2生成训练集的标注文件

i>官网提供了原数据集:

wget https://pjreddie.com/media/files/VOCtrainval_11-May-2012.tar
wget https://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
wget https://pjreddie.com/media/files/VOCtest_06-Nov-2007.tar
tar xf VOCtrainval_11-May-2012.tar
tar xf VOCtrainval_06-Nov-2007.tar
tar xf VOCtest_06-Nov-2007.tar

ii>我们需要将自己标注的数据放进来。

jpg图像放到/VOCdevkit/VOC2007/JPEGImages/中,xml文件放到/VOCdevkit/VOC2007/Annotations/中。

在/VOCdevkit/VOC2007/ImageSets/Main/person_trainval.txt文件格式如下

000005 -1 #前面代表图片名称,后面-1表示负样本,1表示正样本
000007 -1
000009  1
000012 -1

我们需将自己数据集按着这个格式copy到person_trainval.txt文件中。如:

009959 -1
009961 -1
1(1)  1
1(10)  1

windows下批量生成该格式,请参考https://zhidao.baidu.com/question/292884446.html

生成txt文件之后,没有1,-1的标志,需手动添加,或者word打开替换功能将“段落标识符”替换为“ 1 段落标识符”(本人使用了word的替换功能)。

iii>生成训练集的标注文件时需要下载:

wget https://pjreddie.com/media/files/voc_label.py

但是本文单独训练行人检测器,需修改voc_label.py文件:

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join

sets=[('2012', 'person_trainval'), ('2007', 'person_trainval')]  #只选取含有行人的数据

classes = ["person"]  #修改类别,只有person
def convert(size, box):
    dw = 1./(size[0])
    dh = 1./(size[1])
    x = (box[0] + box[1])/2.0 - 1
    y = (box[2] + box[3])/2.0 - 1
    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(year, image_id):
    in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
    out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, 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'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult)==1:
            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')

wd = getcwd()

for year, image_set in sets:
    if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)):
        os.makedirs('VOCdevkit/VOC%s/labels/'%(year))
    image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
    list_file = open('%s_%s.txt'%(year, image_set), 'w')
    
    for i in range(len(image_ids)):
        if image_ids[i]=='1':    #我们只训练正样本
            list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_ids[i-1]))
            convert_annotation(year, image_ids[i-1])
    list_file.close()

os.system("cat 2007_person_trainval.txt 2012_person_trainval.txt  > train.txt")
os.system("cat 2007_person_trainval.txt 2012_person_trainval.txt  > train.all.txt")

 该行if image_ids[i]=='1':表示只训练正样本:请查看/home/ps/darknet/train/VOCdevkit/VOC2007/ImageSets下的person_trainval.txt文件,后面的'1'代表正样本。

3.3修改配置文件

i>修改data/voc.names文件,只保留person一行,其余行删除。

ii>修改cfg/voc.date文件:

classes= 1
train  = /home/ps/darknet/train/train.txt
valid  = /home/ps/darknet/train/2007_test.txt
names = data/voc.names
backup = backup

iii>修改cfg/yolov3-tiny.cfg文件:

找到文件中类似代码段(yolo层附件),共2处:

[convolutional]
size=1
stride=1
pad=1
filters=18
activation=linear

[yolo]
mask = 3,4,5
anchors = 10,14,  23,27,  37,58,  81,82,  135,169,  344,319
classes=1
num=6
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=0

需要改变filters为 3*(classes+1+4),原因为:https://github.com/pjreddie/darknet/issues/582,同时修改下面的classes的种类。

3.4训练

重点之处:需先加载预训练模型,而官网没给tiny版的预训练模型,搜寻半天发现可以在原yolov3-tiny.weights上得到,只需如下指令。参考https://blog.****.net/chengyq116/article/details/83213699

./darknet partial ./cfg/yolov3-tiny.cfg ./yolov3-tiny.weights ./yolov3-tiny.conv.15 15

然后训练:

sudo ./darknet detector train cfg/voc.data cfg/yolov3-tiny.cfg yolov3-tiny.conv.15

3.5结果展示

Darknet YOLOv3-tiny ubuntu配置,训练自己数据集(行人检测)及调参总结

可以看出,500多代后loss很小:2.070119,表明算法收敛。其训练指标具体含义参考https://blog.****.net/lilai619/article/details/79695109

Darknet YOLOv3-tiny ubuntu配置,训练自己数据集(行人检测)及调参总结

四.调参

训练时需调节参数,cfg/yolov3-tiny.cfg参数含义如下:

[net]
# Testing           #测试时取消注释
#batch=1
#subdivisions=1
# Training          #训练时取消注释
batch=32            #训练批次大小
subdivisions=4      #测试时批次分组,即一批32张图片,分4组训练
width=416
height=416          
channels=3          #图片大小
momentum=0.9        #动量参数
decay=0.0005
angle=0             #通过旋转角度来生成更多训练样本
saturation = 1.5    #调整饱和度来生成更多训练样本
exposure = 1.5      #调整曝光量来生成更多训练样本
hue=.1              #调整色调来生成更多训练样本


learning_rate=0.001   #学习率,0.01-0.001
burn_in=50            #前50代不使用steps策略调整学习率
max_batches = 600     #总训练代数
policy=steps          #选取的训练策略   参考https://nanfei.xyz/2018/01/23/YOLOv2%E8%B0%83%E5%8F%82%E6%80%BB%E7%BB%93/
steps=200,400        
scales=.1,.1          #steps策略表示,200代之后学习率缩小0.1,400代之后再缩小0.1

调参中,burn_in需要适当选取,因为小于它时学习率慢慢上升到你设定的值。其后steps两次,学习率依次减小。

在训练过程中可能出现其他问题,参考https://blog.****.net/Pattorio/article/details/80051988