☰
YOLO猫狗目标检测实战:从数据格式转换到训练评估全流程
2026/9/28 12:51:49 网站建设 项目流程

简介:这是一份面向目标检测初学者与算法工程师的YOLO猫狗检测数据集,采集真实场景下场景丰富的猫狗图片,经labelimg精细标注,标注框质量较高,可直接用于YOLO系列模型训练与验证。资源包共约2000个文件,以1986个xml标注文件为主,另含少量html教程、txt说明与py脚本,压缩包约115.12MB,体积轻便便于本地部署。标签同时提供voc、coco和yolo三种格式,分别存放于不同文件夹,省去格式转换环节。随包附赠YOLO环境搭建、训练案例教程及数据集划分脚本,可按需自行划分训练集、验证集与测试集,快速跑通从数据准备到模型训练的全流程。目前已有535人学习下载,适合想练手目标检测、复现YOLO训练流程或搭建猫狗识别应用的读者参考使用。

1. 从一堆散装文件到能跑的训练管线:这套猫狗检测资源到底省了哪几步

如果你做过目标检测,大概率经历过这个场景:网上找到一份数据集,图片倒是能看,但标签只有一种格式,想换 YOLO 训练还得自己写转换脚本;好不容易转完,又发现没有划分脚本,手动分训练集验证集分到怀疑人生。这套 YOLO 猫狗目标检测数据集,本质上就是把这几个最耗时的环节一次性打包好了——5000 张真实场景图片,同时提供 VOC(xml)、COCO(json)、YOLO(txt)三种标签格式,外加训练集/验证集/测试集划分脚本和 Windows、Linux 双平台的 YOLO 环境搭建与训练教程。

它解决的不是"有没有数据"的问题,而是"从拿到数据到跑通训练"这条链路上反复造轮子的痛点。适合两类人:一是刚接触目标检测、想找一个完整案例把流程走通的新手;二是手头有自己数据、想参考一套标准划分和转换流程的从业者。下面按实际拆包后的使用顺序,把这份资源怎么用、参数怎么调、哪里容易翻车讲清楚。

2. 三种标签格式的差异与转换逻辑:为什么不能只留 YOLO 格式

2.1 VOC、COCO、YOLO 三种格式的核心区别

很多人拿到数据集第一反应是"我只要 YOLO 格式,其他删掉"。先别急,三种格式各有存在价值,理解它们的差异能帮你在后续换框架或做数据增强时少走弯路。

VOC 格式以 xml 文件存储,每个标注框记录xmin、ymin、xmax、ymax四个绝对像素坐标,外加类别名。它的优点是可读性强,用 labelImg 打开就能直接改,适合人工校验标注质量。缺点是文件体积大,5000 张图对应 5000 个 xml,批量处理时 IO 开销明显。

COCO 格式把所有标注集中在一个 json 文件里,坐标同样是绝对像素值,但结构是[x, y, width, height]。它的优势是单文件管理方便,pycocotools 生态成熟,做评估时 mAP 计算直接调库就行。缺点是 json 文件一旦损坏,整个数据集的标注全丢,而且大文件用文本编辑器打开基本卡死。

YOLO 格式每张图对应一个 txt,每行是class_id x_center y_center width height,全部归一化到 0~1 之间。这是 YOLO 系列训练直接吃的格式,也是三种里最紧凑的。但归一化坐标不可读,改一个框得先反算回像素值,人工调整很别扭。

提示:如果后续要做数据增强(比如 mosaic、mixup),YOLO 格式最方便;如果要做标注质量抽检,切回 VOC 格式用 labelImg 看最直观。

2.2 格式转换的关键参数与代码实现

资源包里已经分好了三个文件夹,但如果你自己有一批 VOC 标注想转成 YOLO,下面这段脚本可以直接抄。核心逻辑是读 xml 里的绝对坐标,除以图片宽高做归一化,同时把类别名映射成从 0 开始的整数 id。

import os import xml.etree.ElementTree as ET from PIL import Image # 类别映射表,必须和训练时的 data.yaml 保持一致 CLASS_MAP = {"cat": 0, "dog": 1} def voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() # 图片文件名从 xml 的 filename 字段取,不要靠 xml 文件名猜 img_name = root.find("filename").text img_path = os.path.join(img_dir, img_name) w, h = Image.open(img_path).size lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text if cls_name not in CLASS_MAP: continue cls_id = CLASS_MAP[cls_name] bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 归一化:中心点坐标和宽高都除以图片尺寸 x_center = (xmin + xmax) / 2.0 / w y_center = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}") out_file = os.path.join(out_dir, xml_file.replace(".xml", ".txt")) with open(out_file, "w") as f: f.write("\n".join(lines)) voc_to_yolo("./annotations_xml", "./images", "./labels_yolo")

这段代码有几个参数需要根据实际情况改。CLASS_MAP必须和后续训练配置文件里的names列表顺序完全一致,否则模型学出来的类别会错位。归一化保留 6 位小数是 YOLO 官方推荐的精度,再少可能影响小目标框的准确性。另外注意img_name是从 xml 内部读的,不是从 xml 文件名推导的——有些标注工具导出的 xml 文件名和实际图片名不一致,靠文件名猜会翻车。

COCO 转 YOLO 的逻辑类似,区别在于 COCO 的 json 里bbox字段本身就是[x, y, w, h]格式,但它是绝对像素值,还是需要除以图片宽高。而且 COCO 的category_id不一定从 0 开始连续,需要自己建映射表重新编号。

3. 数据集划分脚本怎么用:train/val/test 比例与随机种子控制

3.1 划分脚本的三种模式与适用场景

资源包里带了三个划分脚本,分别对应不同需求。训练集、验证集、测试集划分脚本(图片标签划分写入新文件夹).py是把图片和标签一起复制到新的 train/val/test 文件夹,适合需要物理隔离数据的场景。训练集、验证集划分脚本(图片标签划分写入新文件夹).py只分 train 和 val 两份,适合数据量不大、不想单独留测试集的情况。split_train_val生成ImageSets下txt文件划分脚本.py不复制文件,只生成 ImageSets 目录下的 txt 索引文件,适合数据量大、不想占双倍磁盘空间的场景。

选哪种取决于你的磁盘和后续流程。如果要做多次不同比例的实验,用索引文件模式最灵活,改个比例重跑脚本就行,不用反复复制几十 G 的图片。如果是一次性划分完就不动了,物理复制更省心,后续训练直接指向对应文件夹即可。

3.2 划分比例设置与随机种子固定

下面以三份划分脚本为例,说明关键参数怎么调。核心就两个:比例和随机种子。

import os import random import shutil # 关键参数 VAL_RATIO = 0.2 # 验证集比例 TEST_RATIO = 0.1 # 测试集比例 SEED = 42 # 随机种子,固定后每次划分结果一致 random.seed(SEED) img_dir = "./images" label_dir = "./labels" out_root = "./dataset_split" # 收集所有图片文件名(不含扩展名) names = [os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(".jpg")] random.shuffle(names) n_total = len(names) n_val = int(n_total * VAL_RATIO) n_test = int(n_total * TEST_RATIO) n_train = n_total - n_val - n_test splits = { "train": names[:n_train], "val": names[n_train:n_train + n_val], "test": names[n_train + n_val:] } for split, name_list in splits.items(): img_out = os.path.join(out_root, split, "images") lbl_out = os.path.join(out_root, split, "labels") os.makedirs(img_out, exist_ok=True) os.makedirs(lbl_out, exist_ok=True) for name in name_list: shutil.copy(os.path.join(img_dir, name + ".jpg"), os.path.join(img_out, name + ".jpg")) shutil.copy(os.path.join(label_dir, name + ".txt"), os.path.join(lbl_out, name + ".txt")) print(f"train: {n_train}, val: {n_val}, test: {n_test}")

SEED这个参数很多人忽略,但不固定种子的话,每次跑划分脚本结果都不一样,实验没法复现。我一般固定成 42 或 0,团队里统一一个值。VAL_RATIO和TEST_RATIO的常见设置是 8:1:1 或 7:2:1,5000 张图的话验证集 500~1000 张足够评估用,测试集 500 张左右能给出稳定的指标。

注意:划分前一定要确认图片和标签文件名一一对应。如果某张图没有对应的 txt 标签,复制时会直接报 FileNotFoundError,这时候要么补标签要么把这张图排除掉,不要跳过——跳过会导致训练时找不到部分图片。

3.3 划分后的目录结构校验

划分完成后,建议跑一段校验代码确认没有遗漏和错位。

import os for split in ["train", "val", "test"]: img_set = set(os.path.splitext(f)[0] for f in os.listdir(f"./dataset_split/{split}/images")) lbl_set = set(os.path.splitext(f)[0] for f in os.listdir(f"./dataset_split/{split}/labels")) only_img = img_set - lbl_set only_lbl = lbl_set - img_set print(f"{split}: 图片 {len(img_set)}, 标签 {len(lbl_set)}, 缺标签 {len(only_img)}, 缺图片 {len(only_lbl)}")

如果缺标签或缺图片不为 0,说明划分过程中有文件不配对,需要回到原始数据排查。这个校验步骤花不了几秒钟,但能避免训练跑了一半才报错。

4. Windows 与 Linux 双平台环境搭建:从 CUDA 到 ultralytics 安装

4.1 Windows 平台环境搭建步骤

资源包里的 Windows 教程覆盖了从显卡驱动到 YOLO 训练框架的完整链路。按实际踩坑经验,顺序不能乱:先确认显卡驱动版本,再装 CUDA Toolkit,然后装 cuDNN,最后装 PyTorch 和 ultralytics。

第一步,打开命令行执行nvidia-smi,右上角显示的CUDA Version是你驱动支持的最高 CUDA 版本。比如显示 12.1,那你装 CUDA 11.8 或 12.1 都可以,但不能装 12.4。

第二步,去 NVIDIA 官网下载对应版本的 CUDA Toolkit。安装时选"自定义",取消勾选 Visual Studio Integration(除非你确实要用 VS 编译),其他默认即可。

第三步,装 PyTorch。不要直接pip install torch,那样装的是 CPU 版本。要去 PyTorch 官网复制对应 CUDA 版本的安装命令,比如:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

第四步,装 ultralytics:

pip install ultralytics

装完后验证 GPU 是否可用:

import torch print(torch.cuda.is_available()) # 应该输出 True print(torch.cuda.get_device_name(0)) # 显示显卡型号

如果is_available()返回 False,九成是 PyTorch 装成了 CPU 版,卸载重装对应 CUDA 版本的即可。

4.2 Linux(Ubuntu)平台环境搭建要点

Linux 下的流程和 Windows 类似,但多了几个容易翻车的点。首先是驱动安装,Ubuntu 自带的 nouveau 驱动会和 NVIDIA 驱动冲突,需要先禁用:

sudo bash -c "echo blacklist nouveau > /etc/modprobe.d/blacklist-nvidia-nouveau.conf" sudo bash -c "echo options nouveau modeset=0 >> /etc/modprobe.d/blacklist-nvidia-nouveau.conf" sudo update-initramfs -u sudo reboot

重启后进 BIOS 关闭 Secure Boot,再用ubuntu-drivers devices查看推荐的驱动版本,用apt install安装。装完nvidia-smi能正常输出就说明驱动 OK。

CUDA 和 cuDNN 在 Linux 下建议用 runfile 方式安装,比 apt 更可控。装完后记得把 CUDA 路径写进环境变量:

export PATH=/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH

PyTorch 和 ultralytics 的安装命令和 Windows 一致。Linux 下额外建议装一下opencv-python-headless,避免服务器上没有图形界面时 cv2 报错。

提示:如果实验室或公司服务器是多用户共用,建议用 conda 创建独立环境,不要直接装在系统 Python 里,否则不同项目的 CUDA 版本冲突会让你很头疼。

5. 训练配置与常见报错排查:data.yaml 怎么写、BN 崩溃怎么办

5.1 data.yaml 的关键字段与路径写法

YOLO 训练的第一步是准备data.yaml,这个文件告诉框架去哪里找图片、有几类目标。以下是对应这份猫狗数据集的配置:

path: ./dataset_split # 数据集根目录 train: train/images # 训练集图片路径,相对于 path val: val/images # 验证集图片路径 test: test/images # 测试集图片路径,可选 nc: 2 # 类别数 names: # 类别名列表,顺序必须和标签里的 class_id 对应 0: cat 1: dog

path可以用绝对路径也可以用相对路径,但相对路径是相对于你执行训练命令时所在的目录,不是相对于 yaml 文件位置。很多人在这里翻车——yaml 放在configs/下,path写./dataset_split,结果在项目根目录执行训练时找不到数据。稳妥做法是path写绝对路径,或者确保执行目录和 yaml 里的相对路径基准一致。

names的顺序至关重要。如果你的标签里 cat 是 0、dog 是 1,但 yaml 里写反了,模型会把猫预测成狗,而且 loss 还能正常下降——这种错误不看推理结果很难发现。

5.2 启动训练与关键超参数

配置写好后,启动训练:

yolo detect train data=./data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16

几个关键参数的含义:model指定预训练权重,yolov8n.pt是最小的 nano 版本,速度快但精度低,数据量 5000 张的话建议从yolov8s.pt或yolov8m.pt起步。imgsz是输入分辨率,640 是默认值,如果图片里猫狗占画面比例很小,可以提到 1280,但显存占用会翻倍。batch根据显存调,8G 显存跑 640 分辨率大概能上 16,12G 能上 32。

训练过程中重点关注mAP50和mAP50-95两个指标。mAP50是 IoU 阈值 0.5 时的平均精度,涨到 0.8 以上说明模型基本可用了。mAP50-95更严格,通常比mAP50低 0.15~0.25。如果训练 loss 一直在降但验证 mAP 不涨,大概率是过拟合了,需要加数据增强或减模型复杂度。

5.3 避坑与常见问题排查

现象一:训练启动后报Dataset not found,但路径明明是对的。原因通常是 yaml 里的相对路径基准和你执行命令的目录不一致。解决方法是把path改成绝对路径,或者cd到 yaml 所在目录再执行训练。

现象二:训练几个 epoch 后 loss 突然变成 NaN,BN 层崩溃。这是 YOLO 训练里比较常见的玄学问题,通常和 batch size 太小或学习率太高有关。先把batch调大(至少 8),如果显存不够就降imgsz。还不行就把学习率从默认的 0.01 降到 0.001,加lr0=0.001参数。另外检查数据里有没有标注框宽高为 0 的脏数据,这种框会导致除零。

现象三:训练完推理时所有框都标成同一类。九成是data.yaml里names顺序和标签里的class_id对不上。回头检查转换脚本里的CLASS_MAP和 yaml 是否一致。另一个可能是某一类样本太少,模型直接摆烂全预测成多数类,这时候需要检查两类图片数量是否均衡。

现象四:nvidia-smi显示显存占满但 GPU 利用率接近 0。这是数据加载瓶颈,CPU 预处理速度跟不上 GPU 计算速度。解决办法是把workers参数调大(默认 8,可以试 16),或者把图片提前 resize 到接近imgsz的尺寸,减少训练时的缩放开销。

现象五:验证集 mAP 比训练集低很多,差距超过 0.2。典型过拟合。优先加数据增强,YOLO 默认开了 mosaic 和 flip,可以再加scale=0.5和hsv_h=0.015。如果还不行就换更小的模型,或者检查训练集和验证集是不是同分布——划分脚本如果没打乱就切分,可能出现训练集全是白天、验证集全是晚上的情况。

6. 从跑通到跑好:用混淆矩阵定位类别混淆与阈值调优

训练跑通只是起点,真正让模型可用的是后续的误差分析。YOLO 训练结束后会在runs/detect/train/下生成confusion_matrix.png,这张图能直接告诉你模型把哪些类别搞混了。猫狗检测里最常见的是猫被预测成狗——尤其是幼猫和幼犬,体型和毛色接近时模型确实容易懵。

看混淆矩阵时重点关注对角线以外的数值。如果cat→dog的误判数量明显高于dog→cat,说明猫的样本多样性不够,需要补充不同姿态、不同光照的猫图。如果背景被大量预测成目标(矩阵最后一行背景列数值高),说明标注里可能漏标了一些猫狗,模型把没标注的目标当成了背景,推理时又强行检测出来。

除了混淆矩阵,results.png里的 P、R、mAP 曲线也值得细看。如果 Precision 很高但 Recall 很低,说明模型只敢在特别确定的时候才输出框,这时候可以降低推理时的置信度阈值。默认conf=0.25,可以试 0.1 看能不能召回更多目标,代价是误检会增加。反过来如果 Recall 高但 Precision 低,就提高阈值到 0.4 或 0.5。

推理时还有一个iou参数控制 NMS 的合并阈值,默认 0.7。如果同一只猫被输出了好几个重叠框,把iou降到 0.5 能缓解。如果两只挨得很近的猫被合并成了一个框,就把iou提到 0.8。

yolo detect predict model=runs/detect/train/weights/best.pt source=./test_images conf=0.3 iou=0.6

我一般会在验证集上跑几组不同conf和iou的组合,把结果导出来对比。具体做法是用yolo detect val加不同参数跑,看 mAP 变化。通常conf=0.001时 mAP 最高(因为把所有可能的框都算进去了),但实际部署时要用更高的阈值来平衡误检。

从那以后我每次训练完都强制走一遍混淆矩阵和 PR 曲线的检查,不再只看一个 mAP 数字就收工。这套猫狗数据集的价值不在于 5000 张图本身,而在于它把格式转换、划分、训练、评估这条链路完整地串了起来,让你能把精力花在调参和误差分析上,而不是反复写格式转换脚本。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询