zoukankan      html  css  js  c++  java
  • tensorflow yolov3训练自己的数据集,详细教程

    这个教程是我在自己学习的过程中写的,当作一个笔记,写的比较详细
    在github上下载yolov3的tensorflow1.0版本:
    https://github.com/YunYang1994/tensorflow-yolov3
    在19年12月,发现网上训练的教程大部分似乎已经过时了,因为作者对开源代码进行了部分修改
    其实在作者的readme上已经写了训练的方法,但是不是那么详细,于是记录下
    由于本人水平有限,如果文章有不当之处还望评论区指出

    一,制作训练集

    1,打标签
    训练集需要实验labelimg工具进行制作,这里的数据集格式采用的是voc格式:
    labelimg下载地址
    实验labelimg打好标签后会生成两个文件夹:
    Annotations —存放标记的图片
    JPEGImages —存放xml格式的标签
    这里不做多余的解释
    2,按照voc数据集格式建立文件夹

    因为作者给的模型是训练VOC数据集的模型,所以训练我们自己的数据集的时候也需要改为VOC格式的,VOC格式解析:
    第一步:首先了解VOC2007数据集的格式

    1. 1)JPEGImages文件夹

    文件夹里包含了训练图片和测试图片,混放在一起

    1. 2)Annatations文件夹

    文件夹存放的是xml格式的标签文件,每个xml文件都对应于JPEGImages文件夹的一张图片

    1. 3)ImageSets文件夹

    Action存放的是人的动作,我们暂时不用

    Layout存放的人体部位的数据。我们暂时不用

    Main存放的是图像物体识别的数据,分为20类,当然我们自己制作就不一定了,如果你有精力,Main里面有test.txt , train.txt, val.txt ,trainval.txt.这四个文件我们后面会生成
    train中存放的是训练使用的数据,每一个class的train数据都有5717个。
    val中存放的是验证结果使用的数据,每一个class的val数据都有5823个。
    trainval为训练和验证的图片文件的文件名列表 。
    Segmentation存放的是可用于分割的数据
    如果你下载了VOC2007数据集,那么把它解压,把各个文件夹里面的东西删除,保留文件夹名字。如果没下载,那么就仿照他的文件夹格式,按照这个目录格式建立文件夹:
    在这里插入图片描述
    然后分别把标记的图片放入JPEGImages文件夹,标签xml文件放入Annotations文件夹:
    在这里插入图片描述
    在这里插入图片描述
    3,划分训练集和测试集
    训练时要有测试集和训练集,通过划分放在
    VOCdevkitVOC2008ImageSetsMain
    文件夹下,这里可以使用一段python代码按照9:1进行随机划分:
    在VOC2008文件夹建立split.py

    import os
    import random
    import sys
    
    if len(sys.argv) < 2:
        print("no directory specified, please input target directory")
        exit()
    
    root_path = sys.argv[1]
    
    xmlfilepath = root_path + '/Annotations'
    
    txtsavepath = root_path + '/ImageSets/Main'
    
    if not os.path.exists(root_path):
        print("cannot find such directory: " + root_path)
        exit()
    
    if not os.path.exists(txtsavepath):
        os.makedirs(txtsavepath)
    
    trainval_percent = 0.9
    train_percent = 0.8
    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("train size:", tr)
    
    ftrainval = open(txtsavepath + '/trainval.txt', 'w')
    ftest = open(txtsavepath + '/test.txt', 'w')
    ftrain = open(txtsavepath + '/train.txt', 'w')
    fval = open(txtsavepath + '/val.txt', 'w')
    
    for i in list:
        name = total_xml[i][:-4] + '
    '
        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()
    
    
    
    • 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

    打开控制台,运行该python文件,后跟Annotation的目录即可进行划分:

    python .split.py F:datesethat_dataVOC	estVOCdevkitVOC2007
    
    • 1

    在这里插入图片描述
    train为训练集,test为验证集
    4,根据划分结果制作训练集
    上面代码已经实现了将数据集划分为训练集和验证集,但是tensorflow yolov3作者写的模型要求的数据集格式为:

    在这里插入图片描述
    所以我们需要写一个小脚本,根据train.txt 和test.txt将数据集进行更改

    import os
    from shutil import copyfile
    #根据tarin.txt和test.txt将数据集分为标准数据集
    train_text_path = 'F:/dateset/hat_data/VOCdevkit/VOC2007/ImageSets/Main/train.txt'
    test_text_path = 'F:/dateset/hat_data/VOCdevkit/VOC2007/ImageSets/Main/test.txt'
    #图片存放地址
    image_path = 'F:/dateset/hat_data/VOCdevkit/VOC2007/JPEGImages'
    #xml文件存放地址
    xml_path = 'F:/dateset/hat_data/VOCdevkit/VOC2007/Annotations'
    
    #输出的目录
    outdir = 'F:/dateset/hat_data'
    #创建各级文件夹
    test_xml_out = os.path.join(outdir,'VOC/test/VOCdevkit/VOC2007/Annotations')
    os.makedirs(test_xml_out)
    os.makedirs(os.path.join(outdir,'VOC/test/VOCdevkit/VOC2007/ImageSets/Layout'))
    os.makedirs(os.path.join(outdir,'VOC/test/VOCdevkit/VOC2007/ImageSets/Main'))
    os.makedirs(os.path.join(outdir,'VOC/test/VOCdevkit/VOC2007/ImageSets/Segmentation'))
    test_img_out = os.path.join(outdir,'VOC/test/VOCdevkit/VOC2007/JPEGImages')
    os.makedirs(test_img_out)
    os.makedirs(os.path.join(outdir,'VOC/test/VOCdevkit/VOC2007/SegmentationClass'))
    os.makedirs(os.path.join(outdir,'VOC/test/VOCdevkit/VOC2007/SegmentationObject'))
    train_xml_out = os.path.join(outdir,'VOC/train/VOCdevkit/VOC2007/Annotations')
    os.makedirs(train_xml_out)
    os.makedirs(os.path.join(outdir,'VOC/train/VOCdevkit/VOC2007/ImageSets/Layout'))
    os.makedirs(os.path.join(outdir,'VOC/train/VOCdevkit/VOC2007/ImageSets/Main'))
    os.makedirs(os.path.join(outdir,'VOC/train/VOCdevkit/VOC2007/ImageSets/Segmentation'))
    train_img_out = os.path.join(outdir,'VOC/train/VOCdevkit/VOC2007/JPEGImages')
    os.makedirs(train_img_out)
    os.makedirs(os.path.join(outdir,'VOC/train/VOCdevkit/VOC2007/SegmentationClass'))
    os.makedirs(os.path.join(outdir,'VOC/train/VOCdevkit/VOC2007/SegmentationObject'))
    
    
    
    with open(train_text_path) as f:
        lines = f.readlines()
        for i in lines:
            img_save_path = os.path.join(train_img_out,i.rstrip('
    ')+'.jpg')
            xml_save_path = os.path.join(train_xml_out, i.rstrip('
    ') + '.xml')
            copyfile(os.path.join(image_path,i.rstrip('
    ')+'.jpg'),img_save_path)
            copyfile(os.path.join(xml_path, i.rstrip('
    ') + '.xml'), xml_save_path)
            print(i)
    with open(test_text_path) as f:
        lines = f.readlines()
        for i in lines:
            img_save_path = os.path.join(test_img_out, i.rstrip('
    ') + '.jpg')
            xml_save_path = os.path.join(test_xml_out, i.rstrip('
    ') + '.xml')
            copyfile(os.path.join(image_path, i.rstrip('
    ') + '.jpg'), img_save_path)
            copyfile(os.path.join(xml_path, i.rstrip('
    ') + '.xml'), xml_save_path)
            print(i)
    
    • 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

    根据这个小脚本就可进行划分为规定的格式,如果数据集的量比较大,可能有点慢,当然你也可以进行手动划分,省略前面几步操作,只要最终目录结构满足作者的格式就行
    在这里插入图片描述
    5,分别在test和train下面运行split.py脚本

     python .split.py F:datesethat_dataVOC	rainVOCdevkitVOC2007
    
    • 1
     python .split.py F:datesethat_dataVOC	estVOCdevkitVOC2007
    
    • 1

    二,制作标准数据集txt文件

    作者在github上声明,训练需要两个文件,如下所示:

    dataset.txt:

    xxx/xxx.jpg 18.19,6.32,424.13,421.83,20 323.86,2.65,640.0,421.94,20 
    xxx/xxx.jpg 48,240,195,371,11 8,12,352,498,14
     image_path x_min, y_min, x_max, y_max, class_id  x_min, y_min ,..., class_id 
     make sure that x_max < width and y_max < height
    
    • 1
    • 2
    • 3
    • 4

    class.names:

    person
    bicycle
    car
    ...
    toothbrush
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1,生成dataset.txt
    其实前几步都是为这两步做准备,通过目录下的 scripts/voc_annotation.py就可以生成dataset.txt文件,但是需要改一些代码参数:
    只需更改 classes为自己的类别

    classes = ['hat','person']
    
    • 1


    default 更改为你自己的看注释

        #default 更改为你自己数据集VOC的目录
        parser.add_argument("--data_path", default="F:/dateset/hat_data/VOC")
        #default 更改为voc_train.txt的存放的位置
        parser.add_argument("--train_annotation", default="../data/dataset/voc_train.txt")
        parser.add_argument("--test_annotation",  default="../data/dataset/voc_test.txt")
        flags = parser.parse_args()
    
        if os.path.exists(flags.train_annotation):os.remove(flags.train_annotation)
        if os.path.exists(flags.test_annotation):os.remove(flags.test_annotation)
        
        #更改训练集和测试集的相对路径
        num1 = convert_voc_annotation(os.path.join(flags.data_path, 'train/VOCdevkit/VOC2007'), 'trainval', flags.train_annotation, False)
        num3 = convert_voc_annotation(os.path.join(flags.data_path, 'test/VOCdevkit/VOC2007'),  'trainval', flags.test_annotation, False)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    其他默认即可:
    更改后的代码:

    import os
    import argparse
    import xml.etree.ElementTree as ET
    
    def convert_voc_annotation(data_path, data_type, anno_path, use_difficult_bbox=True):
    
        #更改为你自己的类别
        classes = ['hat','person']
        img_inds_file = os.path.join(data_path, 'ImageSets', 'Main', data_type + '.txt')
        with open(img_inds_file, 'r') as f:
            txt = f.readlines()
            image_inds = [line.strip() for line in txt]
    
        with open(anno_path, 'a') as f:
            for image_ind in image_inds:
                image_path = os.path.join(data_path, 'JPEGImages', image_ind + '.jpg')
                annotation = image_path
                label_path = os.path.join(data_path, 'Annotations', image_ind + '.xml')
                root = ET.parse(label_path).getroot()
                objects = root.findall('object')
                for obj in objects:
                    difficult = obj.find('difficult').text.strip()
                    if (not use_difficult_bbox) and(int(difficult) == 1):
                        continue
                    bbox = obj.find('bndbox')
                    class_ind = classes.index(obj.find('name').text.lower().strip())
                    xmin = bbox.find('xmin').text.strip()
                    xmax = bbox.find('xmax').text.strip()
                    ymin = bbox.find('ymin').text.strip()
                    ymax = bbox.find('ymax').text.strip()
                    annotation += ' ' + ','.join([xmin, ymin, xmax, ymax, str(class_ind)])
                print(annotation)
                f.write(annotation + "
    ")
        return len(image_inds)
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser()
        #default 更改为你自己数据集VOC的目录
        parser.add_argument("--data_path", default="F:/dateset/hat_data/VOC")
        #default 更改为voc_train.txt的存放的位置
        parser.add_argument("--train_annotation", default="../data/dataset/voc_train.txt")
        parser.add_argument("--test_annotation",  default="../data/dataset/voc_test.txt")
        flags = parser.parse_args()
    
        if os.path.exists(flags.train_annotation):os.remove(flags.train_annotation)
        if os.path.exists(flags.test_annotation):os.remove(flags.test_annotation)
    
        #更改训练集和测试集的相对路径
        num1 = convert_voc_annotation(os.path.join(flags.data_path, 'train/VOCdevkit/VOC2007'), 'trainval', flags.train_annotation, False)
        num3 = convert_voc_annotation(os.path.join(flags.data_path, 'test/VOCdevkit/VOC2007'),  'trainval', flags.test_annotation, False)
        print('=> The number of image for train is: %d	The number of image for test is:%d' %(num1 , num3))
    
    
    • 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

    更改后运行该python文件:
    在这里插入图片描述
    在这里插入图片描述
    这就生成了dataset.txt
    2,更改voc.names的类别
    在这里插入图片描述
    修改为自己的类别:
    在这里插入图片描述
    3,修改配置文件
    编辑您的文件./core/config.py以进行一些必要的配置

    _C.YOLO.CLASSES                = "./data/classes/voc.names"
    __C.TRAIN.ANNOT_PATH            = "./data/dataset/voc_train.txt"
    __C.TEST.ANNOT_PATH             = "./data/dataset/voc_test.txt"
    
    • 1
    • 2
    • 3

    到这里已经配置完成,已经可以训练了

    三,训练数据

    1)从头开始训练:

    $ python train.py
    $ tensorboard --logdir ./data
    
    • 1
    • 2

    在这里插入图片描述
    (2)从COCO配置训练(推荐):

    $ cd checkpoint
    $ wget https://github.com/YunYang1994/tensorflow-yolov3/releases/download/v1.0/yolov3_coco.tar.gz
    $ tar -xvf yolov3_coco.tar.gz
    $ cd ..
    $ python convert_weight.py --train_from_coco
    $ python train.py
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2评估VOC数据集

    $ python evaluate.py
    $ cd mAP
    $ python main.py -na
    原文:https://blog.csdn.net/qq_38441692/article/details/103652760

    作者:柒月
    Q群 :2122210(嵌入式/机器学习)
  • 相关阅读:
    爬虫基础
    设计BBS
    中间件和auth模块
    git
    分页器与form表单
    cookie与session
    springboot2.x基础教程:@Enable原理
    springboot2.x基础教程:@Scheduled开启定时任务及源码分析
    springboot2.x基础教程:@Async开启异步任务
    springboot2.x基础教程:JRebel实现SpringBoot热部署
  • 原文地址:https://www.cnblogs.com/Ph-one/p/13864196.html
Copyright © 2011-2022 走看看