Ubuntu16.04
GetForce MX250 4G
CUDA 9.0
CuDNN 7.0.5
git clone https://github.com/pjreddie/darknet
cd darknet
GPU=1 #可选0/1
CUDNN=1 #可选0/1
OPENCV=1 #可选0/1
OPENMP=0
DEBUG=0
make
# yolov3.weights
wget https://pjreddie.com/media/files/yolov3.weights
# yolov-tiny.weight
wget https://pjreddie.com/media/files/yolov3-tiny.weights
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
./darknet detect cfg/yolov3-tiny.cfg yolov3-tiny.weights data/dog.jpg
成功后说明环境和代码没问题,即可进行下一步。
Yolov3 darknet: ./src/cuda.c:36: check_error: Assertion ‘0’failed.
或CUDA Error: out of memory darknet: ./src/cuda.c:36: check_error: Assertion ’0‘ failed.
等报错batchsize
(后面会详细讲设置)---VOCdevkit
---VOC2007
---Annotations # 存放xml文件
---ImageSets
---Main # 为了方便后续步骤中得到训练和测试图片的索引号
---JPEGImages # 存放对应的图片文件
---Labels # Yolo自带的脚本会生成此文件夹,不需提前创建。yolov生成的文件夹名称为‘labels’
import os
import random
trainval_percent = 0.66 #可以自己修改
train_percent = 0.9 #可以自己修改
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
total_xml = os.listdir(xmlfilepath)
num=len(total_xml)
list=range(num)
tv=int(num*trainval_percent)
tr=int(tv*train_percent)
trainval= random.sample(list,tv)
train=random.sample(trainval,tr)
ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')
for i in list:
name=total_xml[i][:-4]+'\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
sets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
classes = ["Tire"]
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('fisheyedata/Tiredfishevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
out_file = open('fisheyedata/Tiredfishevkit/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('fisheyedata/Tiredfishevkit/VOC%s/labels/'%(year)):
os.makedirs('fisheyedata/Tiredfishevkit/VOC%s/labels/'%(year))
image_ids = open('fisheyedata/Tiredfishevkit/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/fisheyedata/Tiredfishevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
convert_annotation(year, image_id)
list_file.close()
os.system("cat 2007_train.txt 2007_val.txt > train.txt")
os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt > train.all.txt")
<object-class-id> <center-x> <center-y> <width> <height>
object-class-id是类别id,这里只有雪人一类,故这里统一为1,
center-x是目标的中心x坐标并除以图像宽度归一化了,
center-y是目标的中心y坐标并除以图像高度归一化了,
width为目标的宽度并除以图像宽度归一化了,
height为目标的宽度并除以图像高度归一化了。
修改data/voc.names
注意:一定要和之前配置文件中的名称保持一致,注意大小写也要一致。
一行一个类别名称。
修改cfg/voc.data
classes= 1 # 数据集类别数
train = ~/train.txt # train.txt所在路径
valid = ~/2007_test.txt # 2007_test.txt所在路径
names = data/voc.names # voc.names所在路径
backup = backup
将如下的Training参数打开,关闭Testing参数
[net]
#Testing #注释
#batch=1 #注释
#subdivisions=1 #注释
#Training
batch=64
subdivisions=16
batch 批尺寸,每一次迭代送到网络的图片数量。batch * iteration得到的就是一个epoch内训练的样本数。增大这个可以让网络在较少的迭代次数内完成一个epoch。在固定最大迭代次数的前提下,增加batch会延长训练时间,但会更好的寻找到梯度下降的方向。如果你显存够大,可以适当增大这个值来提高内存利用率。这个值是需要大家不断尝试选取的,过小的话会让训练不够收敛,过大会陷入局部最优。
subdivisions 如果内存不够大,会将batch分割为subdivisions个子batch,每个子batch大小为batch/subdivisions。子batch一份一份的跑完后,在一起打包算作完成一次iteration。这样会降低对显存的占用情况。如果设置这个参数为1的话就是一次性把所有batch的图片都丢到网络里,如果为2的话就是一次丢一半。
找到如下位置(以yolo为关键字),修改filters和classes,整个文本共有2个filters和2个classes需要修改
[convolutional]
size=1
stride=1
pad=1
filters=18 #### 改为3*(classes +5)
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 #多尺度训练,显存小的建议改为0
# 获取yolov3-tiny.conv.15
./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]
count
是表示当前层与真实label正确配对的box数。
其中所有参数都是针对这个值的平均值,除no obj外,不过从代码上来,这个参数意义并不大,所以当前yolo层如果出现nan这个的打印,也是正常的,只是表示当前batch刚好所有图片都是大框或是小框,所以提高batch的数目可以降低nan出现的机率。
Avg IOU
表示当前层正确配对的box的交并比的平均值
Class
表示表示当前层正确配对类别的平均机率
Obj
表示confidence = P(object)* IOU,表示预测box包含对象与IOU好坏的评分
0.5R/0.7R
表示Iou在0.5/0.7上与正确配对的box的比率
训练过程中的参数含义如下(https://blog.csdn.net/lilai619/article/details/79695109):
Avg IOU:
当前迭代中,预测的box与标注的box的平均交并比,越大越好,期望数值为1;
Class:
标注物体的分类准确率,越大越好,期望数值为1;
obj:
越大越好,期望数值为1;
No obj:
越小越好;
.5R:
以IOU=0.5为阈值时候的recall; recall = 检出的正样本/实际的正样本
0.75R:
以IOU=0.75为阈值时候的recall;
count:
正样本数目。
训练得到的模型文件位于/backup文件中,源码中(examples/detector.c)当迭代小于1000时每隔100次保存一次模型,当大于1000时每10000次迭代保存一次模型,可以自行修改保存规则。
以10000迭代得到的模型为例测试:
./darknet detector test cfg/voc.data cfg/yolov3-tiny.cfg backup/yolov3-tiny_10000.weights VOCdevkit/VOC2007/JPEGImages/000022.jpg
① 断点重新训练
如果从某次断了重新开始训练,只需要把 yolov3-tiny.weights换成你的某一次的weights即可
例如:把命令行中的参数 yolov3-tiny.weights改成 yolov3-tiny_1000.weights 即可
② 修改cfg/xxx.cfg,首先修改分类数为自己的分类数,然后注意开头部分训练的batchsize和subdivisions被注释了,如果需要自己训练的话就需要去掉,测试的时候需要改回来,最后可以修改动量参数为0.99和学习率改小,这样可以避免训练过程出现大量nan的情况
③ 若出现显存不足,可修改batch的大小和取消random多尺度,默认情况下random=1,取消将random=0(需要修改几处)
④GPU动态监控
新打开一个终端,输入:
watch -n 1 -d nvidia-smi
其中各部分的含义为:
参考资料:
[1] Darknet yolov3-tiny 训练自己的数据集步骤
[2] 实用教程!使用YOLOv3训练自己数据的目标检测
[3] yolov3-tiny模型训练参数和训练自己的数据