简介:这份资源面向计算机视觉入门者、目标检测课程学习者以及需要快速搭建猫狗识别实验的开发者,提供了一套真实场景下的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/ymax:
<annotation> <filename>cat_001.jpg</filename> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>cat</name> <bndbox> <xmin>100</xmin> <ymin>80</ymin> <xmax>300</xmax> <ymax>320</ymax> </bndbox> </object> </annotation>COCO 格式是一个大的 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 height:
0 0.312500 0.416667 0.312500 0.500000计算过程:x_center = (100+300)/2/640 = 0.3125,y_center = (80+320)/2/480 = 0.4167,width = (300-100)/640 = 0.3125,height = (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_ratio=0.2, seed=42): 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_ok=True) os.makedirs(lbl_out, exist_ok=True) 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(f'train: {len(train_list)}, val: {len(val_list)}') return train_list, val_list stratified_split('images', 'yolo_labels', 'dataset', val_ratio=0.2, seed=42)关键参数:val_ratio=0.2表示验证集占 20%,1000 张图大约 200 张验证;seed=42固定随机种子,保证每次划分结果一致,方便复现;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 对应 cat,1 对应 dog。如果转换时把猫狗顺序搞反了,训练也能跑,但推理时会把猫标成狗。nc必须等于len(names),写错会报AssertionError。
4.2 启动训练的命令与参数含义
yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/catdog \ name=exp1model=yolov8n.pt用 nano 版本,1000 张图足够,不需要更大的模型;imgsz=640是输入分辨率,猫狗图如果原图小于 640,会被放大,如果原图很大,会被缩小,统一到 640 是常见做法;batch=16在 8GB 显存上比较稳,显存不够就降到 8;lr0=0.01是初始学习率,YOLOv8 默认用 SGD 时这个值合适,如果用 AdamW 可以降到 0.001;patience=20表示 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 model=best.pt source=test_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 为 0,YOLO 计算 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里iscrowd=1的框通常表示密集区域,YOLO 训练时一般忽略这些框。如果转换时没过滤,会把 crowd 区域也当成目标,导致框数量偏多。解决:转换时加if ann.get('iscrowd', 0) == 1: continue,只保留iscrowd=0的标注。另外 COCO 的bbox是 [x, y, w, h],转 YOLO 时要先算 xmax = x + w,ymax = 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/test,test 集不参与训练和早停,只用来做最终评估。YOLOv8 的data.yaml里加一行test: images/test,训练完用yolo detect val model=best.pt data=data.yaml split=test跑测试集。这样得到的 mAP 更接近真实部署效果,避免在验证集上过拟合。另外,如果你要做实例分割,猫狗数据集的 VOC 标签里没有分割多边形,需要自己用 labelme 或 CVAT 重新标注,不能直接转。
希望帮到你。
本文还有配套的精品资源,点击获取