首页 / 资讯中心 / 文章详情

YOLO猫狗检测数据集实战:VOC/COCO/YOLO标签转换与分层划分指南

YOLO猫狗检测数据集实战:VOC/COCO/YOLO标签转换与分层划分指南 ★ FEATURED ARTICLE
简介这份资源面向计算机视觉入门者、目标检测课程学习者以及需要快速搭建猫狗识别实验的开发者提供了一套真实场景下的YOLO猫狗目标检测数据集。数据经labelimg精细标注标注框质量较高并同步提供voc(xml)、coco(json)与yolo(txt)三种格式标签分别存放于独立文件夹可直接接入YOLO系列模型训练省去格式转换的繁琐步骤。压缩包共2000个文件以1000个xml标注文件和990个txt标签文件为主另含少量html教程、py划分脚本与yaml配置文件整体约23.05MB体积轻便易于传输。资源还附赠环境搭建、训练案例教程及数据集划分脚本可按需自行切分训练集、验证集与测试集帮助读者快速跑通从数据准备到模型训练的全流程。目前已有1357人学习下载适合希望低成本上手目标检测实战的读者参考使用。1. 猫狗检测数据集拿到手之后1000 张图、三套标签、一个划分脚本到底怎么用很多人第一次拿到「YOLO猫狗目标检测数据集(含1000张图片)对应voc、coco和yolo三种格式标签划分脚本训练教程.rar」这类压缩包第一反应是解压、翻目录、找 train.py然后直接开跑。跑完发现 mAP 只有 0.3或者训练到一半报IndexError: index 1 is out of bounds再回头翻文件夹才发现标签格式和代码里写的根本不是一回事。猫狗二分类检测看起来是最简单的入门任务但它恰好把目标检测里最容易翻车的几个环节全暴露了VOC 的 XML 和 YOLO 的 txt 坐标口径不同、COCO 的 json 里 category_id 从 1 开始而 YOLO 从 0 开始、划分脚本如果按图片随机切而不按类别分层验证集里可能一只猫都没有。这个数据集的价值不在于 1000 张图本身而在于它同时给了 VOC、COCO、YOLO 三种标签。这意味着你可以用它验证自己的数据转换链路是否可靠同一张图三种格式解析出来的框应该完全重合。如果你正在入门 YOLO 目标检测或者手头有一个自定义数据集要接入 YOLOv5/v8/v11这套猫狗数据是很好的对照样本。下面按「先搞清楚三种格式的对应关系 → 再跑通转换和划分 → 最后接训练」的顺序讲每一步都给可复现的命令和参数。2. VOC、COCO、YOLO 三种标签的坐标口径与转换验证2.1 三种格式到底差在哪一张猫图拆给你看假设一张 640×480 的图里有一只猫框的左上角在 (100, 80)右下角在 (300, 320)。三种格式记录方式完全不同。VOC 格式是每张图一个 XML框用绝对像素坐标且是 xmin/ymin/xmax/ymaxannotation filenamecat_001.jpg/filename size width640/width height480/height depth3/depth /size object namecat/name bndbox xmin100/xmin ymin80/ymin xmax300/xmax ymax320/ymax /bndbox /object /annotationCOCO 格式是一个大的 json所有图片和标注集中管理框用 [x, y, width, height] 绝对像素category_id 通常从 1 开始{ images: [{id: 1, file_name: cat_001.jpg, width: 640, height: 480}], annotations: [ {id: 1, image_id: 1, category_id: 1, bbox: [100, 80, 200, 240], area: 48000, iscrowd: 0} ], categories: [{id: 1, name: cat}, {id: 2, name: dog}] }YOLO 格式是每张图一个 txt每行一个框全部归一化到 0~1格式为class_id x_center y_center width height0 0.312500 0.416667 0.312500 0.500000计算过程x_center (100300)/2/640 0.3125y_center (80320)/2/480 0.4167width (300-100)/640 0.3125height (320-80)/480 0.5。class_id 从 0 开始猫是 0狗是 1。提示YOLO 的归一化是除以图片的宽和高不是除以 640 这个最大值。如果图片不是正方形x 和 y 的归一化分母不同这是新手最容易搞错的地方。2.2 用脚本验证三套标签是否对齐拿到数据集后不要急着训练先写一个校验脚本随机抽 20 张图把三种格式解析出来的框画到同一张图上看是否重合。这是最省时间的后悔药。import os import json import xml.etree.ElementTree as ET import cv2 import numpy as np def parse_voc(xml_path): tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) boxes [] for obj in root.findall(object): name obj.find(name).text b obj.find(bndbox) xmin int(b.find(xmin).text) ymin int(b.find(ymin).text) xmax int(b.find(xmax).text) ymax int(b.find(ymax).text) boxes.append((name, xmin, ymin, xmax, ymax)) return w, h, boxes def parse_yolo(txt_path, w, h): boxes [] with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cid, xc, yc, bw, bh map(float, parts) xmin int((xc - bw / 2) * w) ymin int((yc - bh / 2) * h) xmax int((xc bw / 2) * w) ymax int((yc bh / 2) * h) boxes.append((int(cid), xmin, ymin, xmax, ymax)) return boxes # 抽一张图做对照 img_path images/cat_001.jpg img cv2.imread(img_path) h, w img.shape[:2] _, _, voc_boxes parse_voc(voc_annotations/cat_001.xml) yolo_boxes parse_yolo(yolo_labels/cat_001.txt, w, h) for name, x1, y1, x2, y2 in voc_boxes: cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 绿色画 VOC for cid, x1, y1, x2, y2 in yolo_boxes: cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 1) # 红色画 YOLO cv2.imwrite(check_overlap.jpg, img)这段代码的逻辑是VOC 解析出绝对坐标YOLO 解析出归一化坐标后反算回绝对坐标两者用不同颜色画在同一张图上。如果绿框和红框完全重合说明转换链路没问题如果红框整体偏移或大小不对大概率是归一化时分母用错了或者 class_id 映射错了。参数上注意parse_yolo里的w, h必须来自实际图片不能硬编码。2.3 COCO 的 category_id 映射坑COCO 格式里 category_id 从 1 开始而 YOLO 从 0 开始。如果你直接把 COCO 的 category_id 写进 YOLO 的 txt猫会变成 1狗变成 2而你的data.yaml里写的是names: [cat, dog]训练时类别索引对不上模型会把猫学成狗。常见做法是维护一个映射表coco_to_yolo {1: 0, 2: 1} # COCO 的 1(cat)-YOLO 的 0, 2(dog)-YOLO 的 1转换时用coco_to_yolo[ann[category_id]]取新 id。如果数据集里还有别的类别映射表要同步扩展并且data.yaml的nc要改成实际类别数。这个坑不报错但会让 mAP 莫名其妙地低属于典型的玄学问题。3. 划分脚本怎么写按类别分层抽样避免验证集里没有猫3.1 随机划分为什么会翻车很多人用random.shuffle把 1000 张图打乱前 800 张做训练后 200 张做验证。如果猫狗比例是 6:4随机切 200 张验证集里面可能只有 70 只猫、130 只狗甚至极端情况下某一边只有个位数。验证集类别不平衡会让 mAP 波动很大今天 0.75明天 0.68你以为是模型问题其实是划分问题。正确做法是按类别分层抽样先统计每张图里出现了哪些类别然后保证训练集和验证集里每个类别的比例接近全局比例。猫狗检测里一张图可能同时有猫和狗所以分层要按「类别组合」来分而不是按单类别。3.2 分层划分脚本与参数说明import os import random import shutil from collections import defaultdict def get_image_classes(label_path): 读取 YOLO 标签返回该图包含的类别集合 classes set() if not os.path.exists(label_path): return classes with open(label_path) as f: for line in f: parts line.strip().split() if parts: classes.add(int(parts[0])) return classes def stratified_split(images_dir, labels_dir, output_dir, val_ratio0.2, seed42): random.seed(seed) # 按类别组合分组 groups defaultdict(list) for fname in os.listdir(images_dir): if not fname.lower().endswith((.jpg, .png, .jpeg)): continue stem os.path.splitext(fname)[0] label_path os.path.join(labels_dir, stem .txt) cls_set frozenset(get_image_classes(label_path)) groups[cls_set].append(fname) train_list, val_list [], [] for cls_set, files in groups.items(): random.shuffle(files) n_val max(1, int(len(files) * val_ratio)) # 每组至少留 1 张验证 val_list.extend(files[:n_val]) train_list.extend(files[n_val:]) # 复制到目标目录 for split, file_list in [(train, train_list), (val, val_list)]: img_out os.path.join(output_dir, images, split) lbl_out os.path.join(output_dir, labels, split) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for fname in file_list: stem os.path.splitext(fname)[0] shutil.copy(os.path.join(images_dir, fname), os.path.join(img_out, fname)) src_lbl os.path.join(labels_dir, stem .txt) if os.path.exists(src_lbl): shutil.copy(src_lbl, os.path.join(lbl_out, stem .txt)) print(ftrain: {len(train_list)}, val: {len(val_list)}) return train_list, val_list stratified_split(images, yolo_labels, dataset, val_ratio0.2, seed42)关键参数val_ratio0.2表示验证集占 20%1000 张图大约 200 张验证seed42固定随机种子保证每次划分结果一致方便复现max(1, ...)保证即使某个类别组合只有 2 张图也至少留 1 张进验证集避免验证集缺失某个类别。运行后目录结构是dataset/images/train、dataset/images/val、dataset/labels/train、dataset/labels/val这是 YOLOv5/v8 默认认的格式。3.3 划分后必须做的两项检查第一项统计训练集和验证集里每个类别的框数量看比例是否接近。如果训练集猫狗比是 6:4验证集也应该是 6:4 左右偏差超过 10% 就调小val_ratio或换seed重跑。第二项检查有没有图片没有对应标签文件。YOLO 训练时如果图片没有标签会被当成负样本但猫狗数据集里每张图都应该有框缺标签说明转换时漏了。用下面这段快速查# 列出没有对应 txt 的图片 for img in dataset/images/train/*.jpg; do stem$(basename $img .jpg) [ -f dataset/labels/train/$stem.txt ] || echo missing label: $img done如果输出为空说明图片和标签一一对应。有输出就回去检查转换脚本通常是文件名里的空格或大小写导致匹配失败。4. 接 YOLOv8 训练data.yaml 配置与三个必调参数4.1 data.yaml 怎么写才不报错YOLOv8 用data.yaml描述数据集路径和类别。猫狗数据集的配置如下path: /home/user/dataset train: images/train val: images/val nc: 2 names: [cat, dog]path是数据集根目录train和val是相对路径。注意names的顺序必须和标签里的 class_id 一致0 对应 cat1 对应 dog。如果转换时把猫狗顺序搞反了训练也能跑但推理时会把猫标成狗。nc必须等于len(names)写错会报AssertionError。4.2 启动训练的命令与参数含义yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/catdog \ nameexp1modelyolov8n.pt用 nano 版本1000 张图足够不需要更大的模型imgsz640是输入分辨率猫狗图如果原图小于 640会被放大如果原图很大会被缩小统一到 640 是常见做法batch16在 8GB 显存上比较稳显存不够就降到 8lr00.01是初始学习率YOLOv8 默认用 SGD 时这个值合适如果用 AdamW 可以降到 0.001patience20表示 20 个 epoch 没有提升就早停避免过拟合。4.3 训练过程中看什么指标训练日志里重点看三个box_loss应该持续下降如果震荡不降检查学习率是否太大mAP50在猫狗这种简单任务上1000 张图训练 100 epoch 通常能到 0.85 以上如果卡在 0.5 左右回去检查标签格式cls_loss如果一直很高说明类别映射有问题猫和狗的 id 可能混了。训练完成后runs/catdog/exp1/weights/best.pt是验证集上最好的权重用yolo detect predict modelbest.pt sourcetest_images/跑几张测试图看效果。注意如果训练时提示No labels found先确认data.yaml里的train路径下有没有labels文件夹YOLOv8 默认从images/train同级找labels/train如果目录结构不对需要显式指定train: images/train和val: images/val并保证labels文件夹和images平级。5. 避坑与排查猫狗数据集训练中最容易翻车的 5 个问题5.1 现象训练 loss 正常下降但 mAP 始终为 0原因标签文件里的 class_id 超出了nc范围。比如data.yaml写nc: 2但某个 txt 里出现了2或更大的数字YOLO 会忽略这些框导致验证时没有有效预测。解决用脚本扫描所有标签文件找出 class_id 最大值awk {print $1} dataset/labels/train/*.txt | sort -n | uniq -c如果输出里有大于 1 的值说明转换时类别映射错了回去改转换脚本。5.2 现象训练报ZeroDivisionError或NaNloss原因标签里有宽或高为 0 的框。VOC 转 YOLO 时如果 xmin 等于 xmax归一化后 width 为 0YOLO 计算 loss 时会除零。解决在转换脚本里加过滤if xmax xmin or ymax ymin: continue把无效框丢掉。同时检查有没有坐标超出图片边界的框超出部分要裁剪到 [0, w] 和 [0, h]。5.3 现象验证集 mAP 比训练集低很多差距超过 0.2原因划分时没有分层验证集里猫狗比例和训练集差太多或者验证集里全是难样本。解决用第 3 章的分层划分脚本重跑固定seed并统计两个集合的类别分布。如果差距依然大说明 1000 张图对猫狗检测来说偏少可以加数据增强YOLOv8 默认开启 mosaic 和 flip不需要额外配置。5.4 现象推理时框的位置整体偏移原因训练时imgsz和推理时不一致或者图片在预处理时被拉伸而不是保持宽高比填充。YOLOv8 默认用 letterbox 保持宽高比如果你自己写推理脚本要确保和训练时一样的预处理。解决直接用yolo detect predict命令不要自己写预处理除非你清楚 letterbox 的填充计算。5.5 现象COCO 格式的 json 解析后框数量对不上原因COCO 的annotations里iscrowd1的框通常表示密集区域YOLO 训练时一般忽略这些框。如果转换时没过滤会把 crowd 区域也当成目标导致框数量偏多。解决转换时加if ann.get(iscrowd, 0) 1: continue只保留iscrowd0的标注。另外 COCO 的bbox是 [x, y, w, h]转 YOLO 时要先算 xmax x wymax y h再归一化。6. 用 1000 张猫狗图验证你的自定义数据集链路这套猫狗数据集最大的用处不是训练一个猫狗检测器而是当作你自定义数据集的「对照组」。当你手里有一个新数据集要接入 YOLO 时先用猫狗数据跑一遍完整流程VOC 转 YOLO、COCO 转 YOLO、分层划分、训练、推理。如果猫狗数据能跑到 mAP50 0.85 以上说明你的转换脚本、划分逻辑、训练配置都没问题再把同样的脚本套到新数据集上出问题的概率会小很多。我自己的习惯是每次写新的数据转换脚本都先拿猫狗数据跑一遍用第 2 章的校验脚本画框对比。有一次转一个工业缺陷数据集VOC 转 YOLO 后 mAP 只有 0.4用猫狗数据一测发现转换脚本没问题最后查出来是缺陷数据集的 XML 里size字段写的是 0导致归一化分母为 0所有框都变成 NaN。如果没有猫狗数据做对照我可能会花更多时间在训练参数上瞎调。进阶用法上你可以把猫狗数据集的 1000 张图按 8:1:1 分成 train/val/testtest 集不参与训练和早停只用来做最终评估。YOLOv8 的data.yaml里加一行test: images/test训练完用yolo detect val modelbest.pt datadata.yaml splittest跑测试集。这样得到的 mAP 更接近真实部署效果避免在验证集上过拟合。另外如果你要做实例分割猫狗数据集的 VOC 标签里没有分割多边形需要自己用 labelme 或 CVAT 重新标注不能直接转。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站