资讯动态

YOLO实例分割数据集实战:从标注格式到训练避坑指南

发布时间:2026/10/28 1:35:54 来源:尧图企业网站定制
简介通用物体实例分割数据集面向目标检测与实例分割模型开发覆盖人物、汽车、动物等80个常见类别共972张真实场景图片按579/245/148划分训练、验证与测试集并采用YOLO格式多边形标注便于直接用于YOLO、Mask R-CNN等主流框架。压缩包共1946个文件包含972个jpg图像、972个txt标注、1个yaml配置及1个docx说明文档整体约63.44MB目录结构规整适合模型训练、算法评估及学术基准测试。这一数据集同样适用于智能监控中的物体分割与跟踪、零售商品识别等场景类别丰富且标注精细能有效提升模型在复杂环境下的泛化能力。已有75人学习下载对于需要构建自动驾驶视觉、机器人感知或进行计算机视觉研究的人员是一份可直接落地的数据基础。1. 通用物体实例分割数据集一个开箱即用的YOLO-seg标注包通用物体实例分割数据集这是我在工业视觉项目里实际拆过的一份标注资源。做抓取、质检这类多类目标检测场景的人最头疼的不是YOLO学不会而是标注跟不上——框住一个物体容易把每个物体边界抠到像素级一帧图就得花十几分钟。这份zip里装的是已经按YOLO实例分割格式排好的图片和txt标注训练前不用再做坐标换算解压就能喂给yolov8-seg或yolov11-seg。适合谁手里有工业相机、想做多类目标检测但缺干净标注数据的工程师以及刚接触实例分割、想跳过造轮子直接看训练全流程的新手。它能帮你省下最费时间的那一步标注格式整理。以下内容是我拆包、清洗、训练这一路踩过的真实记录参数和坑都写具体。2. 拆包与格式摸底先看清目录结构再谈怎么train2.1 实例分割格式的目录约定images与labels怎么对齐拿到通用物体实例分割数据集_20251117_152913.zip先别急着解压后双击看图片。我一般会先看一眼压缩包内的顶层目录判断它是裸文件还是标准结构。常见的YOLO-seg数据集结构是这样排的dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml这个结构几乎是Ultralytics生态的默认约定images/train里的每张图在labels/train里有一个同名txt文件二者一一对应。txt里每一行代表一个实例格式是class_id x1 y1 x2 y2 ... xn yn。注意这里没有框坐标——实例分割的标签只有多边形顶点而且全部是归一化坐标即把像素坐标除以图像宽高值域在0到1之间。训练时模型会根据多边形的外接矩形自动算box你不需要额外提供bbox。判断一个数据集是不是真的YOLO-seg格式比想象中容易踩坑。很多网上下载的资源打着实例分割的名义实际给的是COCO JSON或者一个mask PNG目录你要自己转成txt。而这个zip包如果打开后直接就是labels目录加txt文件说明已经做完了格式转换是我这种怕麻烦的人最喜欢的形态。解压后建议先把data.yaml打开看一遍里面至少要有path、train、val、names四个字段names是一个字典键从0开始递增。为什么从0开始这一点很关键因为txt文件里的class_id是数字训练时YOLO读的就是数字names字典的下标决定了这个数字最终显示成什么类别名。如果names里第一个键是1而不是0哪怕txt里第一行类号是0寻址也会出问题轻则类别错乱重则索引越界直接崩。所以拿到数据集的第一件事不是看图片长什么样而是先数一下names的个数和txt里出现的最大类号能不能对上。2.2 标注完整性校验坐标范围、多边形顶点数、空文件解压完成后我习惯写一个短脚本把整个labels目录扫一遍。这一步能提前暴露大部分训练时会炸的雷省得训练到一半报错再回头查。脚本逻辑很简单遍历所有txt逐行解析检查三类问题——坐标是否越界、多边形顶点是否足够、文件是否为空。import os from pathlib import Path label_root Path(labels/train) errors [] file_count 0 for txt in label_root.rglob(*.txt): file_count 1 with open(txt, r, encodingutf-8) as f: lines f.readlines() if len(lines) 0: errors.append(f{txt}: 空文件该图无目标) continue for line_no, line in enumerate(lines, 1): parts line.strip().split() # 最小合法行 class_id 3个点(6个坐标值) 7个元素 if len(parts) 7: errors.append(f{txt}: 第{line_no}行元素只有{len(parts)}个点数不足) continue cls_id int(parts[0]) coords list(map(float, parts[1:])) if any(c -0.01 or c 1.01 for c in coords): errors.append(f{txt}: 第{line_no}行坐标越界出现值 {coords}) if cls_id 0: errors.append(f{txt}: 第{line_no}行类别号为负数) print(f共扫描 {file_count} 个标签文件) print(f异常 {len(errors)} 条) for e in errors[:30]: print(e)这段代码的逻辑分三层。第一层处理空文件如果某张图没有任何目标对应的txt文件是零字节空文件这在YOLO格式里是合法的表示背景图但如果整个目录全是空文件那说明标注根本没生成成功得回去重新导出。第二层检查len(parts) 7一个多边形最少需要三个顶点才能构成面每行元素个数就是1 顶点数 * 2少于7个就不是个闭合区域训练时会被跳过但会拖慢数据加载。第三层检查坐标范围因为使用了浮点归一化正常值必然在0到1之间出现负数或大于1的值多半是转换脚本里忘记除宽高或者是把COCO的像素坐标直接塞了进来。边界放了一点余量-0.01到1.01是因为有些标注工具会为了贴合物体边缘产生极轻微的越界例如0.99998这样的值。完全等于1也没有问题真正需要警惕的是大幅度越界比如坐标跑到5.7这种十有八九是单位没换算。我一般会跑完脚本把报错文件数除以总文件数如果异常率超过1%就不会直接用这份数据训练而是回去做一轮清洗。3. 从通用数据落行业场景类别筛选与数据清洗是训练前的必修课3.1 为什么“通用”不等于“拿来就训”域差异和类别分布“通用物体”这四个字是双刃剑。往好里说类别覆盖面广瓶瓶罐罐、箱子、零件、车辆都有适合做多类目标检测的底座数据往坏里说它默认你什么都要检测而工业场景通常只需要其中的三五个类。我见过有人拿通用数据集直接训一个“检测划痕”的模型结果模型把高光反射当成划痕因为通用数据里根本没教它区分反光和缺陷——这就是域差异。通用数据集的类别分布几乎都是长尾头部类别样本量占了大半尾部类别可能只有几十张图。如果你需要的恰好是尾部类别要么补齐标注要么就得做类别筛选。另外通用数据集的拍摄视角多为平视或俯视工业相机的固定机位、固定光源拍出来的图像和通用数据的背景纹理差距很大。一份通用实例分割数据集直接训练出来的模型可能在测试集上mAP不错一到现场就翻车原因不是模型笨而是现场背景在训练时压根没见过。所以我的习惯是先用脚本扫出这份数据集的实际类别列表和每个类别的实例数量再根据自己要检测的目标决定保留哪些类、丢弃哪些类。这一步不是可选项是必修项。把几十个类砍到需要的三五个类模型的学习负担小很多收敛速度也快得多。尤其是实例分割每个类别都要学习多边形轮廓类别越多模型参数被平均稀释得越严重精度反而下降。3.2 类别重映射与清洗脚本改txt、删冗余、对齐yaml类别筛选不能只在data.yaml里改names因为txt里的class_id是原始编号。比如原始数据集里杯子是0、瓶子是1、背景杂物是4你只想保留杯子和瓶子就得把0、1重映射成新模型的0、1把4整行删掉。写一个重映射脚本一劳永逸。from pathlib import Path # 原类号: 新类号, -1 表示丢弃 CLASS_MAP { 0: 0, # 杯子, 保留并重映射为0 1: 1, # 瓶子, 保留并重映射为1 2: -1, # 背景杂物, 丢弃 3: 2, # 纸箱, 保留并重映射为2 4: -1, # 其他物体, 丢弃 } KEEP {k for k, v in CLASS_MAP.items() if v 0} label_src Path(labels/train) label_dst Path(labels_filtered/train) image_src Path(images/train) image_dst Path(images_filtered/train) for txt in label_src.rglob(*.txt): rel txt.relative_to(label_src) out label_dst / rel out.parent.mkdir(parentsTrue, exist_okTrue) valid_lines [] with open(txt, r, encodingutf-8) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) 7: continue cls_id int(parts[0]) if cls_id in KEEP: parts[0] str(CLASS_MAP[cls_id]) valid_lines.append( .join(parts)) # 如果过滤后没有目标连图片也一并删掉免得白占加载时间 if len(valid_lines) 0: out.write_text(\n.join(valid_lines), encodingutf-8) img image_src / (txt.stem .jpg) if not img.exists(): img image_src / (txt.stem .png) if img.exists(): (image_dst / img.name).write_bytes(img.read_bytes())这段代码的重点在CLASS_MAP。它是一个字典键是原始类别号值是新类别号-1代表丢弃。遍历每一行txt时先判断原始类号是否需要保留需要保留的就把第一列的类别号替换成新号然后写入新的labels目录。同时如果一张图的所有目标都被过滤光了那这张图就属于无目标背景图对训练没有正向帮助我一般会直接连图片一起删掉减少数据加载时的搜索开销。参数调整上有一个细节txt.stem .jpg这里假设图片是jpg后缀实际包里有可能是png或bmp。所以代码里做了两段判断先找jpg找不到再找png。如果你们拿到的数据集图片后缀五花八门可以把判断改成image_src.glob(txt.stem .*)取第一个存在的。跑完脚本后labels_filtered里出现的类别号应该是一个从0开始、无断档的连续序列——如果不连续训练时data.yaml的names会很难写有空档还会导致类别数统计错。4. 跑通YOLO实例分割训练配置yaml与三个关键超参4.1 dataset.yaml怎么配路径、类别名、标签对齐清洗完数据接下来就是配置data.yaml。这一步看着简单翻车率反而最高因为很多人会把path写成相对路径结果换了目录就找不到数据。我习惯把所有数据集路径都写成绝对的省得在不同机器上复现时反复改。下面是一个我实际用过的配置三类目标对应上面重映射后的结果。# 注意path 建议写绝对路径避免在不同机器上启动时报路径不对 path: /home/user/datasets/custom_seg_dataset train: images/train val: images/val names: 0: cup 1: bottle 2: cartonpath是数据集根目录train和val填写相对于path的子目录。这里有一个容易忽略的点YOLO读标签时是根据图片路径自动推导的它会把images/train替换成labels/train。所以图片目录名必须叫images标签目录名必须叫labels不能改成imgs或annos否则训练时全部提示标签找不到。如果你想自定义目录名需要显式字段train_labels但那是非标准写法不推荐。names的键和值必须与txt里的类别号逐一对应。键0对应txt里第一列的0键1对应1以此类推。类别名用什么字符串都行不影响训练只影响可视化时显示的文字。但如果txt里出现了names里没定义的类别号训练过程会直接抛异常日志里会提示“class id out of range”。我在2.2节的校验脚本里已经扫过这个可以把那一行的检查结果再对一遍。4.2 训练命令与指标解读mask mAP和box mAP分开盯配置完成后训练命令本身不复杂复杂的是怎么选模型和超参。直接贴我常用的训练命令yolo segment train \ datadataset.yaml \ modelyolov8m-seg.pt \ epochs120 \ imgsz640 \ batch16 \ device0 \ patience30 \ lr00.01modelyolov8m-seg.pt是官方预训练权重下载后放在当前目录。选m而不是n是因为实例分割任务对特征的精细度要求比目标检测高n模型太轻量mask边缘容易糊。如果显存不够可以退回yolov8s-seg但代价是mask的边界细节会差一些。imgsz640是训练分辨率如果数据集里物体普遍偏小可以提到800或960显存占用会增大推理时也建议用同一分辨率否则精度会掉落。batch16取决于GPU显存。我实测过一块24G显存的卡跑yolov8m-segimgsz640时batch最多开到16再大就OOM。8G显存的卡建议batch8或者把模型换成s。patience30是早停参数连续30个epoch验证集指标不提升就停这个值不要设太小实例分割的mAP提升是波浪形的有时第25个epoch才突然跳一个点。跑完训练日志里会输出两个关键指标box_mAP50和mask_mAP50。这两个要分开盯。box指标反映的是模型找到目标位置的能力mask指标反映的是轮廓贴合精度。场景是抓取或者缺陷检测mask指标更重要场景是目标计数或定位box指标更重要。别在报告里只写一个mAP那是自欺欺人。另外验证指标里有mask_mAP50-95这是多IoU阈值的平均数值会比mAP50低不少属正常现象别被吓到。这里有一张我整理的超参参考表针对通用物体实例分割数据集的常用取值区间参数常用值说明epochs100~150实例分割收敛慢太少了mask学不好imgsz640~960物体小就调大注意显存batch8~16按显存调OOM就减半lr00.005~0.01预训练权重继续训太大易震荡patience20~30早停阈值别设太小optimizerAdamWSGD也可以AdamW收敛更稳训练到后半程我一般会盯val_mask_mAP50(B)这个字段括号里的B表示best。如果这个值在最后几十个epoch里不再上涨而且patience触发了早停就说明模型容量已经吃到瓶颈。这时候不要盲目加epoch而是回看数据集——某个类别的实例有没有覆盖到不同角度、不同光照如果没有补数据比调参有用得多。5. 避坑指南标注格式、类别丢失和解压乱码的四个典型翻车现场5.1 训练启动时报“验证集为空”训练循环直接卡死现象yolo segment train启动后日志显示train正常加载但val阶段提示找不到任何图片或者数据集为空程序卡在第一个epoch不前进。原因我在2.1节说过YOLO通过images目录推断labels目录。如果清洗数据时把labels/val里的空txt全部删了但images/val里的对应图片没有同步删除训练框架在构建验证集时会去遍历images/val里的图片发现每张图都没有对应标签直接判定数据集无效。解决把标签过滤脚本和图片过滤脚本合成一个凡是过滤后没有标签的图片同时从images和labels目录删除。更保险的做法是训练前跑一遍目录对齐检查遍历images/val里的每一个文件确认labels/val里有同名txt计数不一致就用2.2节的校验脚本查出差异文件。从那次之后我每次拆新数据集都强制先跑目录对齐。5.2 mask mAP高但可视化时mask比实际物体小一圈现象训练时指标都正常mask_mAP50到了80以上但用模型跑推理把mask叠加到原图上发现轮廓比真实物体小了一圈就像给物体穿了紧身衣。原因这是标注数据源头的问题。部分数据集的mask多边形点密度不够尤其在大目标上一个多边形只有十几个点拟合圆形或弧形物体时会把边缘拉成直线训练出来的模型自然学不到外扩的弧度。解决检查原始txt里每行的坐标点数分布统计平均顶点数。如果平均顶点数少于12个建议对mask做一次稠密化处理用cv2.approxPolyDP的逆操作插值增加点。常见做法是先把归一化坐标转成像素坐标然后用贝塞尔曲线拟合多边形边缘重新采样到每边8~12个点再写回txt。这能明显改善大目标的mask贴合度。当然还有一个更省事的选择就是换一个标注更精细的同源数据集版本前提是你能找到。5.3 类别id越界训练中动态报错“class index out of range”现象训练跑了一两个epoch后突然中断控制台报某一行标注的class id大于classes数量。原因这是老生常谈的问题——数据集的类别号没有从0开始或者类别号有跳档。比如原始数据集是COCO格式类别号从1开始转换脚本没做-1处理导致txt里的类号最大是79而data.yaml里names只写了80个键索引从0到79还好说就怕names只定义了三个类别txt却出现了原始类号12。解决不要手动改txt用3.2节的重映射脚本把原始类别号统一映射成从0开始的连续序列。如果你确定保留的类别只有三类就映射成0、1、2中间不要留空洞。跑完脚本后再用2.2节的校验脚本检查一遍最大类号确保严格小于names的键数。5.4 zip解压后文件名乱码图片和标签对不上现象在Windows下用右键解压zip图片和标签的文件名变成一堆乱码jpg是乱码同名txt也是乱码但两者偏偏能对上一旦换成Linux解压jpg和txt的乱码不一致直接匹配失败。原因zip文件在打包时使用了特定编码记录文件名常见的是GBK。Windows的默认解压工具会自动处理GBK解码所以显示正常Linux的unzip默认按UTF-8解码GBK的字节序列被解释成乱码。图片和txt文件名乱码不一致是因为某些字符在转换过程中被替换成了不可见字符。解决在Linux下用unzip -O gbk指定编码解压或者用Python的zipfile模块手动解压并重命名。我写过一次血的教训当时用unzip直接解压了三千多张工业图文件名全乱花了两个小时才写脚本批量恢复。从那以后我只要看到zip是中文文件名一律先执行unzip -l看一下编码再决定怎么解压。5.5 验证集mAP不错一到现场就疯狂误检现象在通用数据集上训练出来的模型验证集mAP挺好看但是拿到工业相机现场一跑把传送带缝隙、螺栓阴影、油渍都识别成了目标物体。原因这就是3.1节说的域差异。验证集来自通用数据集的分布现场的背景纹理、光源角度、镜头畸变在训练数据里完全没有体现。模型学到了通用场景下的“目标特征”但没学到“背景负样本长什么样”。解决两个方向。第一采集现场背景图加入训练集作为不带标注的背景类样本让模型学会区分。第二把模型输出的原始mask做一次面积过滤和置信度过滤比如设定面积阈值和conf0.45以上才输出误检能压下去不少。别指望只调conf就行conf调太高会把真目标也过滤掉还是要从数据侧补。6. 进阶把实例分割结果导出为自定义JSON——一个通用导出脚本训练完模型最后一个环节是把模型输出转成业务系统能用的格式。很多现场的后端不认YOLO的txt更常见的是要求一个JSON包含图像尺寸和每个目标的类别id、多边形坐标。写一个脚本把YOLO-seg的txt转成这种通用JSON之后不管对方要的是那份上报格式还是做统计分析都能一份搞定。import json import cv2 from pathlib import Path label_dir Path(labels/val) image_dir Path(images/val) out_data [] for txt in sorted(label_dir.glob(*.txt)): # 根据txt文件名找对应图片, 读宽高用于坐标反归一化 img_path image_dir / (txt.stem .jpg) if not img_path.exists(): img_path image_dir / (txt.stem .png) img cv2.imread(str(img_path)) if img is None: continue h, w img.shape[:2] items [] with open(txt, r, encodingutf-8) as f: lines f.readlines() for line in lines: line line.strip() if len(line) 7: continue seg line.split() cls_id int(seg[0]) pts list(map(float, seg[1:])) # 归一化坐标 - 像素坐标 pixels [] for i in range(0, len(pts), 2): px round(pts[i] * w, 2) py round(pts[i 1] * h, 2) pixels.append([px, py]) items.append({ category_id: cls_id, polygon: pixels }) out_data.append({ image_name: txt.stem, width: w, height: h, annotations: items }) with open(results.json, w, encodingutf-8) as f: json.dump(out_data, f, indent2, ensure_asciiFalse) print(f已导出 {len(out_data)} 张图的标注, 共 {sum(len(d[annotations]) for d in out_data)} 个实例)脚本的核心是坐标反归一化txt里存的是0到1的相对值要把它们还原成像素坐标必须知道图片的真实宽高所以用cv2.imread读一遍图片。宽高获取之后每个点的x乘以宽度y乘以高度保留两位小数输出成[x, y]结构。这样后端拿到JSON不需要再依赖YOLO环境直接画轮廓、算面积、统计数量都行。这个脚本还有一个用途就是质量核验。我会把导出的JSON再画回图片上和目标检测结果做重叠对比确认没有错位。画的过程中如果发现某个类别的多边形面积普遍小了一半那就是训练时mask的缩放出了问题回查预处理参数。拆了这么多数据集我养成了一个习惯任何zip包到手先花十分钟跑校验脚本和解压编码检查再谈训练。这十分钟省下来的时间远比后面翻车再去排查多得多。通用物体实例分割数据集本身是个好起点但真正让模型在现场可靠的是你对数据的每一次核对。希望这份拆包记录帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑