YOLO v11的学习记录(五) 使用自定义数据从头训练一个实例分割的模型

1、准备数据

上一篇,使用AnyLabeling对图片进行了标注,当标注完成后,在图片所在的文件夹内会生成与每个图片同名的json文件,当再次使用AnyLabeling导入图片文件夹时,json文件会一同被导入,方便再次编辑和导出。这些json文件内包含了每个同名图片的标注数据。

这篇,将学习如果获取用于YOLO实例分割的数据集。


  • 目录结构

预期的YOLO项目下训练文件的目录结构是这样的:

python 复制代码
ultralytics-main
├──datasets
   ├── images/
   │   ├── train/
   │   ├── var/
   │   ├── test/
   ├── labels/
   │   ├── train/
   │   ├── valr/
   │   ├── test/
   ├── dataset.yaml

imags目录下保存图片,按照用途的不同又分为train(训练集)、var(验证集)和test(测试集),labels目录下保存与images目录下图片文件的同名txt文件,dataset.yaml是训练配置文件。如图:


  • 标注文件格式

YOLO对实例分割的标注文件格式要求如下:

https://blog.csdn.net/xulibo5828/article/details/145657008

每行的内容:

class x1 y1 x2 y2... xn yn

其中 class 是类别索引,后面跟着目标轮廓上一系列点的归一化坐标(x 和 y 交替出现)。


之前用AnyLabeling对图片标注完成后,在图片所在的文件夹内生成的与图片同名的json文件,其内容格式如下:

python 复制代码
{
  "version": "0.4.30",
  "flags": {},
  "shapes": [
    {
      "label": "人",
      "text": "",
      "points": [
        [
          303.0,
          146.0
        ],
        [
          295.0,
          152.0
        ],        
      ],
      "group_id": null,
      "shape_type": "polygon",
      "flags": {}
    },
    {
      "label": "钟表",
      "text": "",
      "points": [
        [
          0.0,
          0.0
        ],
        [
          0.0,
          34.0
        ],
        [
          9.0,
          34.0
        ],
        [
          10.0,
          33.0
        ]
      ],
      "group_id": null,
      "shape_type": "polygon",
      "flags": {}
    }
  ],
  "imagePath": "000000000294.jpg",
  "imageData": null,
  "imageHeight": 427,
  "imageWidth": 640
}

其中的"label"是标签名称,"points"是轮廓点位。我需要做的就是将这两个内容提取出来,写入每一个与图片同名的txt文件。


生成数据,复制文件

创建以下脚本在项目根目录下,并运行:

python 复制代码
import json
import os
import shutil
from pathlib import Path

import yaml


def convert_AnyLabeling_to_YOLO(source_path, target_path, copy_image=False, divide_train_val=True):
    # 创建目标文件夹
    os.makedirs(os.path.join(target_path, 'labels', 'train'), exist_ok=True)
    os.makedirs(os.path.join(target_path, 'labels', 'val'), exist_ok=True)
    os.makedirs(os.path.join(target_path, 'labels', 'test'), exist_ok=True)
    os.makedirs(os.path.join(target_path, 'images', 'train'), exist_ok=True)
    os.makedirs(os.path.join(target_path, 'images', 'val'), exist_ok=True)
    os.makedirs(os.path.join(target_path, 'images', 'test'), exist_ok=True)

    file_idx = 0   # 当前处理文件的索引(用来区分训练集和验证集)
    classes_file = os.path.join(source_path, 'classes.txt')  # 类别文件,所有分类标签
    yaml_file = os.path.join(target_path, 'dataset.yaml')  # 设置文件
    #  读取json文件
    jsons = list(Path(source_path).resolve().glob("*.json"))  # 获取json文件列表
    jsons_count = len(list(jsons))  # json文件数量

    # 标签类别文件的处理
    if not os.path.exists(classes_file):   # 如果不存在标签类别文件,则创建一个空文件
        with open(classes_file, 'w', encoding='utf-8') as cf:
            cf.write('')
    with open(classes_file, 'r+', encoding='utf-8') as cf:
        classes = cf.read().splitlines()   # 读取标签类别文件中的所有标签,并存储在列表中

        # 从json文件中读取标签类别并写入标签类别文件
        for file in jsons:
            with open(file, 'r', encoding='utf-8') as jf:
                data = json.load(jf)
                for d in data['shapes']:
                    if d['label'] not in classes:
                        classes.append(d['label'])
                        cf.write(f"{d['label']}\n")


    if divide_train_val and jsons_count < 10:
        print(f"{source_path} 文件夹中json文件数量小于10,不进行转换")
        return

    # 开始转换json文件
    for json_file in jsons:
        # 读取json文件并获取数据
        file_name = json_file.stem   # 文件名(不带后缀)
        with open(json_file, encoding="utf-8") as f:
            data = json.load(f)  # json数据
        # 数据集的划分
        if file_idx < jsons_count * 0.7:
            purpose = 'train'
        elif file_idx < jsons_count * 0.9:
            purpose = 'val'
        else:
            purpose = 'test'
        if not divide_train_val:   # 如果不划分训练集和验证集,则全部写入训练集
            purpose = 'train'

        h, w = data['imageHeight'], data['imageWidth']  # 图片的宽度和高度
        # 写入标注文件
        with open(os.path.join(target_path, 'labels', purpose, file_name + '.txt'), 'w') as f:
            for d in data['shapes']:
                label_idx = str(classes.index(d['label']))   # 类别在类别表中的索引顺序
                points = d['points']
                line = label_idx    # 写入类别索引(每一行的第一个数字)
                # 写入坐标点
                for point in points:
                    x = point[0] / w
                    y = point[1] / h
                    x = max(0, min(1, x))
                    y = max(0, min(1, y))
                    x = round(x, 6)
                    y = round(y, 6)
                    line += ' ' + str(x) + ' ' + str(y)
                line += '\n'  # 写入换行符
                f.write(line)

        # 如果需要复制图片到images文件夹中,则复制图片到images文件夹中
        if copy_image:
            jpg_file = os.path.join(source_path, file_name + '.jpg')
            png_file = os.path.join(source_path, file_name + '.png')
            if os.path.exists(jpg_file):
                shutil.copy(jpg_file, os.path.join(target_path, 'images', purpose, file_name + '.jpg'))
            if os.path.exists(png_file):
                shutil.copy(png_file, os.path.join(target_path, 'images', purpose, file_name + '.png'))
        file_idx += 1

    # 设置文件的处理
    if not os.path.exists(yaml_file):  # 如果不存在yaml文件,则创建一个空文件
        with open(yaml_file, 'w', encoding='utf-8') as f:
            f.write('')
    with open(yaml_file, 'r+', encoding='utf-8') as f:
        data = yaml.safe_load(f) or {}
        data['path'] = target_path
        data['train'] = os.path.join('..', 'images', 'train')
        data['val'] = os.path.join('..','images', 'val')
        data['test'] = os.path.join('..','images', 'test')
        data['nc'] = len(classes)
        data['names'] = classes
        yaml.dump(data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)

    print(f"{source_path} 文件夹中的json文件转换完成")

if __name__ == '__main__':
    convert_AnyLabeling_to_YOLO(r'E:\images', r'datasets', copy_image=True)

执行完成后,会在AnyLabeling图片文件夹内生成一个classes.txt文件:

classes.txt文件内容是该目录下所有的图片标注时用过的标签:

并且会在项目中生成图片文件夹和标注文件夹:

以及复制训练图片和生成标注文件

和生成配置文件dataset.yaml。


另外需要知道的是,AnyLabeling导出的YOLO格式数据是用于目标检测和分类检测的,不能用于实例分割。需要用AnyLabeling导出coco格式后再用ultralytics自带的工具来转换成YOLO数据集(ultralytics.data.converter.convert_coco),才能用于实例分割。由于AnyLabeling对中文的支持不好,导出coco格式时生成的json文件中文是字节形式,需要再编写代码转换成汉字。

如图,AnyLabeling生成的json文件中,中文是以字节方式保存的:

AnyLabeling导出coco格式还有个问题,就是图片比较多,分多次标注和导出时,它的分类标签的顺序可能是不固定的,某一个分类,比如"长颈鹿",第一次导出时在annotations.json中的序号可能是2,下一次可能就是3,这就给后面的训练带来了隐患,有可能使模型认错种类。所以,我才编写了上面的脚本,在图片目录中创建classes.txt文件,把标签顺序固定下来,即使多次标注和导出,和新增了种类标签,原先的标签种类和顺序也不会改变。


2、训练

windows下需要打开anaconda prompt命令行窗口,Ubuntu直接打开命令行窗口即可,激活安装ultralytics时创建的虚拟环境,然后用cd指令切换到本地ultralytics下的datasets\目录下,输入命令:

python 复制代码
yolo task=segment mode=train model=yolo11s-seg.yaml data=dataset.yaml epochs=10 imgsz=640 batch=4 device=0 workers=1

命令解释:

  • task=segment:任务类型为实例分割
  • mode=train:模式为训练
  • model=yolo11s-seg.yaml:使用的基础模型是 YOLOv11 的 small 版本(yolo11s),但是不用它的预训练权重(从头训练),如果使用预训练权重,将该段指令改为:model=yolo11s-seg.pt
  • model=yolo11s-seg.pt:使用的基础模型是 YOLOv11 的 small 版本(yolo11s)的预训练分割模型
  • data=dataset.yaml:指定数据集配置文件,包含训练集 / 验证集路径、类别信息等
  • epochs=10:训练总轮次为 10 轮
  • imgsz=640:输入图像的尺寸统一调整为 640x640 像素
  • batch=4:每批处理 4 张图像
  • device=0:指定使用第 0 号 GPU 进行训练(若为 cpu 则使用 CPU)
  • workers=1:使用1个并行进程训练,提高效率

开始训练:

由于使用了中文标签,需要进行设置,使matplot支持中文字体输出,方法见:

https://blog.csdn.net/xulibo5828/article/details/154879460

训练完成后,会得到模型以及各种过程曲线:

复制代码

本例只是演示了训练模型的方法,使用了较少数量的图片(35类,约300张图片)和10个epochs。如果想得到更好的训练效果,就需要更多的训练图片和更多轮的训练。

相关推荐
YJlio16 分钟前
Contig 学习笔记(13.4):单文件碎片整理工具的原理与基本用法
笔记·学习·stable diffusion
FL16238631295 小时前
[C#][winform]基于yolov8的水表读数检测与识别系统C#源码+onnx模型+评估指标曲线+精美GUI界面
开发语言·yolo·c#
石像鬼₧魂石8 小时前
HexStrike-AI人工智能 渗透测试学习(Metasploitable2 192.168.1.4)完整流程总结
学习·ubuntu
非凡ghost8 小时前
MusicPlayer2(本地音乐播放器)
前端·windows·学习·软件需求
蓝桉~MLGT9 小时前
Ai-Agent学习历程—— 阶段1——环境的选择、Pydantic基座、Jupyter Notebook的使用
人工智能·学习·jupyter
杰米不放弃10 小时前
AI大模型应用开发学习-26【20251227】
人工智能·学习
执笔论英雄11 小时前
【RL】Megatron使学习forward_backward_func返回值
学习
幺零九零零11 小时前
压测学习-JMeter
学习·jmeter
AI街潜水的八角12 小时前
基于YOLOv12番茄农作物病害检测系统1:番茄农作物病害检测数据集说明(含下载链接)
yolo
懷淰メ12 小时前
python3GUI--基于YOLOv8深度学习的车牌识别系统(详细图文介绍)
深度学习·opencv·yolo·pyqt·图像识别·车牌识别·pyqt5