YOLOv3在ubuntu20.04下训练自己的数据集(从“零”开始)

本文最后更新于:2022年2月9日 上午

YOLOv3在ubuntu20.04下训练自己的数据集

  • 背景基础

    • 实现了在win10及树莓派4B下yolov3的配置及使用
  • 目的

    • 使用Ubuntu20.04下的yolov3 v4 对自己的数据集进行训练
    • 将训练好的权重文件移植到机载树莓派上实现嵌入式系统实时的目标检测
    • 利用机载树莓派实时的目标检测信息,将该点位置信息发送至感知无人机进行协作,同时将检测结果发送至地面站进行反馈
  • 方法

    darknet-yolov3训练自己的数据集(超详细) - AnswerThe - 博客园 (cnblogs.com)
    • 准备工作

    • 环境安装

    • 训练数据

      • darknet-yolov3训练自己的数据集(超详细) - AnswerThe - 博客园 (cnblogs.com)

      • 数据集可以用之前在Windows下标注的数据集,将之前标注好的图片和xml文件放到对应目录下。

      • 在myData目录下创建test.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
        26
        27
        28
        29
        30
        31
        import os
        import random
        trainval_percent = 0.1
        train_percent = 0.9
        xmlfilepath = 'Annotations'
        txtsavepath = 'ImageSets\Main'
        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)
        ftrainval = open('ImageSets/Main/trainval.txt', 'w')
        ftest = open('ImageSets/Main/test.txt', 'w')
        ftrain = open('ImageSets/Main/train.txt', 'w')
        fval = open('ImageSets/Main/val.txt', 'w')
        for i in list:
        name = total_xml[i][:-4] + '\n'
        if i in trainval:
        ftrainval.write(name)
        if i in train:
        ftest.write(name)
        else:
        fval.write(name)
        else:
        ftrain.write(name)
        ftrainval.close()
        ftrain.close()
        fval.close()
        ftest.close()
        • 运行test.py生成对应的四个txt文件运行
        • 用python脚本将数据集进行格式转换(YOLO 训练需要的格式),在darknet文件夹下新建一个my_lables.py文件(代码如下),运行后生成代表类别和相对位置的myData_train.txt,以及lables文件夹下的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
        import xml.etree.ElementTree as ET
        import pickle
        import os
        from os import listdir, getcwd
        from os.path import join #源代码sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
        sets=[('myData', 'train')] # 改成自己建立的myData
        classes = ["people"] # 改成自己的类别
        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('myData/Annotations/%s.xml'%(image_id)) # 源代码VOCdevkit/VOC%s/Annotations/%s.xml
        out_file = open('myData/labels/%s.txt'%(image_id), 'w') # 源代码VOCdevkit/VOC%s/labels/%s.txt
        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('myData/labels/'): # 改成自己建立的myData
        os.makedirs('myData/labels/')
        image_ids = open('myData/ImageSets/Main/%s.txt'%(image_set)).read().strip().split()
        list_file = open('myData/%s_%s.txt'%(year, image_set), 'w')
        for image_id in image_ids:
        list_file.write('%s/myData/JPEGImages/%s.jpg\n'%(wd, image_id))
        convert_annotation(year, image_id)
        list_file.close()
      • 打开自己的cfg文件如图按照自己电脑配置修改训练批次和训练迭代次数。

      • 据实际情况分别修改三处classes(每个[yolo]下),filters=(classes + 5)x3(每个[yolo]上的第一个[convolutional])。

      • 修改cfg/my_data.data

      • 新建myData.names

      • 下载预训练权重,网上找个拷贝进去

        预训练权重链接: https://pan.baidu.com/s/10HPZsmBP8mpU0HcHLgSk-Q 提取码: 76fq

      • 开始训练:

        1
        ./darknet detector train cfg/my\_data.data cfg/my\_yolov3.cfg darknet53.conv.74
      • 从停止处重新训练:

        1
        ./darknet detector train cfg/my\_data.data cfg/my\_yolov3.cfg darknet53.conv.74 -gups 0,1,2,3 myData/weights/my_yolov3.backup -gpus 0,1,2,3
  • 结果分析

    • 纵坐标平均loss(误差)接近0.0359,即变化不大时可停止训练。横坐标batch(每batch(64)个样本更新一次参数)
    • 当前训练的迭代次数: 6478,总体的 Loss(损失):0.0279,平均 Loss:0.0359
    • 测试效果:
       
      1
      ./darknet detect cfg/my\_yolov3.cfg weights/my\_yolov3.weights 72.jpg