写在前面:YOLOV3只有修改了源码才需要重新make,而且make之前要先make clean。
一.准备数据
在/darknet/VOCdevkit1下建立文件夹VOC2007.
voc2007文件夹下建立三个文件夹,分别为Annotations,ImageSets和JPEGImages,其中JPEGImages存放所有.jpg格式的训练图片,Annotations存放所有图片的xml文件
图片最好按数字顺序排列,如00001.jpg,00002.jpg等,可以用脚本生成。
ImageSets文件夹下存放三个子文件夹,Main中存放train.txt,即用于训练的图片名字
二.生成xml文件
xml文件采用labelimg标注
cd labelImg python labelImg.py
先进入labelImg所在路径,然后用python打开.py文件,即可打开标注工具。
三、生成txt文件
因为YOLOV3训练时需要用的是txt文件,因此需要将xml生成对应的txt,可采用voc_label.py脚本。
在利用voc_label.py脚本生成txt文件之前,还需要先生成一个train.txt文件,该文件位于ImageSets文件夹下的Main文件夹中,主要存图片名称(不包含后缀)
可采用以下脚本生成
# -*- coding:utf-8 -*- import sys import os import random data_dir='/home/wmy/darknet/VOCdevkit1/VOC2007/JPEGImages' file_list=[] write_file_name='/home/wmy/darknet/VOCdevkit1/VOC2007/ImageSets/Main/train.txt' write_file=open(write_file_name,'w') for file in os.listdir(data_dir): if file.endswith(".jpg"): write_name=file.split('.')[0] file_list.append(write_name) sorted(file_list) number_of_lines=len(file_list) for current_line in range(number_of_lines): write_file.write(file_list[current_line]+'\n') write_file.close()
运行该脚本将生成存放图片名称的txt文件。
将该脚本放在与VOCdevkit1同等目录下,稍作修改。
import xml.etree.ElementTree as ET import pickle import os from os import listdir, getcwd from os.path import join sets=[(', 'train')] #根据数据修改 classes = ["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('VOCdevkit1/VOC%s/Annotations/%s.xml'%(year, image_id)) #修改为数据路径 out_file = open('VOCdevkit1/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('VOCdevkit1/VOC%s/labels/'%(year)): #修改 os.makedirs('VOCdevkit1/VOC%s/labels/'%(year)) #修改 image_ids = open('VOCdevkit1/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/VOCdevkit1/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id)) #修改 convert_annotation(year, image_id) list_file.close()
脚本中标注部分需修改,主要修改数据标签、路径。
运行改程序,将在VOCdevkit1同等路径下生成2007_train.txt文件。
四、修改配置文件
1.修改data/voc.names ,我的数据为1类,‘person'
2.修改cfg/voc.data ,主要修改类别,训练数据路径
3.修改yolov3-voc.cfg,修改class=1,将最后一个卷积层中的filter修改为18(计算公式为filter=(class+5)*3,yolov2中的计算公式为5*(5+classes))
五、训练
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
在终端输入该命令即可开始训练,训练模型每迭代1000保存一次,保存于backup文件夹内。
训练完成后的模型讲保存在backup文件夹。
每1000次迭代保存一次模型,最后模型为yolov3-voc_final.weights。
测试模型:
./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_final.weights data/4.jpg
终端输入上述命令,运行模型,得出单张检测结果。
./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_final.weights
输入上述命令,可得多张检测结果。
参数解释:
1.batch
每一次迭代送到网络中的图片数量。增大该值可以使网络在较少的迭代次数内完成一个epoch。增大该值会延长训练时间,但会的更好的寻找梯度下降方向;减小该值可能会导致训练陷入局部最优,或不收敛。
2.subdivision
让每一个batch分为subdivision份丢入网络。降低对显存的占有情况。
3.angle
图片旋转角度,可通过旋转增大数据集
4.saturation exposure hue
分别是饱和度,曝光度和色调,都是增大数据集的
5.max_batches
最大迭代次数
6.policy
学习策略
7.random
设置为1时,在训练时每一batch图片会随机的改成320-640的倍数。目的也是增大数据集
8.Region Avg IOU
预测的bbx与实际bbx的交集,越接近1越好
训练时各参数意义:
region 82 avg IOU:当前尺度当前图片的平均IOU,预测矩形框与真实矩形框之比,越接近1越好。82表示最大尺度,yolov3在三个尺度和那个检测目标,分别是82,94和106,分别表示最大预测尺度/中间预测尺度和最小预测尺度,不同预测尺度分别使用不同的mask。
class:标注物体分类的正确率,越接近1越好。
obj:越接近1越好。
no obj:越小越好,但不能为0。
.5R:以0.5为阈值时的recall,指当前模型在所有subvision图片中检测出的正样本与实际的正样本的比值,全部被正确检测则值为1,因此越接近1越好。
.7R:以0.7为阈值时的recall,recall指的是检测出的正样本/实际正样本。
count:所有当前subvision图片中包含正样本的图片数量。
voc数据集在差不多迭代5w次才能达到初步效果。
因为VOC数据集和coco数据集复杂,种类繁多,因此配置不高的时候,尽量采用小数据集,或者手动标注一个数据集。
YOLOV3中mask的作用:每一层都必须知道所有的锚箱(先验框),但是只预测其中的一个子集,第一个yolo层预测6,7,8,因为这是最大的箱子,最粗糙,第二个yolo层预测4,5,6,稍微小一些,第三个yoo层预测1,2,3,最精细。 这样做的目的是为了更好的检测目标,也提升了小目标的检测率,提升了检测速度。
训练中出现nan,是因为没有检测到目标,不是错误,但是还是要尽可能的减少nan的出现次数,据说适当增大batch大小,可以减少nan的出现,等gpu到了以后可以尝试一下。
锚的作用:在对真实标签进行聚类以后,发现大多数的边界框(bbx)具有一定的高宽比,因此yolo在预测边界框的时候,不是盲目的,而是选用特定的高宽比的预测框,这些预测框被称作锚
网络在预测的时候,不是在预测对象的最终大小,而是调整最近锚点的大小以适应对象。
锚的大小取决于网络输入的大小,而不是输出。在yolov2中,锚的大小不是实际像素值,yolov3的锚点大小就是实际像素大小。