资讯动态

110张熊猫图双格式数据集:VOC与YOLO标注解析及YOLOv8训练实战

发布时间:2026/10/1 17:24:24 来源:尧图企业网站定制
简介这是一份面向目标检测初学者与算法验证人员的熊猫单类别数据集采用Pascal VOC与YOLO双格式标注可直接用于YOLO、Faster R-CNN等主流框架的训练与测试省去格式转换的繁琐步骤。压缩包共332个文件包含110张jpg原图、110个VOC格式xml标注文件以及110个yolo格式txt标注文件另有少量说明性文本整体约36.34MB体积轻便便于快速下载与本地部署。所有图片均由labelImg工具人工画框标注类别统一为Panda共114个标注框标注准确合理适合小样本实验、模型微调与教学演示。目前已有189人学习下载可作为入门目标检测的练手素材帮助读者快速跑通数据加载、训练与推理流程验证算法在单类别场景下的表现。1. 110 张熊猫图、114 个框这份双格式数据集到底能拿来干什么如果你正在找一个体量小、标注干净、拿来就能跑通训练流程的动物检测数据集这份 110 张熊猫图片的包值得先看一眼。它同时给了 Pascal VOC 的 xml 和 YOLO 的 txt 两套标注类别只有一个Panda总框数 114。也就是说平均每张图 1 个出头的目标画面里基本是单只或少量熊猫背景干扰不算复杂。这种规模的数据集指望它训出一个能上生产的高精度模型不现实但它的价值在别处验证你的训练管线通不通、类别映射对不对、数据增强有没有把框带偏、评估指标能不能正常收敛。很多人第一次跑 YOLOv8 训练自己的数据集翻车往往不是模型问题而是标注路径、类别索引、图片和标签对不上这些琐事。这份包正好能当一块干净的试金石。适合谁刚接触目标检测、想跑通端到端流程的新手需要快速搭一个熊猫检测 demo 做演示的开发者以及想拿小数据集做数据增强、过拟合实验、标注格式转换练手的人。下面从格式结构讲到训练落地再讲我踩过的坑。2. 拆开压缩包VOC 与 YOLO 双格式的目录结构和字段含义2.1 两套标注的物理组织方式拿到包先别急着写训练脚本把目录结构看清楚能省掉后面一半的调试时间。这类双格式数据集常见的组织是图片放一个目录VOC 的 xml 放一个目录YOLO 的 txt 放一个目录三者文件名主干一致只是扩展名不同。图片命名形如firc_Panda_40.jpg对应的标注就是firc_Panda_40.xml和firc_Panda_40.txt。VOC 的 xml 是自描述结构打开能看到folder、filename、size宽高通道、object节点每个 object 里有name、pose、truncated、difficult和bndboxxmin、ymin、xmax、ymax。YOLO 的 txt 则是纯数值每行一个目标格式是class_id x_center y_center width height后四个都是相对图片宽高的归一化值范围 0 到 1。这里有个容易忽略的点VOC 的框是绝对像素坐标YOLO 是归一化中心点坐标两者换算时如果图片尺寸读错框会整体偏移。xml 里的size节点就是用来做这个换算的别用 PIL 重新读一遍图片尺寸去替代除非你确认两者一致。2.2 类别索引与框数核对这个数据集只有一个类别 PandaVOC 里name字段全是PandaYOLO 里 class_id 全是 0。单类别的好处是不用纠结类别映射表坏处是很多人会忘记写data.yaml里的names导致训练时类别名显示成数字。框数核对是上手第一步。110 张图对应 110 个 xml、110 个 txt总框数 114说明有 4 张图里不止一个目标。写个脚本统计一下每张图的框数分布能提前发现有没有空标注文件或者异常多的框。import os import xml.etree.ElementTree as ET xml_dir annotations # VOC xml 所在目录 box_counts {} total 0 for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, fname)) root tree.getroot() objs root.findall(object) box_counts[fname] len(objs) total len(objs) print(图片数:, len(box_counts)) print(总框数:, total) # 打印框数大于 1 的文件确认多目标图 for k, v in box_counts.items(): if v 1: print(k, v)这段脚本做两件事统计总框数是否等于 114以及找出多目标图。如果统计出来对不上说明有 xml 解析失败或者文件缺失先解决再往下走。参数上xml_dir换成你解压后的实际路径即可ET.parse对格式错误的 xml 会直接抛异常正好能帮你定位坏文件。2.3 为什么同时保留两套格式VOC 格式生态老labelImg 默认就导出它很多传统检测框架和可视化工具吃这套YOLO 格式轻量训练时读取快YOLOv5/v8 系列直接认。数据集同时给两套等于把格式转换这一步替你做了。但要注意两套标注必须指向同一批图片、同一套框如果转换脚本有 bug可能出现 xml 和 txt 框位置不一致的情况。上手时抽几张图把两边的框画出来叠一起看是最省事的验证方式。3. 从零跑通 YOLOv8 训练data.yaml 配置与目录改造3.1 把数据集整理成 YOLO 训练目录YOLO 官方推荐的结构是 images 和 labels 两个大目录各自下面再分 train 和 val。原始包通常不是这个结构需要自己拆。110 张图按 8:2 分训练 88 张、验证 22 张比较合理小数据集别再切 test否则验证集太小指标抖动大。# 假设解压后图片在 images/yolo txt 在 labels/ mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val # 用 python 做随机划分更可控这里给 bash 思路 # 实际建议用脚本按文件名主干配对避免图片和标签错位配对这件事必须按文件名主干来不能图片和标签各自随机打乱否则图和框就对不上了。常见做法是列出所有 jpg取主干检查同名 txt 是否存在存在才纳入划分列表然后对列表做 shuffle 再切分。import os, random, shutil img_dir images lbl_dir labels out_img dataset/images out_lbl dataset/labels random.seed(42) pairs [] for f in os.listdir(img_dir): if f.endswith(.jpg): stem os.path.splitext(f)[0] if os.path.exists(os.path.join(lbl_dir, stem .txt)): pairs.append(stem) random.shuffle(pairs) split int(len(pairs) * 0.8) train, val pairs[:split], pairs[split:] for subset, items in [(train, train), (val, val)]: os.makedirs(f{out_img}/{subset}, exist_okTrue) os.makedirs(f{out_lbl}/{subset}, exist_okTrue) for stem in items: shutil.copy(f{img_dir}/{stem}.jpg, f{out_img}/{subset}/{stem}.jpg) shutil.copy(f{lbl_dir}/{stem}.txt, f{out_lbl}/{subset}/{stem}.txt) print(train:, len(train), val:, len(val))random.seed(42)固定随机种子保证每次划分一致方便复现。先配对再打乱是关键直接对图片列表 shuffle 而标签单独处理是新手最常见的错位来源。跑完打印 train 和 val 数量加起来应该等于 110。3.2 data.yaml 的字段与路径写法YOLOv8 训练靠一个 yaml 描述数据位置和类别。字段不多但路径写错是最常见的报错来源。path: /abs/path/to/dataset train: images/train val: images/val nc: 1 names: [Panda]path是数据集根目录建议写绝对路径相对路径在不同工作目录下启动训练时容易找不到。train和val相对path解析指向图片目录YOLO 会自动把images替换成labels去找同名 txt。nc是类别数这里是 1。names是类别名列表顺序必须和 txt 里的 class_id 对应Panda 是 0 就放第一个。提示如果训练时报 “No labels found”九成是 labels 目录结构和 images 不镜像或者 txt 文件名和图片主干不一致。先手动确认dataset/labels/train/下有对应 txt。3.3 启动训练与关键参数环境装好后一条命令就能起训。小数据集不需要大模型yolov8n.pt足够训练轮数可以设大一点观察过拟合。yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch8 \ lr00.01 \ patience20 \ projectruns/panda \ nameexp1imgsz640是输入分辨率小数据集不建议开太大显存吃紧就降到 416。batch8在 110 张图上够用显存小可以降到 4。lr00.01是初始学习率YOLOv8 默认值小数据集如果 loss 震荡可以降到 0.005。patience20表示 20 轮验证指标不提升就早停避免在小数据上白跑。project和name决定权重和日志输出位置。训练过程中重点看mAP50和mAP50-95两个指标。110 张图、单类别正常收敛后 mAP50 能到较高水平但如果验证集只有 22 张指标波动会比较大别因为某一轮掉了就慌。真正要警惕的是 loss 一直不降那通常是标注或路径问题不是模型问题。4. 避坑与排查小数据集训练最容易翻车的五个地方4.1 图片和标签错位模型学了个寂寞现象训练 loss 能降但验证时框位置完全不对或者 mAP 一直很低。 原因划分数据时图片和标签分别打乱或者复制时只复制了图片没复制对应标签导致图和框对不上。 解决按文件名主干配对后再统一 shuffle复制时成对操作。训练前抽 3 到 5 张图用脚本把 YOLO 框画回图片上肉眼确认这一步花两分钟能省几小时排查。4.2 归一化坐标算错框跑到图片外现象可视化时框大面积超出图片边界或者全部挤在左上角。 原因VOC 转 YOLO 时用了错误的图片宽高或者把绝对坐标当成了归一化坐标直接写进 txt。 解决转换时严格用 xml 里size节点的 width 和 height 做分母中心点坐标是(xminxmax)/2/width宽高是(xmax-xmin)/width。转换完写个校验脚本检查所有值是否落在 0 到 1 之间超出就报错。4.3 类别索引从 1 开始训练直接报错现象训练启动时报 “Label class x exceeds nc” 或类似越界错误。 原因有些标注工具或转换脚本把类别从 1 开始编号而 YOLO 要求 class_id 从 0 开始。 解决检查 txt 第一列的最大值单类别数据集应该全是 0。如果发现 1批量减 1 即可。写个脚本扫一遍所有 txt 的第一列确认取值范围。4.4 验证集太小指标忽高忽低现象mAP 在相邻两轮之间大幅跳动一会儿 0.9 一会儿 0.6。 原因110 张图切 8:2 后验证集只有 22 张单张图的预测好坏对整体指标影响很大。 解决要么增大验证集比例到 7:3要么用交叉验证的思路多切几次看平均表现。小数据集的指标本来就不稳别拿单次结果下结论重点看趋势是否收敛。4.5 过拟合来得比想象中快现象训练集 loss 持续下降验证集 loss 先降后升mAP 到某个点后不再提升。 原因110 张图对检测模型来说太少模型很快记住训练样本。 解决开数据增强YOLOv8 默认带 mosaic、翻转、缩放可以再加degrees、translate等参数。同时用早停patience控制别硬跑到几百轮。如果只是验证流程过拟合本身不影响你确认管线是否跑通。5. 进阶玩法用这 110 张图验证增强策略与格式互转小数据集真正的价值不在训出多强的模型而在当试验台。我一般会拿它做两件事验证数据增强有没有把框带偏以及练手 VOC 和 YOLO 的互转。先说增强验证。YOLOv8 训练时会把增强后的图存到runs/.../train_batch*.jpg直接打开看框有没有跟着目标走。如果发现翻转后框没翻、缩放后框偏移说明增强管线有问题。这一步用 110 张图跑几十轮就能看出来比在大数据集上浪费算力划算得多。再说格式互转。虽然这个包两套格式都给了但实际项目里经常遇到只有 VOC 或只有 YOLO 的情况转换脚本得自己会写。下面是把 VOC 转 YOLO 的核心逻辑反过来转就是逆运算。import os import xml.etree.ElementTree as ET classes [Panda] xml_dir annotations out_dir labels_yolo os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, fname)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): cls obj.find(name).text if cls not in classes: continue cid classes.index(cls) bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) xc (xmin xmax) / 2 / w yc (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cid} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) stem os.path.splitext(fname)[0] with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines))classes列表的顺序决定 class_id必须和训练时的names一致。坐标保留 6 位小数足够YOLO 读取时会再解析成浮点。转换完拿几张图把新 txt 画出来和原 xml 对比确认框重合这是最直接的验证。检查项合格标准常见异常图片与标签配对主干名一一对应有图无标签坐标范围全部在 0 到 1 之间出现负数或大于 1类别索引单类别全为 0出现 1 或其他值框数统计与 xml 一致转换丢框最后说个习惯。从那以后我每次拿到新数据集不管多小都强制先跑一遍「配对检查 坐标范围检查 抽图可视化」这三步再动训练脚本。这三步加起来不到五分钟但能挡掉后面绝大多数玄学问题。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑