yolo进行数据训练

摘要

本文主要记录Ubuntu下yolov4数据的训练

  • 安装opencv4
  • 安装opencv_contrib
  • 制作训练数据集
  • 生成yolov4需要的数据集
  • 配置GPU
  • 修改yolov4网络配置参数
  • 开始训练

笔记主要记录一些指令和相关的配置

  • [x] Edit By Porter, 积水成渊,蛟龙生焉。

安装环境

安装Opencv4 和opencv_contrib

安装cuda && cudnn环境

一、配置yolo

1、yolo参数配置

  • 首先看看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]
# Testing
#batch=1
#subdivisions=1
# Training
batch=64 #讲训练样本分为64个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 # 2000*num_classes
policy=steps
steps=6400,7200 # max_batches*80%,max_batches*9./darknet detector test data/obj.data cfg/yolo-obj.cfg yolo-obj_8000.weights0%
scales=.1,.1

每个yolo layer 前的最后一个conv内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# step1: 修改batch和subdivisions
L2: batch=64 # 原来就是64,根据gpu自己选择
L3: subdivisions=16 # 原来是8,根据自己的gpu选择
####
# step2: 修改图片的尺寸
L7: width=608 # 这边我就不进行修改了
L8: height=608 # 这边我也不修改
####
# step3: 修改classes(每个yolo层都需要修改一次,一共需要修改三次)
L968: classes=2 # 我只需要识别两类物体,因此需要修改成2
L1056: classes=2
L1144: classes=2

# step4: 需要修改每个yolo相邻的上一个convolution层的filter
L961: filters=21 # 因为我预测两类物体:21 = 3*(5+2)
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 编译配置好参数的模型

1
2
cd darknet
make

好了,到这里假设你的样本数据都制作好了,你就可以开始训练了,./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) #用字符串函数zfill 以0补全所需位数
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
# from PIL import Image
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)
# try:
new_img = cv.resize(img, (width, height), interpolation=cv.INTER_CUBIC)
# if new_img.mode == 'P':
# new_img = new_img.convert("RGB")
# if new_img.mode == 'RGBA':
# new_img = new_img.convert("RGB")
# new_img.save(os.path.join(outdir, os.path.basename(jpgfile)))
cv.imwrite(os.path.join(outdir, os.path.basename(jpgfile)), new_img)
# except Exception as e:
# print(e)


if __name__ == "__main__":
for jpgfile in glob.glob("../JPEGImages/*.jpg"):
# print(jpgfile)
convertjpg(jpgfile,"../JPEGImages/",width=640,height=480)

2.4

文章目录
  1. 1. 摘要
    1. 1.1. 安装环境
      1. 1.1.1. 安装Opencv4 和opencv_contrib
      2. 1.1.2. 安装cuda && cudnn环境
    2. 1.2. 一、配置yolo
      1. 1.2.1. 1、yolo参数配置
        1. 1.2.1.1. 1.1 my_dataset.data 文件中的内容:
        2. 1.2.1.2. 1.2 yolov4-my_dataset.cfg主要是模型配置文件
        3. 1.2.1.3. 1.3 yolov4-my_dataset.weights主要是模型文件
        4. 1.2.1.4. 1.4 yolov4_porter.names
        5. 1.2.1.5. 1.5 makefile文件修改
        6. 1.2.1.6. 1.6 编译配置好参数的模型
    3. 1.3. 二样本制作
      1. 1.3.1. 2 样本标注
        1. 1.3.1.1. 2.1 VOC标记后的xml转txt
        2. 1.3.1.2. 2.2 将xml->txt转label
        3. 1.3.1.3. 2.2 图片名字随机规范命名
        4. 1.3.1.4. 2.3 图片的尺寸归一化
      2. 1.3.2. 2.4
|