复现YOLO

来源:互联网 发布:珠海网络推广服务 编辑:程序博客网 时间:2024/05/17 17:55

需要ubuntu 14.04, opencv2.4.10, cuda7.5,复现详情参考http://pjreddie.com/darknet/yolo/,结果如下
这里写图片描述

怎么训练自己的数据?
首先要把自己的数据制作成VOC格式的,具体步骤如下:
(1)在darknet/scripts/下建立数据及文件夹,如下所示

--VOC2007    --Annotations      --ImageSets        --Main        --train.txt      --JPEGImages      --原始图片(类似000001.jpg这种格式) 

train.txt里面放的是所有训练图片的名称(不含后缀和路径),这里需要重命名原始图片的名字以及把重命名后的名字写入train.txt两个操作,下面给出我的代码:
重命名

import osrootdir = "/home/yy/darknet/scripts/VOCdevkit/VOC2007/JPEGImages" #改为自己的目录prefix_1 = "0"prefix_2 = "00"prefix_3 = "000"prefix_4 = "0000"for parent, dirnames, filenames in os.walk(rootdir):  for filename in filenames:    if len(filename) == 5:      os.rename(os.path.join(parent, filename), os.path.join(parent, prefix_4 + filename))    if len(filename) == 6:      os.rename(os.path.join(parent, filename), os.path.join(parent, prefix_3 + filename))    if len(filename) == 7:      os.rename(os.path.join(parent, filename), os.path.join(parent, prefix_2 + filename))    if len(filename) == 8:      os.rename(os.path.join(parent, filename), os.path.join(parent, prefix_1 + filename))    

写入traix.txt

import osrootdir = "/home/yy/darknet/scripts/VOCdevkit/VOC2007/JPEGImages" #改为自己目录for parent, dirnames, filenames in os.walk(rootdir):  with open("/home/yy/darknet/scripts/VOCdevkit/VOC2007/ImageSets/Main/train.txt", "w") as f:  #改为自己目录    for filename in filenames:      f.write(filename[:-4])      if filename != filenames[-1]:        f.write("\n")

完成后需要标注数据,这里推荐https://github.com/tzutalin/labelImg工具,简单好用,生成的XML文件保存在Annotations下,需要注意的是生成的xml文件中的filename是不带后缀名的,如下:

<annotation>  <folder>JPEGImages</folder>  <filename>000001</filename>  <path>/home/yy/VOC2007/JPEGImages/000001.jpg</path>  <source>    <database>Unknown</database>  </source>  <size>    <width>270</width>    <height>187</height>    <depth>3</depth>  </size>  <segmented>0</segmented>  <object>    <name>DaLai</name>    <pose>Unspecified</pose>    <truncated>0</truncated>    <difficult>0</difficult>    <bndbox>      <xmin>95</xmin>      <ymin>36</ymin>      <xmax>207</xmax>      <ymax>167</ymax>    </bndbox>  </object></annotation>

我们要自己添加后缀名,在Annotations目录下执行

find -name '*.xml' |xargs perl -pi -e 's|</filename>|.jpg</filename>|g'

这个命令就行了。完成后我们需要修改darknet/scripts/下的voc_label.py文件,如下:

import xml.etree.ElementTree as ETimport pickleimport osfrom os import listdir, getcwdfrom os.path import join# sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]# classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]sets = [('2007', 'train')] #年份改为自己设置的classes = ["DaLai", "NonDaLai"] #类别改为自己数据的类别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(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 image_id in image_ids:        list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))        convert_annotation(year, image_id)    list_file.close()

完成后运行voc_label.py,会在VOC2007下生成labels文件夹,里面放的是每张图片归一化后的box和类别cls,如下:

0 0.559259259259 0.542780748663 0.414814814815 0.700534759358

第一个是类别,后四个是x,y,w,h(x,y是box的中心坐标,w,h是box的寬高)这里的xywh都是归一化后的值,归一化的具体操作如下:

dw = 1./wdh = 1./hx = x*dwy = y*dhw = w*dwh = h*dh

最后需要修改cfg文件和voc.data文件,首先是cfg,这里以yolo.cfg为例:

[net]batch=1subdivisions=1width=416height=416channels=3momentum=0.9decay=0.0005angle=0saturation = 1.5exposure = 1.5hue=.1learning_rate=0.001max_batches = 120000policy=stepssteps=-1,100,80000,100000scales=.1,10,.1,.1上面是网络参数,可以根据需要更改[convolutional]size=1stride=1pad=1filters=425  #最后一个卷积层的卷积核的数量改为num*(classes + 1 + coords) 我的classes是2所以 这里filters = 5*(2+1+4)=35activation=linear[region]anchors = 0.738768,0.874946,  2.42204,2.65704,  4.30971,7.04493,  10.246,4.59428,  12.6868,11.8741bias_match=1classes=2  #改为自己的类别数coords=4num=5softmax=1jitter=.2rescore=1object_scale=5noobject_scale=1class_scale=1coord_scale=1absolute=1thresh = .6random=0

接下来修改darknet/cfg/voc.data,如下:

classes= 2 #改为自己的类别数train  = /home/yy/darknet/scripts/2007_train.txt #训练集的路径//valid  = /home/pjreddie/data/voc/2007_test.txt #验证集路径names = data/voc.namesbackup = /home/yy/darknet/results/ #存放最终model的地方

所有步骤完成后可以开始训练了在darknet下执行

./darknet detector train ./cfg/voc.data cfg/tiny-yolo-voc.cfg

也可以用官网的预训练好的权重初始化

./darknet detector train ./cfg/voc.data cfg/tiny-yolo-voc.cfg darknet.conv.weights

训练时的样子是这样的
这里写图片描述

如果报nvcc not found错误需要把Makefile里的NVCC修改为自己的路径,如下:

NVCC = nvcc改为NVCC=/usr/local/cuda-7.5/bin/nvcc 

如果报CUDA error:invalid device function则是因为gpu的架构错了,修改如下:

ARCH= -gencode arch=compute_52,code=compute_52默认是TITAN X的52,需要修改成你自己的GPU的架构

详见http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#virtual-architecture-feature-list

如果报CUDA error:out of memory 则是GPU的显存不够,我就是这种情况,要么升级显卡,要么改用cpu训练,楼主这里改用cpu训练,改用cpu训练需要把Makefile里的GPU=1改为GPU=0并重新编译,CPU的速度比GPU慢了至少100倍,奈何没有钱啊。。慢慢训练吧

YOLO的map较之ssd和frcn系较低的原因可能是分类需要的平移不变性和边框回归需要的平移变换性的矛盾,所以最好把这两个任务分成两个子网络完成

0 0