yolov5ds训练步骤

目录

参考链接

以下步骤是参考:用YOLOv5ds训练自己的数据集——同时检测和分割

0、配置环境

不在赘述,跟YOLOv5差不多

1、下载预训练模型——推荐

在yolov5ds-main根目录新建weights文件夹

下载yolov5预训练模型Releases · ultralytics/yolov5 · GitHub放到weights文件夹中

我下载的是 yolov5s.pt,下面均以 yolov5s.pt为例

2、准备数据集——非常关键

在yolov5ds-main根目录新建 paper_data文件夹

paper_data文件夹下新建 detseg两个文件夹

det文件夹存放检测数据集

seg文件夹存放分割数据集

🍀对于数据集,我的整体步骤是:

(1)运行:paper_data/det/json2txt.py,生成json对应的txt文件

(2)运行:paper_data/det/split.py,以9:1划分train、val(没有划分test),存放到paper_data/det/ImageSet/Main

(3)运行:paper_data/det/voc_labels.py,通过(2)划分的数据集将文件绝对路径存放到相应txt中,存放到paper_data/det

(4)运行:paper_data/seg/getmask.py,将raw_data中所有的.png图片复制到paper_data/seg/labels中

(5)运行:paper_data/seg/segsplit.py,按照train.txt和val.txt划分到paper_data/seg/images或labels下的train、val文件夹下

❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

关于以下我制作数据集的代码,我都分享出来了,有需要的好兄弟自取:👉百度网盘:paper_data

2-1、det文件夹下

images文件夹下存放 .jpg图像(或者.png格式)

labels文件夹下存放 .txt标签文件

Annotations文件夹下存放 xml标签文件(这个在训练过程其实用不到,只是如果标签文件是xml格式的话,就暂存在这个文件夹,然后会通过 3.voc_labels.py转换成txt格式且划分到labels中的train或val子文件下)

注:xml、txt文件应和对应图像名称相同

再注:

  • 因为,我的数据集格式只有实例分割数据集:jpg图像+json格式标签,所以这里还多了json2txt.py文件
  • 有xml检测标签的,就直接执行2、3步
  • *直接有txt,也只执行2、3步,但是第3步中要注释掉调用 convert_annotation(image_id)函数

1. json2txt.py

(1)paper_data/det/ json2txt.py,生成json对应的txt文件

import json
import os
import os.path
import re

def getclass(rootdir):
    classes = []
    for file in os.listdir(rootdir):
        load_f = open(os.path.join(rootdir, file), 'r')
        load_dict = json.load(load_f)
        objects = load_dict['shapes']

        for i in range(0, len(objects)):
            label = objects[i]['label']
            if label not in classes:
                classes.append(label)

    return classes

def image_id(rootdir):
    a = []

    for root, dirnames, filenames in os.walk(rootdir):
        for filename in filenames:
            filename = filename.strip('.json')
            a.append(filename)
    return a

def position(pos):

    x = []
    y = []
    nums = len(pos)
    for i in range(nums):
        x.append(pos[i][0])
        y.append(pos[i][1])
    x_max = max(x)
    x_min = min(x)
    y_max = max(y)
    y_min = min(y)
    b = (float(x_min), float(x_max), float(y_min), float(y_max))
    return b

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(rootdir, out_file, cls_list, image_id):

    load_f = open(rootdir + '/{0}.json'.format(image_id), 'r')
    load_dict = json.load(load_f)

    out_file = open(out_file + '/{0}.txt'.format(image_id), 'w')

    w = load_dict['imageWidth']
    h = load_dict['imageHeight']

    objects = load_dict['shapes']
    nums = len(objects)

    for i in range(0, nums):
        print(image_id + '第{}个'.format(i))
        pos = objects[i]['points']
        box = position(pos)
        bb = convert([w, h], box)

        cls = objects[i]['label']

        if re.match('Support', cls) is not None:
            cls_id = 0
        elif re.match('Bolt_hole', cls) is not None:
            cls_id = 1
        elif re.match('Grouting_hole', cls) is not None:
            cls_id = 2
        elif re.match('Cable', cls) is not None:
            cls_id = 3
        elif re.match('Pipe', cls) is not None:
            cls_id = 4
        elif re.match('Signal_light', cls) is not None:
            cls_id = 6
        elif re.match('Sign', cls) is not None:
            cls_id = 5
        elif re.match('Railway', cls) is not None:
            cls_id = 7
        elif re.match('PJB', cls) is not None:
            cls_id = 8
        elif re.match('Instrument_box', cls) is not None:
            cls_id = 9
        elif re.match('Crack', cls) is not None:
            cls_id = 10
        elif re.match('Falling_block', cls) is not None:
            cls_id = 11
        else:
            cls_id = -1

        out_file.write(str(cls_id) + " " + " ".join([str(round(a, 3)) for a in bb]) + '\n')

if __name__ == '__main__':

    rootdir = '../../raw_data/labels-json'

    cls_list = getclass(rootdir)
    print(cls_list)

    out_file = 'labels'
    if not os.path.exists(out_file):
        os.makedirs(out_file)

    ids = image_id(rootdir)

    for id in ids:
        convert_annotation(rootdir, out_file, cls_list, id)
        print(id + '.json' + '已转换')

    print('over!')

2. split.py

det文件夹下新建一个 split.py文件,使用下面代码生成 ImageSets,里面有一个 Main文件夹, Main文件夹里包括 test.txttrain.txttrainval.txtval.txt四个文本文档

  • trainval.txt包含你数据集里所有图像名称
  • train.txt为数据集的训练集,为总数据集的90%
  • val.txt为数据集的验证集,为总数据集的10%
  • test.txt文件里是空的不用担心,因为没有划分测试集

import os
import random
import argparse

parser = argparse.ArgumentParser()

parser.add_argument('--xml_path', default='./images', type=str,
                    help='input xml label path')

parser.add_argument('--txt_path', default='./ImageSets/Main', type=str,
                    help='output txt label path')
opt = parser.parse_args()

trainval_percent = 1.0
train_percent = 0.9
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)
if not os.path.exists(txtsavepath):
    os.makedirs(txtsavepath)

num = len(total_xml)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)

random.seed(0)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)

file_trainval = open(txtsavepath + '/trainval.txt', 'w')
file_test = open(txtsavepath + '/test.txt', 'w')
file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/val.txt', 'w')

for i in list_index:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        file_trainval.write(name)
        if i in train:
            file_train.write(name)
        else:
            file_val.write(name)
    else:
        file_test.write(name)

file_trainval.close()
file_train.close()
file_val.close()
file_test.close()

3. voc_labels.py

更改记录:

  1. 添加了 abs()函数,以保证转换为txt时都是正数,不然可能运行代码时加载数据会出错
  2. out_dir如果不存在则创建

import xml.etree.ElementTree as ET
import os
from os import getcwd

sets = ['train', 'val', 'test']
classes = ['Bolt_hole', 'Grouting_hole', 'Crack']
abs_path = os.getcwd()
print(abs_path)

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(os.path.join(r'E:\A_new_dataset\A_tunnel_crack\labels_xml', f'{image_id}.xml'), encoding='UTF-8')
    out_dir = os.path.join(abs_path, 'labels')
    if not os.path.exists(out_dir):
        os.makedirs(out_dir)
    out_file = open(os.path.join(out_dir, f'{image_id}.txt'), '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'):
        cls = obj.find('name').text
        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))
        b1, b2, b3, b4 = b

        if b2 > w:
            b2 = w
        if b4 > h:
            b4 = h
        b = (b1, b2, b3, b4)
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(abs(a)) for a in bb]) + '\n')

wd = getcwd()
for image_set in sets:
    if not os.path.exists('./labels'):
        os.makedirs('./labels')
    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(abs_path + '/images/%s.png\n' % (image_id))
        convert_annotation(image_id)
    list_file.close()

运行之后会在det目录下生成train.txt、test.txt、val.txt三个文件,对应的图像名称前加入了绝对路径

运行完以上代码后,det文件夹结构为:

yolov5ds训练步骤

2-2、seg文件夹下

images存放图像

labels存放分割图像,类似下面这种图

(经过下面的 segsplit.py代码后,会在images、labels里面分别创建train、val两个子文件夹,这里的train和val里的图要与上述提到的 det/Main/train.txtval.txt里分好的相对应)

注:labels图像应和images对应图像名称相同,格式为 png

这种图是用labelme标注后,使用labelme里自带的一个程序生成

yolov5ds训练步骤

通过网上批量转换labelme生成的json代码,生成了如下的文件夹,然后下面的 getmask.py就是实现将这些文件夹中的label.png全部提出来放到一起

yolov5ds训练步骤

; 1. getmask.py

"""
将所有json转成的label.png拷贝到专门的文件夹
"""

import shutil
import os

json_dir = '../../raw_data/labels-json-labelmeout'
mask_dir = 'labels'

if not os.path.exists(mask_dir):
    os.makedirs(mask_dir)

jsonlist = os.listdir(json_dir)

for x in jsonlist:

    file_new_path = shutil.copy(os.path.join(json_dir, x, 'label.png'), os.path.join(mask_dir, x[:-5] + '.png'))
    print(x + '.png' + ' done.')

2. segsplit.py

import os
import shutil

"""
实现:
1. 将train.txt 和 val.txt中指定的原始图像【复制】到seg/images/train子文件或者val子文件
2. 将train.txt 和 val.txt中指定的原始图像对应的标签png图像,移动到seg/labels/train子文件夹或val子文件夹
"""

def openreadtxt(file_name):
    data = []
    f = open(file_name, 'r')
    for row in f.readlines():
        tmp_list = row.split('\n')[0]
        data.append(tmp_list)
    f.close()
    return data

if __name__ == '__main__':

    labels_dir = r'E:\A_new_dataset\A_tunnel_crack\masks'

    images_train_dir = './images/train'
    images_val_dir = './images.val'
    labels_train_dir = './labels/train'
    labels_val_dir = './labels/val'

    os.makedirs(images_train_dir)
    os.makedirs(images_val_dir)
    os.makedirs(labels_train_dir)
    os.makedirs(labels_val_dir)

    train_list = openreadtxt('../det/train.txt')
    val_list = openreadtxt('../det/val.txt')
    print(train_list, len(train_list))
    print(val_list, len(val_list))

    for file_path in train_list:
        file = file_path.split('/')[-1]
        filename, _ = os.path.splitext(file)

        shutil.copy(file_path, os.path.join(images_train_dir, file))

        labels_name = filename + '.png'
        shutil.copy(os.path.join(labels_dir, labels_name), os.path.join(labels_train_dir, labels_name))

    for file_path in val_list:
        file = file_path.split('/')[-1]
        filename, _ = os.path.splitext(file)

        shutil.copy(file_path, os.path.join(images_val_dir, file))

        labels_name = filename + '.png'
        shutil.copy(os.path.join(labels_dir, labels_name), os.path.join(labels_val_dir, labels_name))

经过以上两个代码之后的 paper_data/seg 文件夹结构:

yolov5ds训练步骤

到此,检测、分割的数据集都做好了,paper_data数据集总结构为:

yolov5ds训练步骤

3、配置文件参数修改

3-1、models/segheads.yaml

  • segnc:改为自己的分割类别数 + 1(这里一定要+1)

3-2、data/voc.yaml

  • train:改为自己det文件夹下train.txt路径
  • val:改为自己det文件夹下val.txt路径
  • road_seg_train:改为自己seg文件夹下images/train文件夹路径
  • road_seg_val:改为自己seg文件夹下images/val文件夹路径
  • nc:改为自己的检测类别数
  • segnc:改为自己的分割类别数(这里一定不要+1 !!!)

3-3、models/yolov5s.yaml

nc:改为自己的检测类别数

3-4、trainds.py

parse_opt函数下修改对应 default里面的内容

关于resume断点训练、继续训练的方法:yolov5ds-断点训练、继续训练(yolov5同样适用)

yolov5ds训练步骤

; 4、遇到的问题

4-1、运行trainds.py

  • 出现报错: RuntimeError: weight tensor should be defined either for all or no class at ...

原因:这个文件中计算分割损失时,没有初始化weight,所以无法设置weight(可能是我自己的torch.nn有问题?)

yolov5ds训练步骤

解决方案:在 trainds.py 中用 Ctrl+F 搜索定位到 SegLoss = nn.CrossEntropyLoss,然后去除掉里面的weight参数,改为以下:

SegLoss = nn.CrossEntropyLoss(ignore_index=255)

4-2、运行detectds.py

  • 出现报错: RuntimeError: Input type (torch.cuda.HalfTensor) and weight type (torch.HalfTensor)

原因:输入放在的gpu上,权重却没有放在gpu上,导致数据类型不一致

解决方案:在 detectds.py 中用 Ctrl+F 搜索定位到 model = ckpts['model']位置,然后在下方加上一行代码:

model = model.cuda()

  • IndexError: index 1 is out of bounds for axis 0 with size 1

yolov5ds训练步骤

Original: https://blog.csdn.net/LWD19981223/article/details/125921793
Author: 孟孟单单
Title: yolov5ds训练步骤

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/628962/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球