ubuntu18.04, yolov4配置,训练自己的数据集

一、ubuntu18.04下配置yolov4

ubuntu18.04下跑通yolov4的demo很简单。编译前要保证cuda, cudnn, opencv的系统环境正确。
下载完源码并修改完makefile文件编译通过后直接执行:

./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights -thresh 0.25 ./data/dog.jpg

如果检测正确,即可进行下面的训练步骤。
参考链接:
Ubuntu18.04配置darknet环境实现YOLOv4目标检测(一)——配置YOLOv4环境darknet

二、训练自己的数据集

写在最前面)无论是yolov5,还是yolov4,其训练需要的数据标签格式都是txt格式的,如果你的数据标签为xml格式的,那么就需要执行darnet/scripts文件下的voc_label将数据集的标签数据由xml文件转成txt文件格式。即xml文件只是一个中间过渡的部分。
接下来为训练自己数据集的讲解:

1. 首先要制作自己的数据集

训练所用数据集格式为:

VOCdevkit
--VOC2007
----Annotations  #(放XML标签文件)
----ImageSets  
------Main       #(放数据集划分的trainval.txt, train.txt, val.txt, test.txt)
----JPEGImages   # (放原始图片)

这个数据集放在哪个路径下无关紧要,重要的是训练时读取路径的代码或者命令正确即可。
如果拿到的是只有图片和xml文件的数据集,那么需要进行如下两步:

1. 先执行split_data.py,执行完之后会在main文件夹下生成完整版共4个txt文件,分别为:trainval.txt, train.txt, val.txt, test.txt。注意:这些文件中的图片名称必须是不包含路径以及后缀.jpg或者.png。
2. 然后再执行voc_label.py,执行完后会自动生成一个labels文件夹,该文件夹下会产生由xml文件转成txt文件的对应标签。同时会生成带有路径以及后缀.jpg或者.png的trainval.txt, train.txt, val.txt, test.txt等文件。这些文件中必须带有。
(注意:执行完voc_label生成的trainval.txt, train.txt, val.txt, test.txt中每一行的元素不能只是图片的名字必须在前面有完整版的路径,以及在名字后面有.jpg。如:
/home/dlm/yolov4_split_shujuji/VOCdevkit_person/VOC2007/JPEGImages/8.jpg)

(文末有上述修改好的两个.py文件的代码以及修改建议)
3. 然后执行完voc_label.py后生成的trainval.txt, train.txt, val.txt, test.txt拷贝到mian文件夹下替换掉里面没路径的trainval.txt, train.txt, val.txt, test.txt这四个txtx文件。
注意:如果拿到的是同时有图片,xml,txt文件的数据集,那么需要进行划分数据集一步即可,即:
执行split_data.py,执行完之后会在main文件夹下生成完整版共4个txt文件,分别为:trainval.txt, train.txt, val.txt, test.txt。但是这四个txt文件必须是包含绝对路径以及.jpg或者.png的文件。

2.修改参数进行训练:

3. 要修改的配置文件共三个,分别为:在darnet文件夹下data文件夹中的person.names,以及cfg文件夹中的person.data, person.cfg。(原本的为coco.names, coco.data, yolov4-custom.cfg。复制之后重命名修改为自己的参数即可)。
其中person.names中每一行为要训练的数据其的类别,一行一个,注意其顺序要和划分数据集voc_label.py中的类别顺寻要一致;
其中person.data文件中的内容为train.txt, val.txt的绝对路径,要注意其参数名字为train和valid,而不是train和val;
其中person.cfg文件中的内容为主要要修改的训练参数,主要为:
1)修改batch=64,修改subdivisions=64(如果显卡性能较高,可以设置batch=96或subdivisions=16;batch的含义为一次喂给网络多少张图片,subdivisions的含义为将batch数量的图片分成多少份喂进去,即通常由于显存的原因,一次喂入64张图片通常会核心转储报错。本电脑1660ti, 6g显存无奈设置的参数为:batch = 64, subdividions = 64;
2)修改width=416,height=416(如果显卡性能较高,可以设置width=608,height=608;
3)修改max_batches=classes*2000,例如当前有3个类别,则设置为6000,这里的max_batches表示最终的迭代次数;
4)修改steps为max_batches的80%到90%,比如max_batches=6000,则steps=4800,5400;
5)修改classes,使用文件内容搜索关键字[yolo]可以搜到3次,修改classes的数量为你的类别数,这里classes=3;
6)修改filters,同上一步搜索[yolo],每次搜到的yolo上一个的[convolutional]中filters=(classes + 5)x3 比如filters=24
(可以跳过)如果要用[Gaussian_yolo] ,则搜索[Gaussian_yolo] 将[filters=57] 的filter 修改为 filters=(classes + 9)x3
4. 然后下载预训练权重,放入darknet主目录下。权重下载地址为:
https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.conv.137
5. 用该命令训练:

./darknet detector train cfg/person.data cfg/yolov4-person.cfg yolov4.conv.137      #(每1000轮保存一个权重)

6. 训练2000此后在之前训练的基础上继续训练(适合中途停止后继续训练):

./darknet detector train cfg/person.data cfg/yolov4-person.cfg backup/yolov4_2000.weights

即可以训练到2000轮保存一个权重,然后把该权重当成预训练权重并在此基础上训练。

参考链接:
https://blog.csdn.net/sinat_28371057/article/details/109672237
https://blog.csdn.net/weixin_44771532/article/details/105495755
https://zhuanlan.zhihu.com/p/138791419
https://blog.csdn.net/u012280876/article/details/117216948
https://zhuanlan.zhihu.com/p/326099868
https://blog.csdn.net/pts_mjt/article/details/84557681
https://ask.csdn.net/questions/4626230
https://blog.csdn.net/linghu8812/article/details/100867702

split_data.py与voc_label.py代码:

split_data.py :

# split_data.py   将JPEGImages下的所有文件分成训练集,测试集,生成ImageSets下Main文件夹中的txt文件。
import os  
import random 
xmlfilepath=r'/home/dlm/yolov4_split_shujuji/VOCdevkit_person/VOC2007/Annotations'   #自己数据集中xml的路径                        
saveBasePath=r"/home/dlm/yolov4_split_shujuji/VOCdevkit_person/"                     # 保存路径
trainval_percent=0.9                                             #adjust trainval percentage    
train_percent=0.9                                                      
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)    
  
print("train and val size",tv)  
print("traub suze",tr)  
ftrainval = open(os.path.join(saveBasePath,'/home/dlm/yolov4_split_shujuji/VOCdevkit_person/VOC2007/ImageSets/Main/trainval.txt'), 'w')    # txt文件储存的路径
ftest = open(os.path.join(saveBasePath,'/home/dlm/yolov4_split_shujuji/VOCdevkit_person/VOC2007/ImageSets/Main/test.txt'), 'w')    #
ftrain = open(os.path.join(saveBasePath,'/home/dlm/yolov4_split_shujuji/VOCdevkit_person/VOC2007/ImageSets/Main/train.txt'), 'w')    #
fval = open(os.path.join(saveBasePath,'/home/dlm/yolov4_split_shujuji/VOCdevkit_person/VOC2007/ImageSets/Main/val.txt'), 'w')    #
  
for i  in list:    
    #name="/home/dlm/yolov4_split_shujuji/VOCdevkit_person/VOC2007/JPEGImages/" + total_xml[i][:-4]+ '.jpg' + '\n'   
    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()  

voc_label.py :

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 = ["car"]


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))
        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")

split_data.py修改建议
只需把含有路径的地方换成自己的路径即可。

voc_label.py修改建议

  1. 要修改类别classes的参数

  2. 修改路径

  3. 如果出现报错:’NoneType’ object has no attribute 'txet’是因为xml中没有difficult这个属性内容,最后导致

    difficult = obj.find(‘difficult’).text没起作用

因此解决办法就是看自己的xml文件中是否存在这一个属性,没有的话直接把含有difficult的语句注释掉即可,当然也有的是xml中含有Difficult,其中D是大写,只要把程序中含有difficult的句子中的difficult变为Difficult即可。
我是直接变为大写的就可以了。

你可能感兴趣的:(opencv,深度学习,python)