yolov4训练自己的数据模型

2023-11-16

看了下yolov4的作者给的操作说明,链接如下:https://github.com/AlexeyAB/darknet#how-to-compile-on-linux-using-make,有兴趣的可以去看看,总结起来,跟yolov3的操作方式基本一样,所以现在记录一下这次的整个操作流程。

在几个月前,一直在准备一个项目,那个项目已经让人用lableme这个标注软件标注好了图片,但是直到现在,这个项目依旧还没有动工的苗头,估计是悬了,之前也用标注好的数据进行过yolov3的训练,但是说实话,那时候对于yolov3是一头雾水,只知道按照他人的给的操作流程去做,但是为什么这样做,我是不清楚的,这也导致我自己都不知道当初的训练模型的流程是否正确,现在好了,前段时间把yolov3,ssd学习了,现在来操作,就知道这样操作的原理了,整个操作流程就知道的很详细了,也知道怎么训练自己的数据了,不再茫然。

环境说明:yolo系列一直都有linux版本和windows版本,我看了哈,很明显linux版本更加简单,windows版本还需要搭配vs来操作,光vs这个软件就十几个g,太大了,所以我就采取在ubuntu上训练数据了。

首先,要感谢下面的这篇文章的作者:https://www.cnblogs.com/Assist/p/11091501.html,这篇文章为我梳理了整个思路,这也为我想处理自己的数据,训练自己的模型有了想法。

一般来说,训练coco, vol这些数据集,网上很多教程来进行数据的预处理,但是训练自己的数据,就得自己根据网上的教程来改代码了,因为coco这些数据集的标注文件和图片存储结果可能跟自己的数据都不一样,对于我用labelme来标注的图像来说,每一张图片会对应一个json文件,这个json文件里面拥有这标注的类别坐标这些信息,所以,我这里值将所有图片放到一个文件夹,所有json文件放到一个文件夹,但是为了区分训练数据和val数据,我将json进行了划分,一部分放到了train文件夹,一部分放到了val文件夹,最后整个文件的结构如下:
在这里插入图片描述接下来咱就可以开始正式的数据预处理了,这一块是最麻烦的,我个人觉得哈。我们运行第一个Py文件后,要创建出下面这几个文件夹:

在这里插入图片描述
第一个文件夹用来装xml文件,这个xml文件里面有标签类别,图片的宽高,标签的左上角和右下角坐标这些信息,每一张照片对应一个xml文件,imagesets这个文件夹里面还有一个文件夹:/Main, 在这个Main文件夹下面会产生train.txt和val.txt,这些文件里面的信息只是图片的名字,不带.jpg这些后缀的名字,而在JPEGImages里面就是所有图片了。

我们假设这第一个py文件叫convertvoc.py,我下面提供我的代码,大家不能照搬,有些地方要改。

'''author:nike hu'''
# -*- coding: utf-8 -*-

import shutil
import os
import json
import cv2

headstr = """\
<annotation>
    <folder>VOC2007</folder>
    <filename>%06d.jpg</filename>
    <source>
        <database>My Database</database>
        <annotation>PASCAL VOC2007</annotation>
        <image>flickr</image>
        <flickrid>NULL</flickrid>
    </source>
    <owner>
        <flickrid>NULL</flickrid>
        <name>company</name>
    </owner>
    <size>
        <width>%d</width>
        <height>%d</height>
        <depth>%d</depth>
    </size>
    <segmented>0</segmented>
"""
objstr = """\
    <object>
        <name>%s</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>%d</xmin>
            <ymin>%d</ymin>
            <xmax>%d</xmax>
            <ymax>%d</ymax>
        </bndbox>
    </object>
"""

tailstr = '''\
</annotation>
'''

# 上面的不用改


def writexml(idx, head, bbxes, tail):
    filename = ("Annotations/%06d.xml" % (idx))
    f = open(filename, "w")
    f.write(head)
    for bbx in bbxes:
        f.write(objstr % (bbx[0], bbx[1], bbx[2], bbx[3], bbx[4]))
        # 这里就是将文件中标签类别,左上角和右下角坐标存进去
    f.write(tail)
    f.close()

# 这个函数不用改
def clear_dir():
    if shutil.os.path.exists(('Annotations')):
        shutil.rmtree(('Annotations'))
    if shutil.os.path.exists(('ImageSets')):
        shutil.rmtree(('ImageSets'))
    if shutil.os.path.exists(('JPEGImages')):
        shutil.rmtree(('JPEGImages'))

    shutil.os.mkdir(('Annotations'))
    shutil.os.makedirs(('ImageSets/Main'))
    shutil.os.mkdir(('JPEGImages'))


def excute_datasets(json_path, tr, idx):
    json_path = os.path.join(json_path, tr)
    json_file = os.listdir(json_path) # 读取文件夹下的所有json文件
    savename = open(('ImageSets/Main/' + tr + '.txt'), 'a') # 写入图片名字
    for file in json_file:
        file_path = os.path.join(json_path, file) # 找到json文件路径
        with open(file_path, 'r', encoding='utf-8') as f: # 开始读取json文件
            file_json = json.load(f)
            imagename = file_json["imagePath"].split('\\')[-1] # 找到当前json文件对应的图片名字
            image_path = os.path.join('./images', imagename) # 找到图片的路径,这里根据不一样的情况,也不一样,需要改
            image = cv2.imread(image_path)
            if image is None: # 如果没有这种照片,跳过
                continue
            label_shape_type = file_json['shapes'][0]['shape_type']
            if label_shape_type != 'rectangle': # 暂时不考虑其它形状的标签,在labelme这个软件打标签的时候有rectangle,还有多边形圆形这些,这些需要另外处理
                continue
            head = headstr % (idx, image.shape[1], image.shape[0], image.shape[2])
            # 这里是把对应图片路径,图片长,维度,宽存进去
            shapes = file_json['shapes'] # 这里是存储标签和坐标的位置,是一个列表,列表里面有很多字典,每个字典就代表一个标签
            boxes = []
            for i in range(len(shapes)):
                classname = file_json['shapes'][i]['label'] # 类别
                '''接下来转化类别为英文,因为labelme在标注的时候,为了标注人员的遍历,类别是中文,但是我们训练模型的时候必须是英文,这里就需要转化了,这里得xxxx不代表真的是xxxx,是你自己训练的类别,为了不让我老板看出我做的是他的项目,这里隐藏了'''
                if 'xxxxxxx' in classname:
                    classname = 'xxxxxxx'
                if 'xxxxxxx' in classname:
                    classname = 'xxxxxxx'
                if 'xxxxxxx' in classname:
                    classname = 'xxxxxxx'
                if 'xxxxxxx' in classname:
                    classname = 'xxxxxxx'
                if 'xxxxxxx' in classname:
                    classname = 'xxxxxxx'
                if 'xxxxxxx' in classname:
                    classname = 'xxxxxxx'

                box = [classname, file_json['shapes'][i]['points'][0][0],
                       file_json['shapes'][i]['points'][0][1], file_json['shapes'][i]['points'][1][0],
                       file_json['shapes'][i]['points'][1][1]] # 这里存储的意义是[标签类别, x1,y1, x2, y2]
                boxes.append(box)
            writexml(idx, head, boxes, tailstr)
            cv2.imwrite('JPEGImages/%06d.jpg' % (idx), image)
            savename.write('%06d\n' % (idx))  # 写入图片编号
            idx += 1
    savename.close()
    return idx


if __name__ == '__main__':
    clear_dir()
    idx = 1
    idx = excute_datasets('./jsonhot', 'train', idx) # 其实主要改的地方是这,'./jsonhot'代表json文件的目录
    idx = excute_datasets('./jsonhot', 'val', idx) # 还有这也要改,再就是上面那个函数有个地方也要改
    print('Complete...')


大概需要改的地方我都标注出来了,大家根据自己的实际情况去改上面的代码。上面的代码借鉴了我最上面给的作者的一些代码,毕竟原理都这样,只需要改一改细节上的东西。之前写过一篇文章,在构造xml结构的时候用的是xml这个库区一个一个节点的构造,现在看来,还是直接向上面的代码那样做简单很多。

我们运行这个代码之后,就会达到我们上面说的目的,生成annotations这些文件夹和数据,这个py文件运行完后大家一定要去看看产生了什么数据,不然对后面的操作会懵逼的,接下来,我们要写一个py文件,我们假设这个文件叫getdata.py,我们要达到的目的是:在主目录下生成两个文件夹train.txt和val.txt,这文件名字随意,这两个文件里面存储的是图片的绝对路径,比如:/media/yunyi/file/code/darknet/data/voc/VOCface/JPEGImages/000003.jpg,这个Py文件运行后还会生成一个label文件夹,这个文件夹里面也会生成以图片名称命名的txt文件,这些文件保存的是图片中的标签类别,中心点的x,中心点的y,标签的宽,边框的高,接下来,我们看看代码:

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join

sets=['train', 'val']#

classes = ['......'] # 这里是你要处理的数据的类别总数

# 这里是将左上角和右下角的坐标转化为中心点和宽高
def convert(size, box):
    dw = 1./size[0]
    dh = 1./size[1]
    x = (box[0] + box[1])/2.0
    y = (box[2] + box[3])/2.0
    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(image_id):
    in_file = open(wd + '/Annotations/%s.xml'%(image_id))
    out_file = open( wd + '/labels/%s.txt'%(image_id), 'w')
    tree=ET.parse(in_file) # 导入xml数据
    root = tree.getroot() # 得到跟节点
    size = root.find('size') # 找到根节点下面的size节点
    w = int(size.find('width').text) # 得到图片的尺寸
    h = int(size.find('height').text)

    for obj in root.iter('object'): # 对根节点下面的'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')



if __name__=='__main__':
    wd = getcwd() # 获取当前文件的路径
    wd = wd.replace('\\', '/')
    for image_set in sets: # image_set是train或者val
        if not os.path.exists(wd + '/labels/'): # 创建一个label文件夹来存放图片对应的类别和坐标
            os.makedirs(wd + '/labels/')
        image_ids = open(wd +'/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(wd + '/JPEGImages/%s.jpg\n' % image_id)
            convert_annotation(image_id)
        list_file.close()


代码里面很多xml.find()啥的函数,就是找xml里面的节点用的,这些代码,我个人感觉当我们第一个py文件执行之后,这个py文件改一改类别就可以运行了,当然,有可能会出现意外情况,这时候根据不同的报错解决吧,我这反正是没问题的。

好了,最麻烦的预处理弄完了,接下来咱看看怎么配置yolov4了,直接看图片,形象:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这个yolov4.cfg里面,我们一般要更改的就是batchsize,subdivisions, classes,这几个参数,对于filters=255这一行,根据上面的说明进行更改,max_batches = 500200,steps=400000,450000这两行大家根据实际情况看该不该,电脑配置给力就没必要改,配置不行就改一下,一般是改为 max_batches = 2000,steps=1600,1800,大家更改的时候直接ctrl+f进行定位就行。

对cfg文件进行更改之后,我们就要创建两个文件了,比如叫face.data,face.names,名字你随意,在face.data里面,你的内容范本如下:
在这里插入图片描述
classes 就是你的数据的类别总数,train就是你上面getdata.py生成的train.txt文件的路径,这里是相对路径,val同理,names就是刚刚创建的face.names的路径,backup就是训练过程保存模型的路径。至于face.names,里面的内容就是你的类别名字了,类似下面这张:
在这里插入图片描述
做了上面那些工作,终于可以开始训练了,只需要一行命令:./darknet detector train data/xxx.data cfg/yolov4.cfg yolov4.conv.137 -map,其中yolov4.conv.137是预训练模型,需要另外进行下载,我有空去下载好然后分享一下,如果没有这个文件,那么只需要执行./darknet detector train data/xxx.data cfg/yolov4.cfg -map(没有-map也可以)即可,yolov4和yolov3训练的时候区别就出来了,yolov4训练的时候会用一张动态图来显示训练的效果,如下所示:
在这里插入图片描述
至于训练的效果,哎,不提了,我的gtx2060还在学校,现在还没有返校,只能用多年前的笔记本了,这笔记本显卡是720m,但是不知道是不是cuda这些版本没有选择好,yolo不能使用我这台的gpu,以前折腾了很久,最后放弃了,所以,我只能用cpu来跑,但是我这cpu还是四代i5的,跑不动,所以。。。。。。。。没有训练效果。

好了,训练过程终于理清并且写完了,觉得有用的,留个赞再走呗。

补充:刚刚把作者给的预训练模型下载了,免费分享链接如下:https://blog.csdn.net/yapifeitu/article/details/105756274,拿走的时候记得点赞哦。

ps:5.7号用百度的显卡跑了一下yolov3的模型,果然出问题了,最后,补上上面没有说到的地方:我们在训练的时候,对于cfg文件,我们要将test注释掉,类似于下面这种:
在这里插入图片描述哎,就这里没注释然后去跑模型,让我纠结了好久,希望大家后面注意。

5.8号我在aistudio上用上面的tesla v100跑了一个人脸检测的yolov4模型(由于这篇文章涉及到的数据是我老板发钱买来的,不适合放到公开平台去,所以就用公开数据集跑了人脸检测的模型),显卡给力,跑模型的速度就给力,模型跑的avgloss在3左右后一直在这附近徘徊,知道应该是降不下去多少了,然后把模型下载了,放到自己的的电脑上来测试了几张照片,效果挺不错的,给大家看看效果吧:
在这里插入图片描述
很明显这里误检测了一个地方,其余的16张人脸都检测到了很明显这里误检测了一个地方,其余的16张人脸都检测到了,还别说,那个误检测的还真有些像。

在这里插入图片描述至于这张图片,用yolov4训练的模型,检测完美,但是用yolov3训练好的模型然后用yolov3去测试,会出现误检的情况,不知道是不是模型训练不够的原因。

2020 4.25

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

yolov4训练自己的数据模型 的相关文章

  • 深度学习(十九)——FCN, SegNet, DeconvNet, DeepLab, ENet, GCN

    前DL时代的语义分割 续 Grab cut Grab cut是微软剑桥研究院于2004年提出的著名交互式图像语义分割方法 与N cut一样 grab cut同样也是基于图划分 不过grab cut是其改进版本 可以看作迭代式的语义分割算法
  • JDBC操作

    目录 一 实现JDBC步骤 1 注册驱动 1 1导入驱动包 1 1异常处理 2 创建连接 2 1导包 2 2处理异常 3 得到执行sql语句的Statement对象 3 1修改数据操作 3 2删除数据操作 3 3插入数据操作 3 4查询数据
  • vue-quill-editor富文本编辑器的汉化版 及 使用心得

    现在网上上有很多的富文本编辑器 但我个人还是非常喜欢Vue家族的vue quill deitor 虽然说它只支持IE10 好 废话不多说直接上代码 现在是见证奇迹的时刻 在vue中使用quill呢 我们需要npm进行安装 安装命令如下 第一
  • spring security 实现免登陆功能

    spring security 实现免登陆功能大体也是基于COOKIE来实现的 主要配置信息

随机推荐

  • Spring Boot系列之修改内置Tomcat版本

    背景 在 spring boot 出来之前 或者没有使用 spring boot 时 Java EE 开发时如果选择 tomcat servlet 需要自己指定 tomcat 版本 此处没有考虑那种直接把打包的 war 直接扔到本地安装的任
  • oracle云避坑小记

    前言 最近白嫖oracle云 用于评估arm64 架构的服务器 发现 oracle 云系统和国内的主要云服务厂商 如 阿里云或者腾讯云 默认的一些策略有所不同 以下是一些避坑指南 一 避坑小记 基于 oracle linux 8 关闭 fi
  • 《代码大全2》第3章 三思而后行,前期准备

    目录 前言 本章主题 3 1 前期准备的重要性 3 1 1 处于不同阶段强调质量 3 1 2 前期准备对 构建活动 的影响 3 1 3 准备不周全的诱因 3 1 4 我理解的准备周全 纯属个人理解 3 2 辨明你所从事的软件的类型 3 2
  • vue.config.js

    vue config js相关的知识信息 一 vue config js是vue打包管理的配置文件 旨在给开发者们自定义自己的配置 1 该文件的根式统一 为导出配置项选项 例如 在对象里面书写我们自己的配置项目 二 具体的配置内容 项目中常
  • 0x00007FFD33144F99处(位于xx.exe中)引发的异常:Microsoft C++异常 查处方法

    一般这样的异常都是try catch语句有异常抛出 比如新建一个工程 int main try throw 1 catch int excep if excep 1 printf throw 1 n return 0 运行就会在输出的调试信
  • CAD球体密堆积3D插件 随机紧密堆积 球体堆积结构

    插件简介 CAD球体密堆积3D插件可用于生成随机紧密堆积的球体模型 插件可指定投放区域 球体集料的粒径范围 球体数量等信息 插件采用模拟重力作用下球体的碰撞堆积行为 实现球体集料的随机紧密堆积模型 插件通过AutoCAD软件进行绘图 生成的
  • CloudCompare——点云标注

    目录 1 概述 2 软件实现 3 合并点云 1 概述 对给定的点云添加分类标签 2 软件实现 1 裁剪点云 裁剪出需要标注的部分 并选中 2 进行标注 工具栏操作 Edit gt Scalar fields gt Add constant
  • [Python入门系列之十]Python 中的类和对象

    Python 中的类和对象 类和对象是面向对象编程 Object Oriented Programming 的基础 类是一种用户定义的数据类型 它封装了属性和方法 用于描述某一类对象的行为和特征 而对象则是类的实例化 是具体的 实际存在的实
  • Vue+ElementUI电商项目(六)

    订单列表 创建订单列表路由组件并添加路由规则 在view中新建orderManagement文件夹 新建Order vue组件 组件中添加代码如下
  • Pandas基本操作——增加、修改和删除

    数据的增加 按列增加数据 按行增加数据 增加多行数据 修改数据 修改列标题 使用DataFrame对象的columns属性直接赋值 使用DataFrame对象的rename方法修改列标题 修改行标题 使用DataFrame对象的index属
  • vue3:el-table多选框设置默认选中,翻页保留选中状态

    问题 el table多选框设置默认选中 进行翻页 之前选中的数据没有保留选中状态
  • 设计模式-模板方法模式

    一 模板方法模式 定义 定义一个操作中的算法骨架 而将一些步骤延迟到子类 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤 类型 行为型模式 特点 通过把不变的行为搬移到超类 去除子类中的重复代码来体现它的优势 提供了
  • 【4】数据结构与算法--- 数据结构 进阶

    第 3 章 数据结构 进阶 3 1 线性表 线性表 按照某种线性关系存储下来的表 分类 线性表 说明 顺序表 将数据放在一个连续的存储空间 链表 把数据分散存储 按照某种关系连成串分类 单向链表 双向链表 单向循环链表 3 2 顺序表 3
  • python怎么一次输入两个数_python如何一次性输入多个数

    python一次性输入多个数的方法 1 输入两个数字 m n map int input split 2 输入三个及三个以上数字 a b c d map int input split python一次性输入多个数的方法 1 输入一个数字直
  • 在NLP上,CNN、RNN、MLP三者相比各有何优劣

    本文为知乎温颖就如下问题的回答 已授权CSDN转载 若想要实现某个具体的任务 如做关系抽取 实体识别 情感分类等 在不考虑实现的难度的情况下 如何从理论 经验 直觉上去选择最有希望的模型 前段时间做过用不同的神经网络模型做文本分类 情感分析
  • Linux(vi基本用法)

    在Linux下 可以键入vimtutor命令 有一个包含实操的vim教程 1 VI的三种命令模式 1 Command 命令 模式 用于输入命令 2 Insert 插入 模式 用于插入文本 3 Visual 可视 模式 用于视化的的高亮并选定
  • 报错:flask: TypeError: ‘function‘ object is not iterable

    错误 TypeError function object is not iterable Type错误 表示 函数 对象不是可迭代的 这是我在学习flask时在html模板中 进行for循环遍历闪现消息时缺少 导致遍历对象为一个函数 报错代
  • tomcat虚拟目录和虚拟主机等相关配置

    一 WEB 服务器 1 什么是WEB 服务器 就是一台电脑 安装了一个服务器软件 2 为什么需要安装 WEB 服务器 思考问题 从一台计算机的 IE 浏览器如何去访问另一台计算机中的文件 2 1 两台计算机是如何实现通讯的 IP地址 计算机
  • mesa 教程

    只有这个是靠谱的 Compiling and Installing The Mesa 3D Graphics Library latest documentation
  • yolov4训练自己的数据模型

    看了下yolov4的作者给的操作说明 链接如下 https github com AlexeyAB darknet how to compile on linux using make 有兴趣的可以去看看 总结起来 跟yolov3的操作方式