简介这份狗狗行为检测数据集面向计算机视觉学习者与目标检测开发者适用于宠物行为识别、动物姿态分析等场景的模型训练与算法验证。数据以VOC与YOLO双格式提供压缩包内分设图片、xml标注与txt标签三个文件夹共2000个文件其中xml与txt各1551份另有说明文件包体约57.77MB方便直接接入主流检测框架。数据集包含1551张清晰jpg图像覆盖bark、default、eat、lyingDown、lyingProne、sit、sleep、stand共8类行为矩形框标注总数1613个各类分布相对均衡如sleep 253框、lyingDown 240框、stand 231框可支撑多类别行为识别任务。目前已有179人学习下载。所有标注均经人工核对格式规范、类别明确读者可直接用于YOLO系列或VOC兼容模型的训练与评估省去自行采集与标注的成本也便于开展类别分布分析与数据增强实验。1. 狗狗行为检测数据集1551 张 8 类标注YOLO 和 VOC 双格式到底怎么用拿到一个标注好的数据集最怕的不是数据少而是格式对不上、类别看不懂、训练时才发现标注有问题。狗狗行为检测数据集 1551 张 8 种 YOLOVOC 格式这个标题里其实藏着三个关键信息数据规模不大1551 张、任务类型明确行为检测8 个类别、格式已经备好两套YOLO 的 txt 和 VOC 的 xml。对于想跑通 YOLO 训练流程、又不想从零标注的人来说这种体量的数据集刚好卡在一个舒服的位置——不算玩具集也不会大到让单卡机器跑不动。这篇文章面向的是想拿它直接训练、验证、甚至做部署原型的从业者。我会把重点放在拿到压缩包后先看什么、YOLO 和 VOC 两套格式怎么选、类别映射怎么核对、训练参数怎么设、以及那些只有真正跑过一遍才会遇到的坑。不会讲 YOLO 算法发展史也不会贴一堆论文公式只讲怎么让这 1551 张图在你的机器上跑出可用的检测结果。2. 拆开压缩包先别急着训练1551 张 8 类数据的结构核对与格式选型2.1 解压后第一件事目录结构和文件数量对账拿到数据集-狗狗行为检测数据集1551张8种YOLOVOC格式.zip很多人第一反应是直接解压然后找data.yaml开跑。我一般会先做一次目录对账因为标注数据集最常见的翻车点就是图文不匹配、类别编号错位、或者某个类别的样本少到根本训不出来。常见做法是解压后先看顶层目录通常会有images/、labels/、annotations/、JPEGImages/这类文件夹。YOLO 格式的标注是每张图对应一个.txtVOC 格式是每张图对应一个.xml。先确认三件事图片总数是不是 1551、txt 文件数是不是 1551、xml 文件数是不是 1551。如果某个数字对不上后面训练时会出现“找不到标签”或者“空标签”的报错。# 统计图片数量常见图片后缀 find . -type f \( -iname *.jpg -o -iname *.jpeg -o -iname *.png \) | wc -l # 统计 YOLO 格式的 txt 标注数量 find . -type f -name *.txt | wc -l # 统计 VOC 格式的 xml 标注数量 find . -type f -name *.xml | wc -l这三条命令跑完如果三个数字都是 1551说明文件层面是齐的。如果 txt 或 xml 少了几十个先别急着补去检查是不是有图片没有对应标注或者标注文件命名和图片命名不一致。命名不一致是高频问题比如图片叫dog_001.jpg标注叫dog_001.txt没问题但有的数据集会写成dog-001.txt这种在 Windows 上可能不报错到 Linux 训练时直接找不到。2.2 YOLO 格式和 VOC 格式的取舍什么时候用哪个这个数据集同时给了 YOLO 和 VOC 两套格式不是让你两个都用而是让你根据训练框架选一个。YOLO 格式的标注是归一化后的class_id x_center y_center width height每行一个目标直接对应 YOLOv5/v8/v11 的训练输入。VOC 格式是 XML包含bndbox的绝对坐标和类别名适合用 MMDetection、Detectron2 或者自己写解析脚本。如果你用的是 Ultralytics 系的 YOLO直接走 YOLO 格式省掉转换步骤。如果你用的是 MMDetection 或者想用 VOC 的ImageSets/Main做划分那就走 VOC 格式。我一般会建议先看 YOLO 格式的classes.txt或者data.yaml里的类别顺序因为 YOLO 的class_id是从 0 开始的整数一旦顺序错了训练出来的模型会把“坐”识别成“跑”。# 快速检查 YOLO 标注的类别分布和坐标范围 import os from collections import Counter label_dir labels/train # 根据实际路径调整 class_counter Counter() bad_lines [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: bad_lines.append((fname, line.strip())) continue cls_id int(parts[0]) class_counter[cls_id] 1 # 检查归一化坐标是否在 0-1 之间 for v in parts[1:]: if not (0 float(v) 1): bad_lines.append((fname, line.strip())) break print(类别分布:, dict(sorted(class_counter.items()))) print(异常行数:, len(bad_lines)) for item in bad_lines[:5]: print(item)这段脚本做两件事统计每个类别的框数量以及检查坐标是否越界。如果某个类别只有几十个框而其他类别有上千个训练时这个类别基本学不出来需要考虑过采样或者合并类别。坐标越界的情况在人工标注里很常见YOLO 训练时不会直接报错但会导致损失异常最后模型对边界目标检测效果很差。2.3 8 个类别的语义核对别让“行为”和“姿态”混在一起狗狗行为检测的 8 个类别常见的有sitting、standing、running、sleeping、eating、barking、playing、walking这类。但不同数据集对“行为”的定义边界不一样有的把“站立”和“行走”分开有的把“吠叫”和“张嘴”混在一起。拿到数据后我一般会抽 20 张图把标注框画出来看一眼确认类别语义和视觉表现是一致的。# 用 PIL 画框抽查标注确认类别和视觉对应关系 from PIL import Image, ImageDraw import os img_path images/train/dog_001.jpg label_path labels/train/dog_001.txt class_names [sitting, standing, running, sleeping, eating, barking, playing, walking] # 按实际顺序替换 img Image.open(img_path).convert(RGB) w, h img.size draw ImageDraw.Draw(img) with open(label_path) as f: for line in f: cls_id, xc, yc, bw, bh map(float, line.split()) x1 (xc - bw / 2) * w y1 (yc - bh / 2) * h x2 (xc bw / 2) * w y2 (yc bh / 2) * h draw.rectangle([x1, y1, x2, y2], outlinered, width3) draw.text((x1, y1 - 12), class_names[int(cls_id)], fillred) img.save(check_001.jpg)画出来之后重点看两件事框是不是只框住了狗的身体有没有把背景里的其他狗也框进去类别标签和狗的实际姿态是否一致。如果发现“坐”的框里狗是站着的说明标注规范有问题这种数据直接拿去训练会把模型带偏。1551 张不算多抽检 20 到 30 张就能对标注质量有个基本判断。3. 用 YOLOv8 跑通第一轮训练从 data.yaml 到训练命令的完整参数3.1 目录重组把数据集整理成 YOLOv8 认识的格式YOLOv8 对目录结构有固定要求常见的是images/train、images/val、labels/train、labels/val。如果压缩包里的目录不是这个结构需要先重组。我一般会写个脚本做软链接或者直接复制避免手动拖拽出错。# 假设原始目录是 images/ 和 labels/按 8:2 划分 train/val mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val # 用 Python 做划分更可控 python - EOF import os, random, shutil img_dir images lbl_dir labels out_img_train dataset/images/train out_img_val dataset/images/val out_lbl_train dataset/labels/train out_lbl_val dataset/labels/val for d in [out_img_train, out_img_val, out_lbl_train, out_lbl_val]: os.makedirs(d, exist_okTrue) files [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .jpeg, .png))] random.seed(42) random.shuffle(files) split int(len(files) * 0.8) for i, fname in enumerate(files): base os.path.splitext(fname)[0] lbl_name base .txt if i split: shutil.copy(os.path.join(img_dir, fname), os.path.join(out_img_train, fname)) shutil.copy(os.path.join(lbl_dir, lbl_name), os.path.join(out_lbl_train, lbl_name)) else: shutil.copy(os.path.join(img_dir, fname), os.path.join(out_img_val, fname)) shutil.copy(os.path.join(lbl_dir, lbl_name), os.path.join(out_lbl_val, lbl_name)) print(train:, split, val:, len(files) - split) EOF这里用random.seed(42)是为了让划分可复现不然每次跑出来的验证集不一样对比实验就没法做。8:2 是个经验值1551 张的话训练集大概 1240 张验证集 310 张。如果某个类别样本特别少可以考虑用分层抽样保证验证集里每个类别都有一定数量的样本。3.2 data.yaml 的写法类别名、路径和 nc 的对应关系YOLOv8 的data.yaml三个关键字段path、train、val、names。nc在 YOLOv8 里可以不写框架会根据names的长度自动推断但写上更清晰。path: /home/user/dataset train: images/train val: images/val names: 0: sitting 1: standing 2: running 3: sleeping 4: eating 5: barking 6: playing 7: walking这里最容易翻车的是names的顺序。YOLO 格式的class_id是整数names里的键必须和标注文件里的class_id一一对应。如果标注里0是standing你names里写0: sitting训练出来的模型会把站立识别成坐。核对方法很简单从标注文件里随机抽几个class_id去图片上画框看实际姿态和names对一遍。3.3 训练命令和关键参数batch、imgsz、epochs 怎么定YOLOv8 的训练命令很简洁但参数设不对要么显存爆要么训不动。yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/dog_behavior \ nameexp1modelyolov8n.pt是最小的预训练模型1551 张图用 nano 版本足够跑通流程。如果显存够可以换yolov8s.pt或yolov8m.pt但要注意小数据集上用大模型容易过拟合。imgsz640是默认值如果图片里狗的目标很小可以提到 960 或 1280但显存占用会明显增加。batch16在 8GB 显存上跑 640 分辨率基本安全如果报 OOM先降到 8 或 4。patience20表示 20 个 epoch 验证指标不提升就早停小数据集上这个参数能省不少时间。训练开始后重点看mAP50和mAP50-95两个指标。如果mAP50一直上不去先别急着调模型回去检查标注质量和类别分布。1551 张图 8 个类别平均每个类别不到 200 个框如果某个类别只有 50 个框mAP低是正常的需要补数据或者做数据增强。4. 训练完别只看 mAP混淆矩阵、误检样本和类别不均衡的排查4.1 混淆矩阵怎么看哪些类别在互相“串门”YOLOv8 训练完会在runs/dog_behavior/exp1/下生成confusion_matrix.png。这个图比mAP更能说明问题。如果sitting和standing之间的格子颜色很深说明模型分不清这两个类别。原因通常是两个类别的视觉差异小或者标注时边界模糊。# 用 seaborn 重新画归一化混淆矩阵方便看百分比 import seaborn as sns import matplotlib.pyplot as plt import numpy as np # 假设从验证结果里拿到了混淆矩阵数据 cm np.array([ [45, 5, 0, 0, 0, 0, 0, 0], [8, 38, 2, 0, 0, 0, 0, 0], [0, 3, 40, 0, 0, 0, 0, 0], [0, 0, 0, 35, 0, 0, 0, 0], [0, 0, 0, 0, 30, 2, 0, 0], [0, 0, 0, 0, 3, 28, 0, 0], [0, 0, 0, 0, 0, 0, 25, 3], [0, 0, 0, 0, 0, 0, 4, 22], ]) class_names [sitting, standing, running, sleeping, eating, barking, playing, walking] cm_norm cm.astype(float) / cm.sum(axis1, keepdimsTrue) plt.figure(figsize(10, 8)) sns.heatmap(cm_norm, annotTrue, fmt.2f, xticklabelsclass_names, yticklabelsclass_names, cmapBlues) plt.xlabel(Predicted) plt.ylabel(True) plt.savefig(confusion_matrix_norm.png, dpi150)归一化之后看的是“真实类别被预测成各个类别的比例”。如果sitting有 20% 被预测成standing说明这两个类别的特征在模型眼里太接近。解决办法有两个一是合并这两个类别如果业务上不需要严格区分二是补充更多“坐”和“站”的边界样本让模型学到更细的差异。4.2 误检和漏检的样本分析把验证集预测结果导出来看mAP是个聚合指标看不出具体哪张图出了问题。我一般会把验证集的预测结果导出来按置信度排序看低置信度的预测和漏检的图。# 用训练好的模型在验证集上推理保存带框的结果 yolo detect predict \ modelruns/dog_behavior/exp1/weights/best.pt \ sourcedataset/images/val \ saveTrue \ conf0.25 \ projectruns/dog_behavior \ nameval_predconf0.25是默认的置信度阈值调低会看到更多误检调高会看到更多漏检。我一般会跑两次一次conf0.1看误检一次conf0.5看漏检。误检多的类别通常是背景里有类似狗姿态的物体比如椅子腿被识别成站立的狗。漏检多的类别通常是目标太小、遮挡严重、或者训练样本里这个姿态的图太少。4.3 类别不均衡的处理过采样、加权和合并的取舍1551 张图 8 个类别如果某个类别只有 80 个框而最多的有 400 个框训练时模型会偏向多数类。YOLOv8 本身没有直接的类别权重参数但可以通过复制少数类图片来过采样。# 对少数类图片过采样复制到训练集 import os, shutil from collections import Counter label_dir dataset/labels/train img_dir dataset/images/train target_count 200 # 目标框数量 class_files {} for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: classes [int(line.split()[0]) for line in f if line.strip()] for c in classes: class_files.setdefault(c, []).append(fname) for cls_id, files in class_files.items(): if len(files) target_count: continue need target_count - len(files) for i in range(need): src files[i % len(files)] base os.path.splitext(src)[0] img_src os.path.join(img_dir, base .jpg) lbl_src os.path.join(label_dir, src) img_dst os.path.join(img_dir, foversample_{cls_id}_{i}_{base}.jpg) lbl_dst os.path.join(label_dir, foversample_{cls_id}_{i}_{base}.txt) shutil.copy(img_src, img_dst) shutil.copy(lbl_src, lbl_dst)过采样之后要重新跑一遍类别分布统计确认每个类别的框数量差距缩小了。但过采样不是万能的如果少数类的视觉多样性本身就不够复制再多也只是让模型记住那几张图验证集上还是不行。这种情况下合并语义相近的类别或者补充新数据更有效。5. 避坑与排查狗狗行为检测数据集训练中最容易翻车的 5 个点5.1 现象训练 loss 正常下降但 mAP 一直是 0原因data.yaml里的names顺序和标注文件里的class_id不一致或者val路径写错导致验证集为空。YOLOv8 在验证集为空时不会报错但mAP会一直是 0。解决先检查data.yaml的val路径是否存在再抽 5 张验证集图片用yolo detect predict单独跑一遍看输出的类别名和实际是否一致。如果类别名对不上把names的顺序改成和标注文件一致。5.2 现象训练到一半报 CUDA out of memory原因batch或imgsz设得太大或者图片分辨率本身很高YOLOv8 在数据加载时会把图片 resize 到imgsz但如果原图是 4000x3000resize 前的内存占用也不小。解决先把batch降到 8 或 4如果还报错把imgsz从 640 降到 416。另外检查workers参数Windows 上workers设大了容易出问题可以设成 0 或 2。5.3 现象验证集 mAP 很高但实际推理时框的位置偏移很大原因训练时用了rectTrue或者数据增强里的mosaic、mixup这些增强会改变目标的绝对位置如果验证集没有做同样的预处理框的位置会对不上。解决推理时确认imgsz和训练时一致不要开rect。如果训练时用了mosaic推理时关掉。另外检查标注文件里的坐标是不是归一化后的值如果误用了绝对坐标训练时 loss 会异常但框架不一定报错。5.4 现象某个类别的 mAP 始终为 0其他类别正常原因这个类别的标注文件里class_id写错了比如应该是3写成了8而names里只有 0 到 7YOLOv8 会忽略越界的类别导致这个类别没有正样本。解决用 2.2 节的脚本统计类别分布看是否有class_id超出names长度的情况。如果有批量修正标注文件里的class_id。5.5 现象训练完模型在测试图上框出了狗但类别全是错的原因names的顺序和标注文件不一致但训练时 loss 正常下降因为模型学的是“框的位置”和“某个整数 ID”至于这个 ID 对应什么名字模型不关心。所以 loss 正常不代表类别映射正确。解决训练前一定做一次可视化抽检把标注框和类别名画在图上人眼确认一遍。这是最笨但最有效的方法能省掉后面反复调参的时间。6. 从 1551 张到可用模型用验证集反推标注质量的一个小技巧训练完一轮之后与其盯着mAP数字发呆不如做一件事把验证集里所有预测错误的样本按错误类型分类然后反推标注质量。具体做法是用训练好的模型在验证集上推理把预测结果和真实标注做匹配找出那些“模型预测了一个框但真实标注里没有对应类别”的图。这些图里有一部分是模型误检但还有一部分是标注漏标。我一般会写个脚本把 IoU 大于 0.5 但类别不匹配的框单独存出来人工看一遍。如果发现某张图里狗明明是坐着的标注却写成了站立那说明标注规范有问题这种图在训练集里越多模型越学不好。1551 张的体量抽 50 张做这个检查大概能发现 5 到 10 张有问题的图把这些图修正后重新训练mAP通常能涨 2 到 5 个点。另一个技巧是把验证集里模型置信度在 0.3 到 0.5 之间的预测框导出来这些是模型的“犹豫样本”。如果这些样本里大部分是正确的说明模型还有提升空间可以适当降低推理阈值如果大部分是错的说明模型对这个类别的判别力不够需要补数据。# 找出 IoU0.5 但类别不匹配的预测辅助排查标注问题 def iou(box1, box2): x1 max(box1[0], box2[0]) y1 max(box1[1], box2[1]) x2 min(box1[2], box2[2]) y2 min(box1[3], box2[3]) inter max(0, x2 - x1) * max(0, y2 - y1) area1 (box1[2] - box1[0]) * (box1[3] - box1[1]) area2 (box2[2] - box2[0]) * (box2[3] - box2[1]) return inter / (area1 area2 - inter 1e-6) # 假设 preds 和 gts 是解析好的列表每个元素是 (cls_id, x1, y1, x2, y2) # 这里只写匹配逻辑实际使用时从 YOLO 的 predict 结果和标注文件里读取 mismatches [] for pred in preds: matched False for gt in gts: if iou(pred[1:], gt[1:]) 0.5: matched True if pred[0] ! gt[0]: mismatches.append((pred, gt)) break if not matched: mismatches.append((pred, None)) print(类别不匹配或漏标数量:, len(mismatches))这个脚本的核心逻辑是对每个预测框找真实标注里 IoU 最大的框如果 IoU 够大但类别不同就记下来。这些记录里既有模型误判也有标注错误。人工过一遍把标注错误改掉比调模型参数见效快得多。最后说个我自己的习惯每次拿到新数据集先花 20 分钟做目录对账和可视化抽检再花 10 分钟跑一个 1 epoch 的冒烟训练确认流程能跑通。这半小时的投入能省掉后面几小时的排错时间。1551 张 8 类的狗狗行为数据集不算大但标注质量决定了它的上限模型和参数只是逼近这个上限的手段。希望帮到你。本文还有配套的精品资源点击获取