ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLO民族服饰数据集训练全流程:格式转换、划分与避坑指南

YOLO民族服饰数据集训练全流程:格式转换、划分与避坑指南 简介面向需要训练YOLO民族服饰识别模型的开发者这份数据集包含真实场景下5000张高质量标注图片标注框质量高可直接用于YOLO系列目标检测。标签同时提供voc、coco、yolo三种格式分别存放于不同文件夹便于按训练框架切换使用。压缩包共2000个文件以xml标签为主另有少量txt标签、html图文教程和py划分脚本整体253.12MB。除数据外还附赠YOLO环境搭建Windows/Linux、训练案例教程以及训练集、验证集、测试集划分脚本可按需求自行拆分数据。目前已有923人学习下载适合目标检测初学者、课程设计与少数民族服饰数字化项目参考省去标注和格式转换的额外工作。1. 拿到 YOLO民族服饰识别数据集的第一晚别急着训练先做三件事YOLO 民族服饰识别数据集这类资源包5000 张图片加上 VOC、COCO、YOLO 三种格式标签看起来是解压就能训练但实际动手时几乎所有人都会在第一晚踩坑标签和图片对不上、类别顺序错位、划分脚本一把梭把同组图片切进训练和验证。这个包真正值钱的地方不是图片本身而是把标注格式统一和数据划分这两道最耗时的前置工序打包好了。它适合做民族文化数字化、服装电商分类、博物馆藏品检索的开发者也适合刚接触 YOLO、想用现成数据把训练流程跑通的新手。不过资源包到手后先别急着解压就训练花半小时做一次完整自检能免掉后面两三天排错的时间。2. 三种标签格式并存VOC、COCO、YOLO 各自解决什么问题2.1 为什么一个数据集要同时给三份标签先看这份资源包的文件结构图片是一份标签却给了三份。这不是冗余而是不同工具链对标签格式有硬性要求。labelImg 这类标注工具导出的是 Pascal VOC 的 XMLYOLO 系列训练脚本读的是每张图一个同名 txt而 mmdetection、detectron2 这类通用检测框架默认吃 COCO 的 JSON。你准备用哪条技术栈就直接拿对应格式开工省掉「转换-校验-再转换」的循环。三份标签的存在还有一个隐藏价值可以做交叉校验。比如你把 VOC 的 XML 和 YOLO 的 txt 都解析一遍统计每个类别的目标数两边对不上就说明某个转换环节出了问题。这是单份标签的数据集给不了的安全感。实际做项目时我一般会以 YOLO 格式为主进行训练把 VOC 当作可视化校验用COCO 格式留着以后跑对比实验这样切换框架时不用重新标注。2.2 VOC 与 YOLO 的转换逻辑一个最小脚本搞定VOC 的 XML 描述目标用的是绝对坐标核心字段是 bndbox 下的 xmin、ymin、xmax、ymax。YOLO 的 txt 每行是一个目标五列分别是 class_id、归一化后的中心点 cx、cy 和宽高 w、h。转换公式就是简单的除法中心点取 bndbox 两端的平均值宽高取两端差值再分别除以图片宽高。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, classes_file, img_w, img_h): # classes_file 是类别清单每行一个类名顺序就是 YOLO 的 class_id with open(classes_file, r, encodingutf-8-sig) as f: classes [line.strip() for line in f.readlines()] tree ET.parse(xml_path) root tree.getroot() yolo_lines [] for obj in root.iter(object): name obj.findtext(name).strip() if name not in classes: continue # XML 里出现 classes.txt 没有的类直接跳过 class_id classes.index(name) bndbox obj.find(bndbox) xmin float(bndbox.findtext(xmin)) ymin float(bndbox.findtext(ymin)) xmax float(bndbox.findtext(xmax)) ymax float(bndbox.findtext(ymax)) cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 越界保护某些标注框会超出图片边界不处理会导致后续训练 loss 异常 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(w, 1.0) h min(h, 1.0) yolo_lines.append(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return yolo_lines这里有两个细节值得说明。第一读 classes_file 用了 utf-8-sig因为 Windows 记事本保存 UTF-8 文件时会在开头加 BOM用普通 utf-8 读取会让第一个类别名变成乱码类别顺序直接错位。第二img_w 和 img_h 必须用 cv2 或 PIL 读图后动态获取不能假设数据集里所有图片尺寸一致。民族服饰数据集里经常混着手机拍摄的竖图和扫描仪的横图尺寸不统一是常态。2.3 COCO 里最容易搞错的三个字段COCO 的标注是单个 JSON 文件核心是 images、annotations、categories 三段。images 里记录图片的 id 和尺寸annotations 里每条是一个目标通过 image_id 指向图片bbox 格式是 [x, y, w, h]categories 就是类别表。新手最容易在三个地方翻车。第一个是 bbox 坐标系的差别。VOC 是 xmin/ymin/xmax/ymaxCOCO 是 x/y/w/h而且 x、y 是左上角坐标不是中心点。从 VOC 转 COCO 时x 直接取 xminy 取 yminw、h 是差值这里没有除法。第二个是 area 字段COCO 定义里它应当等于分割区域的面积没有分割标注的检测数据集通常直接用 bbox 的宽高乘积且要用浮点数不要写成整数。第三个是 iscrowd普通检测标注写 0写 1 表示密集人群场景YOLO 训练时遇到 iscrowd1 的标注会直接跳过。COCO 格式的错误往往是静默的训练框架不报错但评估时 mAP 算出来莫名其妙。建议训练前先跑一段检查逻辑import json with open(annotations/train.json, r, encodingutf-8) as f: coco json.load(f) img_ids {img[id] for img in coco[images]} cat_ids {cat[id] for cat in coco[categories]} for ann in coco[annotations]: if ann[image_id] not in img_ids: print(标注指向不存在的图片, ann[id]) if ann[category_id] not in cat_ids: print(标注指向不存在的类别, ann[id]) if len(ann[bbox]) ! 4: print(bbox 维度异常, ann[id])image_id 对不上和 category_id 对不上是最高频的两个问题前者发生在手工拼 JSON 时编号写错后者发生在类别表被二次编辑后。这段脚本几秒钟就能跑完能拦住后面几小时的无效训练。3. 拿到数据集先做自检处理数据集用于 YOLOv8 训练前的四道检查3.1 图片与标签能不能一一对上资源包解压后先别管训练第一件事是数文件。常见做法是写个脚本遍历图片目录和标签目录统计同名文件的差集。比如你有 5000 张 jpgYOLO 标签目录里就应该有 5000 个同名 txtVOC 目录同理。差集不为零说明有图片没标注或者有标注没图片。import os img_dir images yolo_label_dir labels_yolo img_names {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .jpeg, .png))} label_names {os.path.splitext(f)[0] for f in os.listdir(yolo_label_dir) if f.endswith(.txt)} missing_labels img_names - label_names missing_images label_names - img_names if missing_labels: print(有图片但没有标签数量:, len(missing_labels)) if missing_images: print(有标签但没有图片数量:, len(missing_images))文件层级有时会被打包工具改变这种情况下差集往往特别大一眼就能发现。另外注意扩展名大小写问题有些手机导出的照片是 JPG 大写代码里要一并处理否则凭空多出一堆「缺失」。3.2 用解码器读一遍0 字节文件和异常通道的差别数量对得上只是第一步还要确认图片真能读出来。下载过程可能损坏文件也可能混入 0 字节的空文件。这类坏图用 cv2.imread 不会报错只是返回 None如果训练脚本没做判空处理会在某个 epoch 随机触发奇怪的错误。import os import cv2 bad_images [] for f in os.listdir(images): path os.path.join(images, f) img cv2.imread(path) if img is None: bad_images.append(f) continue if img.ndim ! 3 or img.shape[2] ! 3: bad_images.append(f) print(无法解码或非 RGB 的图片:, len(bad_images))这个检查同时过滤了两类问题一类是文件损坏导致解码失败另一类是 RGBA 四通道图片。YOLO 训练管线一般会自动转 RGB但通道数异常偶尔会在图像增强环节翻车尤其是用 Albumentations 的时候。发现问题图片直接删除或重新导出不要指望训练时自动跳过。3.3 类别分布统计哪些类注定训不好文件检查做完接下来统计类别分布。这一步决定训练策略而不是可有可无的参考。假设 5000 张图里有 8 个民族服饰类别其中「苗族盛装」有 2500 个目标「鄂伦春狍皮衣」只有 60 个后者就算训练 300 轮也难有好的 mAP。from collections import Counter label_dir labels_yolo counter Counter() for txt in os.listdir(label_dir): if not txt.endswith(.txt): continue with open(os.path.join(label_dir, txt), r, encodingutf-8) as f: for line in f: if line.strip(): cls line.split()[0] counter[cls] 1 for cls, cnt in counter.most_common(): print(f类别 {cls}: {cnt} 个目标)注意这里统计的是目标数量而不是图片数量。一张图上可能同时出现多个民族服饰目标如果按图片数统计会把多目标图的信息稀释掉。拿到分布后目标数低于 100 的类别就要考虑两种处理要么做数据增强把样本量补上来要么干脆降级为「其他服饰」类避免它拖累整体收敛。3.4 路径和编码问题提前排掉资源包在 Windows 上解压后路径里可能带着中文目录名或空格。Ultralytics 新版对中文路径支持已经好了一些但旧版 YOLOv5 和不少第三方库仍然会炸。我的习惯是解压后立刻把数据集目录改名为纯英文比如 minzu_fashion并放到没有空格的磁盘根目录下。这一步不花时间但能避免很多奇怪的报错。4. 划分脚本的工程细节按组切分、固定随机种子与三类标签同步4.1 为什么随机划分在服饰数据集上不靠谱数据划分看起来简单训练集 70%、验证集 20%、测试集 10%用 random.shuffle 打乱一分成三份就结束。但服饰识别数据集有一个特殊的泄漏风险同一套服饰通常有多张照片拍摄时一个模特换一套衣服连拍十几张这些照片在特征层面高度相似。如果随机打乱同一套衣服的照片会同时出现在训练集和验证集模型直接记住衣服纹理就能在验证集拿高分真实泛化能力完全没测出来。这种泄漏在民族服饰场景里尤其严重因为整套服饰本身就是识别目标纹理就是决定性特征。正确做法是划分前先定义组的概念组 ID 可以是文件名里的前缀、拍摄批次编号、或者子目录名。同一个组的所有图片必须被一起划走要么全进训练集要么全进验证集绝不允许拆开。4.2 按 group_id 划分的核心脚本下面这段脚本以组为单位做整体切分先按组 ID 聚合再对组进行洗牌最后把组展开成图片列表。import random from collections import defaultdict def group_split(image_paths, group_key_fn, train_ratio0.7, val_ratio0.15, seed42): groups defaultdict(list) for img in image_paths: gid group_key_fn(img) # 同一组拍摄/同一套服饰的照片归入同一个 gid groups[gid].append(img) group_ids list(groups.keys()) rng random.Random(seed) rng.shuffle(group_ids) n_train_groups max(1, int(len(group_ids) * train_ratio)) n_val_groups max(1, int(len(group_ids) * val_ratio)) train_groups group_ids[:n_train_groups] val_groups group_ids[n_train_groups:n_train_groups n_val_groups] test_groups group_ids[n_train_groups n_val_groups:] train [img for gid in train_groups for img in groups[gid]] val [img for gid in val_groups for img in groups[gid]] test [img for gid in test_groups for img in groups[gid]] return train, val, testgroup_key_fn 的写法取决于资源包的目录结构。如果文件名是 miao_001_01.jpg、miao_001_02.jpg 这种取的逻辑可以是从文件名里截取前缀也可以直接用文件所在目录名。参数方面train_ratio 和 val_ratio 分别控制训练和验证比例5000 张图片的量级下0.7/0.15 的默认值合理剩余 15% 留给测试集。seed 必须固定这个参数直接决定实验可复现性换一个 seed 整个数据集分布就变了对比实验时很容易得出错误结论。按组划分的代价是样本比例会受组大小影响同一组图片数量相差悬殊时训练集和验证集的最终图片数比例会偏离 0.7/0.15。只要组内照片数在 5 到 20 张之间误差完全可以接受。如果数据集里某个类别的组数量特别少可以先按主类别把组分层再在每一层里调用 group_split。4.3 划分完成后三类标签怎么跟着图片走图片划分完成标签要同步移动。YOLO 的 txt、VOC 的 XML、COCO 的 JSON 都要按相同的划分边界重建。COCO 格式的处理方式是读入原始 JSON把 train 集合的 image_id 筛出来只保留这些图片对应的 annotations 写入新文件YOLO 和 VOC 则简单很多按图片文件名同步移动同名文件即可。for img in $(cat train.txt); do base${img%.*} mv $base.txt labels_yolo_train/ 2/dev/null mv $base.xml labels_voc_train/ 2/dev/null done这里的 2/dev/null 是有意为之一个图片不一定同时存在 txt 和 xml 版本找不到文件时让 mv 静默失败避免脚本中断。实际操作中要注意train.txt 里如果写的是相对路径mv 的目标目录也要对应调整否则会移错地方。把三种格式同步划分到位之后才轮到写 data.yaml 进训练。5. YOLO 训练必踩的 5 个坑从类别错乱到 bn 崩溃的排查顺序5.1 训练不报错但预测全错类别顺序错位现象训练过程一切正常loss 正常下降验证 mAP 也不难看但推理时明明输入的是苗族服饰输出标签却是藏族服饰。原因VOC 的 XML 里类别名是字符串YOLO 的 class_id 是数字二者靠 classes.txt 的顺序建立映射。如果你中途改过 classes.txt或者从别人手里继承项目时 classes.txt 和标签文件的生成脚本不是同一个版本class_id 就会全部错位。训练集里 0 号类本来是苗族新 classes.txt 里 0 号类却变成了藏族模型学的特征和标签语义对不上。解决训练前打印一遍 data.yaml 里的 names 顺序再解析 3 张验证集图片的 YOLO 标签把 class_id 转成名字人工确认。务必要以一份权威类别清单为基准重新生成所有标签而不是在旧的标签文件上手工改数字。5.2 loss 突然变 NaN一张坏图毁掉整个训练现象训练到第 20 轮时 loss 正常第 21 轮开始所有损失变 NaN重跑一次又在不同的轮次崩溃。原因这个现象是典型的黑匣子问题表象在 loss病灶在数据。最常遇到的是三类数据问题图片文件损坏导致解码返回空数组、标注框坐标越界产生 Inf、标注框宽度或高度为 0 导致除法出问题。解决回到第 3 章的自检脚本把所有图片用 cv2 读一遍确认没有解码失败的文件对 YOLO 的 txt 做数值范围检查cx、cy、w、h 必须在 0 到 1 之间越界的先 clamp 再存w 或 h 小于 1 像素的标注框直接丢弃。不要依赖训练时的容错机制它大概率没有。5.3 验证集 mAP 虚高到 0.99同组图片被切开了现象训练集 mAP 只有 0.75验证集却接近 0.99明显不符合常识。原因这就是 4.1 里说的数据泄漏。同一件民族服饰的多张照片被随机划分进了训练集和验证集模型在验证集上看到的衣服纹理和训练集几乎一模一样本质是在考记忆而不是考泛化。解决用 group_split 按组重新划分。组 ID 的选取规则是同一个模特、同一套衣服、同一拍摄批次的照片必须属于同一个组。注意如果资源包自带的划分脚本只是简单 random.shuffle那它带出来的数据不能直接用来评估模型真实水平只能用来调试流程。5.4 训练中 bn 崩溃loss 全部变 NaN 且不恢复现象前几十轮正常某个 epoch 之后所有 loss 变成 NaN降低学习率重跑依然会崩。原因bn 崩溃在 YOLO 训练中是独立于坏图的另一类问题。当 batch size 很小比如 2 或 4时BN 层的统计量波动极大配合稍高的初始学习率数值很容易在几十轮后溢出。VOC 格式的 XML 里如果同时有难例标注也会加剧这个问题。解决优先把 batch 提到 8 以上batch 受显存限制时同步把初始学习率从 0.01 降到 0.001。做了这两步还崩就回去查 5.2 的坏图问题。bn 崩溃和坏图的表现几乎一样排查顺序建议是先查数据再调超参因为数据问题比超参问题更容易定位。5.5 混淆矩阵里的数字怎么都对不上现象训练完成后打开 confusion_matrix.png发现每一行的数字加起来不等于该类在数据集里的真实目标数量。原因混淆矩阵的统计粒度是「预测结果匹配」而不是「真实标签计数」。同一个真实目标附近有多个预测框时评估脚本可能记录多次匹配NMS 没有完全去重的情况下矩阵里的数字自然对不上总数。这不一定是 bug是可视化工具的统计口径和你的预期不同。解决不要用混淆矩阵来数总量它只适合看类别之间的混淆模式和误检分布。想要精确数量去 results.csv 里看 precision(B) 和 recall(B)或者自己写脚本统计 targets。如果矩阵里大量预测集中在相邻类别优先怀疑 5.1 的类别顺序问题。6. 用 YOLOv8 训练自己的民族服饰数据集从 data.yaml 到 best.pt6.1 训练命令与必调参数划分完成之后进入训练环节。环境配置完成后先写 data.yaml这是 YOLOv8 读取数据集的入口path: /data/minzu_fashion train: train/images val: val/images test: test/images nc: 8 names: 0: miao_finery 1: zang_finery 2: dai_finerynames 的顺序必须和之前转换脚本用的 classes.txt 完全一致这是第五章节反复强调的点写错一个后面全错。训练命令用 Ultralytics 标准写法yolo detect train dataminzu_fashion.yaml modelyolov8s.pt \ epochs100 imgsz640 batch16 patience30 \ projectruns/detect nameminzu_v8s这里用 yolov8s.pt 作为预训练权重而不是从头训练。5000 张图的数据规模用 s 级别最合适yolov8n 精度偏低yolov8l 在小数据集上容易过拟合。epochs 设 100 结合 patience30 做早停连续 30 轮验证集 mAP 不涨就自动停一般跑不到 100 轮。imgsz 用 640 起步民族服饰细节较多时可以试 768代价是显存占用和推理速度。batch 的选择看显存8G 显存用 1624G 可以拉到 32。6.2 训练完只看 loss 等于白训验证的先后顺序训练结束后不要只看 loss 曲线loss 下降不代表检测效果好。先运行一次验证yolo detect val modelruns/detect/minzu_v8s/weights/best.pt dataminzu_fashion.yaml重点看 mAP50 和 mAP50-95 两个指标。mAP50 是粗略的框重合度评估mAP50-95 对框位置要求更严格民族服饰这种细粒度识别场景mAP50-95 更能反映框的精细度。第 33 轮早停后如果 mAP50 在 0.85 以上但 mAP50-95 只有 0.5说明框的位置精度不够下一步优先调 imgsz 或 anchor 设置而不是盲目加轮数。我自己每次都习惯在数据集目录里补一个 data_readme.txt记录划分用的 seed、train/val/test 三份的图片数、各类别目标数以及 data.yaml 里 names 清单的来源。这个习惯救过我一次换机器重跑实验时data.yaml 里的 names 顺序和标签重编号脚本不是同一个版本整个实验作废重来花了三天。后来「先写 readme 再开跑」就成了固定动作。希望这个数据集和这套流程帮到你。本文还有配套的精品资源点击获取
返回列表