摘要
本文主要记录Ubuntu下yolov4数据的训练
- 安装opencv4
- 安装opencv_contrib
- 制作训练数据集
- 生成yolov4需要的数据集
- 配置GPU
- 修改yolov4网络配置参数
- 开始训练
笔记主要记录一些指令和相关的配置
- [x] Edit By Porter, 积水成渊,蛟龙生焉。
安装环境
安装cuda && cudnn环境
一、配置yolo
1、yolo参数配置
1
| ./build_release/darknet detector train data/my_dataset.data cfg/yolov4-my_dataset.cfg backup/yolov4-my_dataset.weights -map
|
包含的文件有:1.my_dataset.data、2.yolov4-my_dataset.cfg、3.yolov4-my_dataset.weights 命令中包含这三个文件。但是代码中还需要有4.yolov4_porter.names
1 2 3 4 5
| $ cat data/porter.names spot scratch colourdiff clusters
|
1.1 my_dataset.data 文件中的内容:
1 2 3 4 5 6
| $ cat cfg/porter.data classes= 4 train = /home/porter/WorkSpace/YOLO4_test/YOLOV4/darknet/train.txt valid = /home/porter/WorkSpace/YOLO4_test/YOLOV4/darknet/test.txt names = /home/porter/WorkSpace/YOLO4_test/YOLOV4/darknet/data/porter.names backup = /home/porter/WorkSpace/YOLO4_test/YOLOV4/darknet/backup/
|
1.2 yolov4-my_dataset.cfg主要是模型配置文件
.cfg文件配置主要有三个地方,分别对应三个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| [net]
batch=64 subdivisions=64 width=640 height=480 channels=3 momentum=0.949 decay=0.0005 angle=0 saturation = 1.5 exposure = 1.5 hue=.1
learning_rate=0.001 burn_in=1000 max_batches = 8000 policy=steps steps=6400,7200 scales=.1,.1
|
每个yolo layer 前的最后一个conv内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| L2: batch=64 L3: subdivisions=16
L7: width=608 L8: height=608
L968: classes=2 L1056: classes=2 L1144: classes=2
L961: filters=21 L1049: filters=21 L1137: filters=21
|
1.3 yolov4-my_dataset.weights主要是模型文件
yolov4.conv.137是预训练模型文件,可以用你自己的模型断点保存的文件yolov4-my_dataset.weights替换。
- 假设断电保存的训练模型文件路径为:backup/yolov4-my_dataset.weights,那么在断点复训时一般都是直接执行这条指令
1
| ./build_release/darknet detector train data/my_dataset.data cfg/yolov4-my_dataset.cfg backup/yolov4-my_dataset.weights -map
|
- 如果不使用断电复训模型文件,从头开始训练完样本可以执行如下的命令:
1
| ./build_release/darknet detector train data/my_dataset.data cfg/yolov4-my_dataset.cfg -map
|
- 如果使用yolo的与训练模型文件进行训练可以使用如下的命令
1
| ./darknet detector train cfg/coco.data yolov4.cfg yolov4.conv.137 -dont_show
|
-dont_show代表不显示训练的趋势图片,-map表示显示均值平均精度( Average Precision,mAP = 所有类别的平均精度求和除以所有类别)。
1.4 yolov4_porter.names
一般这个.names文件就是样本测试中会使用的框图上显示的标签,名字可以随便改,但是类别顺序不能变。
1.5 makefile文件修改
具体的配置文件需要自己按需配置
1 2 3 4 5 6 7 8
| (1) GPU=1: 表示在训练的时候使用CUDA进行加速训练(CUDA应该在 /usr/local/cuda文件夹下) (2) CUDNN=1: 表示在训练的过程中使用CUDNN v5-v7进行加速(cuDNN应该在 /usr/local/cudnn文件夹下) (3) CUDNN_half=1: 为Tensor Cores (在Titan V / Tesla V100 / DGX-2等)上进行加速训练和推理。 (4) OPENCV=1: 编译OpenCV 4.x/3.x/2.4.x等。OpenCV可以读取视频或者图片。 (5) DEBUG=1: 编译debug版本的Yolo (6) OPENMP=1:使用OpenMP进行编译,能够使用多核的CPU进行加速 (7) LIBOS=1: 编译构建darknet.so动态库。 (8) ZED_CAMREA=0: 置为1的时候表示构建ZED-3D-camera的库。这里我们不使用
|
1.6 编译配置好参数的模型
好了,到这里假设你的样本数据都制作好了,你就可以开始训练了,./darknet detector train data/obj.data cfg/yolov4-tiny-obj.cfg yolov4.conv.137 -i 2 -map
二样本制作
2 样本标注
标注精灵或者labelimg等进行VOC格式文件下的标注
2.1 VOC标记后的xml转txt
标注完可以生成xml文件,接着需要将xml文件转换成txt,执行:python voc_annotation_to_txt.py 。注意生成的txt文件只包括分配的图像的名字,不包括标注信息。注意这个txt和后面的label中txt的区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import xml.etree.ElementTree as ET from os import getcwd
sets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
classes = ['spot','scratch','colourdiff','clusters']
def convert_annotation(year, image_id, list_file): in_file = open('Annotations/%s.xml'%( image_id)) tree=ET.parse(in_file) root = tree.getroot()
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 = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text), int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text)) list_file.write(" " + ",".join([str(a) for a in b]) + ',' + str(cls_id))
wd = getcwd()
for year, image_set in sets: image_ids = open('ImageSets/Main/%s.txt'%(image_set)).read().strip().split() list_file = open('%s.txt'%(image_set), 'w') for image_id in image_ids: list_file.write('JPEGImages/%s.jpg'%(image_id)) convert_annotation(year, image_id, list_file) list_file.write('\n') list_file.close()
|
2.2 将xml->txt转label
终端执行代码:python voc_label.py实现label标签文件的转换.注意标签文件的转换需要两个文件,一个是xml一个是xml对应VOC中的tain.txt文件,此时文件中也会生成train.txt和test.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| 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 = ["spot", "scratch", "colourdiff", "clusters"]
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 image_id in image_ids: list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id)) try: convert_annotation(year, image_id) except: print("err,", image_id) list_file.close()
os.system("cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt > train.txt") os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt")
|
2.2 图片名字随机规范命名
执行如下脚本:python sample_name_to_Initialization.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import os path = "../JPEGImages" filelist = os.listdir(path) count=0 for file in filelist: print(file) for file in filelist: Olddir=os.path.join(path,file) if os.path.isdir(Olddir): continue filename=os.path.splitext(file)[0] filetype=os.path.splitext(file)[1] Newdir=os.path.join(path,str(count).zfill(6)+filetype) os.rename(Olddir,Newdir) count+=1
|
2.3 图片的尺寸归一化
执行如下的脚本实现图片的尺寸归一化,python image_size_procesing.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import os.path import glob import cv2 as cv import numpy as np
def convertjpg(jpgfile,outdir,width=416,height=416): img = Image.open(jpgfile) img = cv.imread(jpgfile) new_img = cv.resize(img, (width, height), interpolation=cv.INTER_CUBIC) cv.imwrite(os.path.join(outdir, os.path.basename(jpgfile)), new_img)
if __name__ == "__main__": for jpgfile in glob.glob("../JPEGImages/*.jpg"): convertjpg(jpgfile,"../JPEGImages/",width=640,height=480)
|
2.4