
简介面向YOLOv3猫狗检测任务的数据集取自COCO val2014验证集并经人工筛选专门用于训练和评估猫、狗两个类别的目标检测模型。压缩包约306MB内含2568张jpg图像并配套2568个xml和2569个txt标注文件分别提供边界框坐标与类别信息文件命名规范便于批量读取。已有1704人浏览学习适合正在实践YOLOv3的开发者、算法学习者用于模型训练、验证集测试及mAP指标评估。数据集同时给出XML与TXT两种标签格式可适配不同训练框架的读取方式配合DarkNet-53结构与多尺度检测机制能直观学习小目标处理、anchor box设置等关键环节。无论是入门目标检测还是调优YOLOv3都可获得真实可用的训练样本与评估基准。1. YOLO猫狗检测数据集val2014 里的 2568 个 TXT 标签能做什么拿到“YOLO猫狗检测数据集 coco-val2014-cat_dog-2568.zip”第一眼扫到的是一堆 COCO_val2014_*.txt。别急着以为里面是现成的猫狗图片它实际上是 COCO val2014 里筛出的猫狗样本对应的 YOLO 格式标签集一共 2568 个 TXT 文件。做 YOLOv3 猫狗检测时最麻烦的往往不是网络结构而是把 COCO 的 JSON 标注转成模型能直接读的归一化坐标这份资源相当于帮你省掉了转换这一步。它适合两类人一是想拿 YOLOv3 快速验证目标检测完整流程的入门者二是做数据清洗、标签格式对比的工程老手。动手训练之前至少要清楚这批标签从哪来、字段是什么、坑在哪这是下文要拆的重点。2. 标签格式与转换逻辑COCO JSON 如何变成 YOLO 的 class x y w h2.1 为什么用 val2014 做训练验证集当训练集的边界条件COCO 官方的标准做法是把 train2014 当训练集、val2014 当验证集。这个资源偏偏选 val2014 来做猫狗子集并不是因为它比 train2014 更好而是因为 val2014 的标注文件是公开的图片总量可控做小样本实验时拷贝起来也快。2568 张猫狗标签对于调整 YOLOv3 的训练管线足够但如果你要交付一个生产级模型我一般会建议再联合 train2017 的猫狗类别重新抽一份单纯靠 val2014 样本量偏小边界情况也覆盖不全。把验证集当训练集有个容易被忽略的边界条件这套标签做的是“猫狗二分类检测”不是 COCO 全类别的复现。因此用它训练出来的 YOLOv3 只会认猫和狗看到其他物体会尽量把它们当成背景。如果你后续要扩展到其他类别不能只在 cfg 里把 classes 从 2 改成 N还需要重新合并 COCO 里其他类别的标签否则训练样本里没有那些类的正样本mAP 会非常难看。理解这一点你才不会被“验证集训练”的思路带偏。从工程角度看这份 zip 的价值不在“验证集能不能训练”而在于它把 COCO 里分布分散的猫狗标注聚成了一个开箱即用的集合。省去你从几万张图里筛猫狗的时间也省去统一文件名的时间。文件名规律是COCO_val2014_12位数字ID.txt12 位数字 ID 对应 COCO 官方的 image_id这是后面匹配原图的关键。2.2 读懂一行 YOLO 标签class_id、中心点坐标和归一化宽高YOLO 的 TXT 标签每一行代表一个目标标准字段是class_id x_center y_center width height。和 COCO 标注里常见的[x, y, w, h]绝对像素坐标不同YOLO 要求所有坐标都相对图片宽高做归一化数值范围在 0 到 1 之间。举例来说一行0 0.5143 0.4235 0.2108 0.2654表示类别 ID 为 0假设 0 是猫目标中心点在图片水平方向的 51.43% 处、垂直方向的 42.35% 处目标宽度占整张图的 21.08%高度占 26.54%。想直观验证某个文件内容可以用一条命令head -5 COCO_val2014_000000161505.txt正常输出是五行以内、每行五个数字。如果出现空文件说明这张原图在 COCO 标注里没有猫狗目标或者标注在转换时被过滤掉了。注意一个标定值如果看到width或height大于 1那就是没做归一化训练时会被 YOLO 当成异常框处理轻则损失震荡重则直接产生 NaN。这种问题在手动整理标签时非常常见后面第 4 章我会专门讲怎么查。还有个容易糊涂的点COCO 的 bbox 是左上角坐标加宽高YOLO 的 x_center 和 y_center 是中心点坐标两者的换算公式是x_center (bbox_x bbox_w / 2) / img_wy_center (bbox_y bbox_h / 2) / img_hw bbox_w / img_wh bbox_h / img_h这套公式是所有 COCO 转 YOLO 脚本的底座资源的作者大概率也是按这个流程生成的标签只是中间加了一步只保留 cat 和 dog 的类别筛选。2.3 参考脚本把 COCO JSON 转换成 YOLO TXT如果你不满足于直接用这份打包好的标签想自己从 COCO 原始 JSON 重新生成下面这个脚本是常见做法。它读取instances_val2014.json筛选 cat 和 dog 两个类别输出一批以COCO_val2014_image_id.txt命名的文件理论上可以还原出和资源类似的标签集。 参考脚本把 COCO instances_val2014.json 转成 YOLO TXT 标签 资源 coco-val2014-cat_dog-2568.zip 就是按类似的流程生成的 import json import os from collections import defaultdict # 参数区按实际路径修改 ann_file instances_val2014.json out_dir labels # 输出目录存放 COCO_val2014_*.txt target_names [cat, dog] # 只保留猫和狗 os.makedirs(out_dir, exist_okTrue) with open(ann_file) as f: data json.load(f) # COCO 的 image_id 与图片宽高 image_info {img[id]: img for img in data[images]} # 建立“COCO类别名 - 0/1”的映射顺序影响训练类别 name_to_id {name: i for i, name in enumerate(target_names)} coco_cat_ids set() for cat in data[categories]: if cat[name] in target_names: coco_cat_ids.add(cat[id]) # 每个 image_id 收集若干行 YOLO 标签 samples defaultdict(list) for ann in data[annotations]: if ann[category_id] not in coco_cat_ids: continue img image_info[ann[image_id]] w, h img[width], img[height] if w 0 or h 0: continue x, y, bw, bh ann[bbox] # COCO bbox: 左上角 宽高 cx (x bw / 2) / w cy (y bh / 2) / h bw_n bw / w bh_n bh / h samples[ann[image_id]].append( f{name_to_id[ann[category_id]]} {cx:.6f} {cy:.6f} {bw_n:.6f} {bh_n:.6f} ) for image_id, lines in samples.items(): txt_name fCOCO_val2014_{image_id:012d}.txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines) \n) print(f共生成 {len(samples)} 个标签文件)脚本逻辑并不复杂但有三个参数值得解释。target_names的顺序决定了类别 ID这里 cat 是 0、dog 是 1训练时obj.names必须按同样顺序写否则检测结果会猫狗互换。ann[bbox]读出来的是绝对像素坐标必须除以img[width]和img[height]做归一化少一步都会导致标签越界。image_id:012d的格式化是为了和 COCO 官方文件名对齐图片文件名也是 12 位补零。如果只是想过滤小目标可以在循环里加一行if bw * bh min_area: continue比如最小面积 32×32 像素这样能避免让模型去学一堆不到指甲盖大小的模糊猫狗。脚本跑完labels目录里的文件数量和内容就是这类 YOLO 标签集的标准形态。3. 训练 YOLOv3 猫狗检测目录划分、cfg 修改与超参数设置3.1 目录结构与文件清单labels 之外还要补齐 images这批 zip 里只有标签文件训练 YOLOv3 前必须先把原图补齐。COCO val2014 的官方原图按COCO_val2014_12位ID.jpg命名下载后需要按标签文件名匹配。我习惯的标准目录结构是下面这样既满足 DarkNet 的路径要求也方便后续换成 YOLOv5/v8。coco-catdog/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── train.txt ├── val.txt ├── obj.names └── obj.data先把标签按 85% / 15% 划分成训练和验证两部分。可以用一个 Python 脚本完成标签复制、图片复制和train.txt/val.txt生成import os, random, shutil label_root labels # 解压后放 2568 个 txt 的目录 img_root /path/to/val2014 # COCO 官方原图目录 train_ratio 0.85 txts [f for f in os.listdir(label_root) if f.endswith(.txt)] random.shuffle(txts) split int(len(txts) * train_ratio) train_txts, val_txts txts[:split], txts[split:] for sub in [train, val]: os.makedirs(fimages/{sub}, exist_okTrue) os.makedirs(flabels/{sub}, exist_okTrue) for idx, txt in enumerate(train_txts): img_name txt.replace(.txt, .jpg) shutil.copy(os.path.join(label_root, txt), flabels/train/{txt}) src os.path.join(img_root, img_name) if os.path.exists(src): shutil.copy(src, fimages/train/{img_name}) for txt in val_txts: img_name txt.replace(.txt, .jpg) shutil.copy(os.path.join(label_root, txt), flabels/val/{txt}) src os.path.join(img_root, img_name) if os.path.exists(src): shutil.copy(src, fimages/val/{img_name}) with open(train.txt, w) as f: for txt in train_txts: f.write(os.path.abspath(fimages/train/{txt.replace(.txt, .jpg)}) \n) with open(val.txt, w) as f: for txt in val_txts: f.write(os.path.abspath(fimages/val/{txt.replace(.txt, .jpg)}) \n)这里的随机划分是常见的 85/15 经验值。如果你的数据量更小可以把train_ratio提到 0.9但不要低于 0.8否则验证集占比过大训练样本不足。random.shuffle保证猫狗混合不会出现某个子集全是猫或全是狗的情况。DarkNet 还需要两个辅助文件。obj.names内容就是类别名一行一个cat dogobj.data是路径配置文件classes 2 train train.txt valid val.txt names obj.names backup backup/backup/目录用来存训练过程中的权重记得手动建好。这套 txt 标签格式和 YOLOv5/v8 完全兼容如果你不想用 DarkNet把labels/train和images/train按 YOLOv5 要求的目录关系摆好直接写一个data.yaml就能训练这也是为什么很多人拿这份资源同时验证多个版本的 YOLO。3.2 修改 YOLOv3 的 cfgfilters、classes 和 anchor 的联动关系YOLOv3 的改进核心是引入了多尺度检测和 DarkNet-53 残差骨干。它会在三个不同尺寸的特征图上做预测每个特征图位置预测 3 个 anchor box所以一共 9 个 anchor分别对应大、中、小目标。猫和狗在图片里的尺度变化很大一只趴着的狗能占半个画面远处的猫可能只有几十个像素多尺度检测在这里确实比 YOLOv2 的单尺度更稳。修改yolov3.cfg时最基本的两处是classes和filters。每处[yolo]层的classes改成 2它前面的最后一个[convolutional]层filters改成 21。21 的由来是3 * (classes 5)即每个尺度 3 个 anchor每个 anchor 预测 4 个坐标偏移、1 个 objectness 分数和 2 个类别概率[convolutional] filters21 activationlinear ... [yolo] mask0,1,2 anchors 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 classes2 num9 jitter.3 ignore_thresh.5 truth_thresh1 random1原版yolov3.cfg是 80 类 COCO 配置里面有三处filters255和三处classes80对应三个预测尺度。只改classes不改filters是最常见的翻车操作训练时会报权重维度不匹配或者直接卡在加载阶段。用下面的命令快速定位grep -n filters255\|classes80 yolov3.cfg定位到之后手动把三个filters255改成filters21三个classes80改成classes2。anchor 尺寸暂时不用动因为原来的 COCO anchor 对大中小目标都有覆盖猫狗检测属于复用场景直接沿用 COCO 预训练权重里的 anchor 是合理选择。如果你的图片里全是近距离的大头猫狗可以考虑用 k-means 重新聚类 anchor但这是后续调优的事第一版先跑通再说。3.3 启动训练DarkNet 命令与关键超参数训练命令本身不长但前置条件有三个编译好的 darknet 可执行文件、预训练权重darknet53.conv.74、以及已经改好的 cfg。预训练权重要单独下载这个文件是 DarkNet 官方发布的 DarkNet-53 分类权重用它初始化骨干网络比从零训练收敛快得多。训练时执行./darknet detector train obj.data yolov3-catdog.cfg darknet53.conv.74 -dont_show -map-dont_show表示不弹图像窗口适合在服务器上跑-map会在训练过程中每隔一定 iteration 在验证集上计算 mAP方便你实时观察是否过拟合。obj.data里的valid val.txt就是给-map用的。超参数控制在yolov3-catdog.cfg靠前的网络层。对于 2568 张小样本数据集我一般不改默认的batch64但会把subdivisions调成 16 或 32。subdivisions把 64 张图拆成多个小批量喂给 GPU主要是为了解决显存不足的问题但拆太碎会让 BN 层的统计量不稳定。learning_rate0.001、burn_in1000是稳定起步的关键前 1000 个 iteration 学习率从很小逐渐爬升避免刚开始就震荡。max_batches可以设 8000steps6400,7200表示在 6400 和 7200 轮时把学习率缩小到原来的 0.1 倍这种阶梯下降比从头到尾一个学习率更稳。如果看到 loss 在几十的值上下剧烈抖动甚至变成 NaN优先检查subdivisions和batch。YOLOv3 的损失函数由坐标损失、置信度损失和类别损失三块组成BN 崩溃时这三块梯度会互相放大最后输出 NaN。另一个经验是不要在第一个 epoch 就开random1的数据增强先把random0跑通一遍确认 loss 掉下去之后再开多尺度训练。这样能避免“训练一晚上早上发现 loss 是 NaN白跑”的尴尬。4. 避坑与排查五个让猫狗检测翻车的真实原因4.1 坑一解压后找不到一张 JPG现象下载的是“YOLO猫狗检测数据集”解压之后全是 TXT一张猫狗图片都没有心态当场裂开。原因这个 zip 实际是 COCO val2014 里猫狗图像的 YOLO 标签集2568 个 TXT 文件对应 2568 张图。原始图片体积太大分发时只抽标签是目标检测数据集的常见做法。摘要里的“图像”指的是这些标签对应的 COCO 官方原图不包含在 zip 里。解决不要拿自己手机拍的猫狗图顶替。文件名里的数字 ID 和 COCO 原图一一对应老老实实把 val2014 原图下载下来用 3.1 节的脚本按文件名匹配。后来我拿到这类资源第一反应就是先看文件后缀一堆 txt 就默认是标签集合反而少踩很多坑。4.2 坑二训练时 loss 一直不降甚至 NaN现象DarkNet 跑起来loss 前几百个 batch 在几十上下抖动甚至直接 NaN日志刷屏。原因最常见是batch太小导致 BN 统计崩溃或者learning_rate太大。YOLOv3 的损失函数包含坐标、置信度、类别三部分训练样本里如果混入了空标签文件或超长行文本梯度也会乱跳。这跟数据集本身关系不大通常是配置文件和预处理的问题。解决batch保持 64subdivisions设成 16 或 32learning_rate从 0.001 起步burn_in设 1000。如果日志里已经出现 NaN马上停掉训练回到上一次正常的 weights 继续跑这就是深度学习的“后悔药”。如果你换成 YOLOv5/v8 训练这套标签同样要注意 batch size 别太小Ultralytics 在 batch 过小时也会报 BN 相关的错误。4.3 坑三mAP 一直是 0现象训练几十轮验证集 mAP 始终是 0但训练 loss 看起来在正常下降。原因大概率是标签里的坐标越界或者obj.data里valid路径指错或者类别 ID 与obj.names顺序对不上。YOLO 训练时对越界框的修正很玄学有时候直接忽略导致评估阶段一个正样本都匹配不上mAP 自然为零。解决先用第 5 章的体检脚本把每个 TXT 过一遍查坐标是否都在 [0,1] 内再检查val.txt里的图片路径是否存在最后确认 cfg 里classes2和filters21三处都改齐。只改一处的结果就是前向传播输出维度异常mAP 计算时类别对不上这种问题排查顺序比瞎调参重要。4.4 坑四说好的 XML 格式没找到现象摘要写着提供 XML 和 TXT 两种标签格式结果 zip 里连一个.xml都没有。原因COCO 原生标注是 JSON不是 XML。网上很多工具会把 COCO 转成 VOC XML但这份资源实际只保留了 YOLO TXT 这种更直接的格式。文件列表里清一色.txt说明发布者并没有做 XML 导出。解决直接用 TXT 训练 YOLO 就好没必要转 XML。如果下游工具非要 VOC 格式自己写 Python 把 TXT 再转回 XML但要额外做一次归一化坐标还原成像素坐标这一步容易踩单位转换的坑。记住这是资源边界问题不是 bug别在上面耗时间。4.5 坑五val.txt 图片数量比标签多很多现象把 COCO val2014 整个目录拷进 images发现图片数量是标签的好几倍训练时一堆图片没有对应标注。原因COCO val2014 总图片数接近两万张其中猫狗标签只有 2568 个。其他图片要么不含猫狗要么类别不在筛选范围。直接把整个 val2014 喂给训练器会导致大量无标注图片被当作负样本干扰训练。解决以标签文件为白名单筛选图片。一行命令生成保留清单find labels -name *.txt | sed s/\.txt$/.jpg/ | sed s|labels|images| keep.txt之后按keep.txt把对应图片复制到 images 目录。我一般直接把这个清单当成 train/val 的图片池避免多余图片混进来。这个坑在 COCO 子集类资源里特别普遍拿到手先对数量再谈训练。5. 一天跑通验证把 TXT 标签画出来并算一次 mAP拿到数据集别急着训练先做一次标签体检。把 2568 个 TXT 全部扫描一遍检查坐标是否越界、字段数量是否合法这一步能省下大量排错时间。下面这个脚本是我每次拿到新数据集的固定动作import os def check_label(path): bad [] with open(path) as f: for line in f: parts line.split() if len(parts) ! 5: bad.append(f{path}: 字段数不对 - {line.strip()}) continue cid, cx, cy, w, h parts[0], float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): bad.append(f{path}: 坐标越界 - {line.strip()}) return bad for label in os.listdir(labels): errs check_label(os.path.join(labels, label)) if errs: print(\n.join(errs))如果脚本没有任何输出说明标签格式基本正常。接下来画几个框确认目标位置对不对最直观的方式是写一个大纲框绘制脚本把cx, cy, w, h换回像素坐标画在图上这一步能看到标签和真实猫狗是否对齐。注意w和h必须大于 0一张图上如果出现负数宽高通常是从 COCO 转出时 bbox 解析出错。验证训练结果用 DarkNet 自带的map命令./darknet detector map obj.data yolov3-catdog.cfg backup/yolov3-catdog_last.weights -thresh 0.25这里-thresh是置信度阈值0.25 是检测任务里比较常见的经验值。命令跑完会输出当前权重在 val 集上的 mAP0.5也就是 IoU 阈值取 0.5 时的平均精度。COCO 官方那套 mAP 是把 IoU 从 0.5 到 0.95 每隔 0.05 算一个平均值DarkNet 的map默认只算 0.5如果你想复现 COCO 指标需要把预测结果转成 COCO 格式再用 pycocotools 算。但对于快速判断“模型有没有学会猫狗”看 mAP0.5 就够了。跑通这个流程之后再回头处理 2568 个标签之外的问题比如类别不均衡、小目标漏检、anchor 尺寸不合适都会更有底气。从那以后我每次拿到新数据集都先做一次标签体检强制走一遍坐标边界检查和样本数核对。这个习惯省下的时间远比训练本身多希望帮到你。本文还有配套的精品资源点击获取