参考:https://github.com/AlexeyAB/darknet
记录以下流程和错误,之后再用到,查询起来也快些。
翻译了部分参照文档,并按照流程走的~
提前工作有:
1.克隆了darknet代码;
2.数据已经标注完毕。且生成了对应的标签数据集
标注工具使用了labelme,生成的是json文件,需要转换为txt格式的。
将labelme标记的.json
数据,转为yolov4训练用的.txt
数据。
图片和json文件保存在obj文件夹里。
python代码:
from os import getcwd
import numpy as np
import os
import json
import glob
wd = getcwd()
"labelme标注的json数据集转为keras yolo的txt训练集"
classes = ["car","pedestrian","truck","bus"] #修改为待检测的类别名
image_ids = glob.glob(r"obj/*.jpg") #jpg和json文件都在文件夹obj/里
print(image_ids)
def convert_annotation(image_id):
jsonfile=open('%s.json' % (image_id))
in_file = json.load(jsonfile)
#print(in_file)
height=in_file["imageHeight"]
width=in_file["imageWidth"]
size=[width,height]
list_file = open('%s.txt'%(image_id.split('.')[0]), 'w')
for i in range(0,len(in_file["shapes"])):
object=in_file["shapes"][i]
cls=object["label"]
points=object["points"]
dw = 1./(size[0])
dh = 1./(size[1])
min_x=min_y= np.inf
max_x = max_y = 0
for x, y in points:
min_x = min(min_x, x)
min_y = min(min_y, y)
max_x = max(max_x, x)
max_y = max(max_y, y)
x=(min_x+max_x)/2.0
print(x)
y=(min_y+max_y)/2.0
print(y)
w=max_x-min_x
h=max_y-min_y
x = x*dw
w = w*dw
y = y*dh
h = h*dh
if cls not in classes:
print("cls not in classes")
continue
cls_id = classes.index(cls)
b = (x, y, w, h)
list_file.write(str(cls_id)+" "+" ".join([str(a) for a in b]) )
list_file.write('\n')
list_file.close()
jsonfile.close()
for image_id in image_ids:
# list_file.write('%s.jpg' % (image_id.split('.')[0]))
convert_annotation(image_id.split('.')[0])
该脚本程序会为每个
.jpg
文件生成一个.txt
文件,而且都在obj目录下。
按照比例,划分训练集和测试集合,这里是9:1划分的:
gene train txt代码:
# coding: utf-8
import os
paths = "obj" # 测试图片的路径
f = open('train.txt', 'w') # 保存图片路径的txt文件
filenames = os.listdir(paths)
# 只获取jpg文件
files=[]
for file in filenames:
if file.split('.')[-1]=='txt':
continue
else:
files.append(file)
# 生成测试集
cnt=0
st=[1,2,3,4,5,6,7,8,9]
for filename in files:
cnt+=1
cnt=cnt%10
if cnt not in st:
cnt=0
continue
#print(cnt)
out_path = "data/obj/" + filename # 引号内为测试图片文件夹的路径
print(out_path)
f.write(out_path + '\n')
f.close()
gene test txt代码:
# coding: utf-8
import os
paths = "obj" # 这里保存图片
f = open('test.txt', 'w') # 最后得到的图片路径txt文件
filenames = os.listdir(paths)
# 只获取jpg文件
files=[]
for file in filenames:
if file.split('.')[-1]=='txt':
continue
else:
files.append(file)
# 生成测试集
cnt=0
st=[1,2,3,4,5,6,7,8,9]
for filename in files:
cnt+=1
cnt=cnt%10
if cnt in st:
continue
#print(cnt)
out_path = "data/obj/" + filename # 引号内为测试图片文件夹的路径
print(out_path)
f.write(out_path + '\n')
cnt=0
f.close()
现在,目录如下:
darknet
data
obj
train.txt
test.txt
在data
目录里创建数据集文件,tree
:
darknet
data
obj.names # 物体类别名称
obj.data # 该文件夹保存数据集信息
obj # 存放图片以及标签信息
train.txt # 存放训练集地址
test.txt # 存放检测集地址
以下分别解释data/
下5个文件(夹)的作用:
obj.name
保存要检测物体的名称,一行写一个
obj.data
保存有五类信息:检测类别数量,训练集,验证集,类别名称,保存权重的文件夹,比如:
classes= 2 # 目标物体的类别数量
train = data/train.txt # 训练数据集的路径
test= data/test.txt # 验证数据集的路径
names = data/obj.names # 目标物体的类别名称
backup = backup/ # 保存训练权重文件
obj
存放数据集图片和每张图片的标签信息,即:
001.jpg
001.txt
002.jpg
002.txt
其中,每个.txt
是对应.jpg
的标签文件,其包含的信息有:
<object-class> <x_center> <y_center> <width> <height>
本人是将训练集和验证集的图片,都放在了obj文件夹里。
每个
.jpg
文件,都有同名但.txt
的文件相对应,且在obj文件夹里。001.txt文件中一行对应001.jpg中一个物体信息:类别 中心点x坐标 中心点y坐标 宽 高 ,可能有多行。
计算方式如下:
<x_center> = <absolute_x> / <image_width> 即bounding box中心x实际坐标 / 图片实际宽度
<y_center> = <absolute_y> / <image_height> 即bounding box中心y实际坐标/图片实际高度
<width> = <absolute_width> / <image_width> 即bbox宽度/图片实际宽度
<height> = <absolute_width> / <image_width> 即bbox高度 /图片实际高度
train.txt
保存所有训练集图片的相对地址,如:
data/obj/img001.jpg
data/obj/img002.jpg
data/obj/img003.jpg
test.txt
保存所有验证集图片的相对地址,如:
data/obj/img11.jpg
data/obj/img21.jpg
data/obj/img31.jpg
复制cfg
文件夹下yolov4.cfg
为yolov4-obg.cfg
修改yolov4-obj.cfg
中的内容:
# step1: 修改batch和subdivisions
L2: batch=64 # 原来就是64,根据gpu自己选择,但必须是2的倍数
L3: subdivisions=16 # 根据自己的gpu选择,如果out of memory,则32或64,依次倍增。或者64,32,...,1,依次倍减batch值
# step2: 修改图片的尺寸
L7: width=608 # 这边我没有修改,该值必须被32整除。
L8: height=608
L19: max_batches=500500 #该值最少是classes*2000
L21:steps=400000,450000 #该值是 max_batches的80% 和 90%
# step3: 修改classes(每个yolo层都需要修改一次,一共三次)
L968: classes=2 # 和obj.data文件中classes值相同,=2表示只识别两类物体
L1056: classes=2
L1144: classes=2
# step4: 修改每个yolo相邻的上一个convolution层的filter,一共3个
L961: filters=21 # 因为我预测两类物体:21 = 3*(5+2),具体看下文解释
L1049: filters=21
L1137: filters=21
L2,L3表示第2行,第3行。
filters=21 是3*(5+2)=21得来的,如果一共检测4类物体,比如:car,pedestrian,truck,bus,则filters的值为3*(5+4)=27.关于为什么是5,还要乘3,可以看对应论文。
若无GPU,则不做任何修改,否则一些参数应修改为:
GPU=1: 表示在训练的时候使用CUDA进行加速训练(CUDA应该在 /usr/local/cuda文件夹下),若无,
CUDNN=1: 表示在训练的过程中使用CUDNN v5-v7进行加速(cuDNN应该在 /usr/local/cudnn文件夹下)
CUDNN_half=0: 为Tensor Cores (在Titan V / Tesla V100 / DGX-2等)上进行加速训练和推理。
OPENCV=1: 编译OpenCV 4.x/3.x/2.4.x等。OpenCV可以读取视频或者图片。
#L69 将NVCC=nvcc改为你自己的nvcc的路径
NVCC=/usr/local/cuda/bin/nvcc
注意L109和L121以下的3行:看一下cuda的默认路径,是否和自己的一致,不一致就需要修改。有的人是NVCC=/usr/local/cuda9.0/bin/nvcc,则需要修改。
以上就是开始训练前的准备。
下载预训练权值yolov4.conv.137
,放在darknet/里
执行./darknet detector train data/obj.data cfg/yolov4-obj.cfg yolov4.conv.137 -map
Note:
1.指定GPU:
#如果想要指定具体的gpu进行训练,使用-i来指定,比如我想使用索引为2的gpu进行训练:
./darknet detector train data/obj.data cfg/yolov4-obj.cfg yolov4.conv.137 -i 2 -map
2.训练时不显示loss窗口的命令:
./darknet detector train data/obj.data cfg/yolo-obj.cfg yolov4.conv.137 -dont_show
3.在远程界面,显示mAP&Loss图表:
./darknet detector train data/obj.data cfg/yolo-obj.cfg yolov4.conv.137 -dont_show -mjpeg_port 8090 -map
然后使用浏览器打开链接:http://ip-address:8090
训练过程中,darknet自动保存权值文件到backup/
backup文件夹下的yolo-obj_last.weights文件会每隔1000个iterations保存一次,新的会替代旧的
darknet总的训练步长可以在yolov4-obj.cfg文件中修改max_batches的大小(默认max_batches = 500500)
-map表示可视化显示mAP&Loss 图表,每4Epochs
训练完成后,在backup/查看结果文件:yolo-obj_final.weights
每100次迭代,比如2000次迭代后,由于外因,你停止了检测,之后可以使用:
./darknet detector train data/obj.data yolo-obj.cfg backup/yolo-obj_2000.weights
来继续训练。当avg-average loss (error)值达到最低时,你也可以提前停止训练。
关于什么停止训练,官网文档大概意思是:
在Early Stopping Point处,权值文件是最佳的,关于怎么寻找到该点,文档大概意思是:
- 准备验证集。你需要在 obj.data中将valid = data/test.txt 修改成 valid = data/valid.txt (valid.txt的格式和train.txt一样),如果你没有验证集,你可以直接将train.txt的内容直接复制到valid.txt文件中。
- 对候选权重,测试mAP。如果你是在9000 iterations停止训练的,你就需要对之前保存的权重进行测试:
具体步骤是:
step1.测试7000 iterations的mAP:
./darknet detector map data/obj.data cfg/yolov4-obj.cfg backup/yolo-obj_7000.weights
step2: 测试8000 iterations的mAP
./darknet detector map data/obj.data cfg/yolov4-obj.cfg backup/yolo-obj_8000.weights
step3: 测试9000 iterations的mAP
./darknet detector map data/obj.data cfg/yolov4-obj.cfg backup/yolo-obj_9000.weights
选择mAP值或者IOU值最高的权重文件,该文件就是最优的训练权重。
训练好权重之后,我们可以使用权重进行检测,在终端中输入下面命令就行:
./darknet detector test data/obj.data cfg/yolo-obj.cfg yolo-obj_8000.weights
检测视频命令是:
# 只显示FPS,不显示图像
./darknet detector demo data/obj.data cfg/yolov4-obj.cfg backup/yolov4-obj_9000.weights data/demo3.mp4 -dont_show -ext_output
# 显示FPS,显示图像,保存检测视频结果
./darknet detector demo data/obj.data cfg/yolov4-obj.cfg backup/yolov4-obj_9000.weights data/demo3.mp4 -ext_output -out_filename res.mp4
以上。
大部分都是按照AlexeyAB/darknet上的参考文档走的,基本没有遇到问题。