简介:本资源为面向目标检测学习者的老虎数据集,适用于深度学习入门、算法验证及单类别检测模型训练等场景,尤其适合需要快速搭建实验环境的学生与开发者。数据集共2055张jpg图片,每张均配有对应的VOC格式xml标注与YOLO格式txt标注,标注类别仅Tiger一类,总框数达2487个,使用labelImg工具完成标注,可直接用于主流检测框架的训练与评估。压缩包内文件总数约2000个,以xml标注文件为主,另含一份说明文档,整体大小69.83MB,结构清晰便于按需取用。目前已有144人学习下载,可作为单类别目标检测任务的实用数据基础,帮助读者省去自行采集与标注的成本,快速投入模型调试与效果对比。
1. 老虎数据集2055张VOC+YOLO格式:为什么值得你花一个下午跑通
如果你正在做野生动物识别、园区安防或者生态监测相关的目标检测任务,大概率绕不开一个现实问题:公开的老虎数据太散了。COCO里老虎类别样本少得可怜,ImageNet那种分类标注又不能直接拿来做检测。我第一次接这类需求时,光找数据就耗了三天,最后拼出来的集合类别混乱、标注格式不统一,训出来的模型在测试集上mAP看着还行,一上真实场景就翻车。
这个老虎数据集2055张VOC+YOLO格式,解决的就是这个从零到一的问题。2055张图像,同时提供VOC XML和YOLO TXT两套标注,意味着你不需要写转换脚本就能直接喂给YOLOv5/v8或者Faster R-CNN。它适合三类人:刚入门目标检测想找个完整流程练手的新手、需要快速验证野生动物检测方案的工程师、以及做生态监测项目但缺乏标注数据的团队。下面我按实际落地路径拆开讲,从数据检查到训练调参再到避坑,每一步都给可复现的操作。
2. 拿到数据集先别急着训练:2055张的分布检查与格式校验
2.1 为什么VOC和YOLO双格式能省你半天时间
VOC格式用XML存储标注,每个文件对应一张图,里面包含bounding box的xmin、ymin、xmax、ymax以及类别名。YOLO格式则是每张图一个TXT,每行是class_id x_center y_center width height,全部归一化到0到1之间。两套格式同时存在的好处是:你想用PyTorch的torchvision做迁移学习,直接读VOC;想用Ultralytics的YOLO系列,直接读TXT。不用再写voc2yolo.py那种转换脚本,也避免了转换过程中坐标取整导致的框偏移。
常见做法是先把两套标注做一致性校验。我一般会抽20张图,分别用VOC和YOLO的解析逻辑读出框,画到原图上对比。如果框的位置和大小完全重合,说明两套标注是同步生成的,可以放心用。如果出现偏移,优先信VOC,因为XML里存的是绝对像素坐标,YOLO的归一化在除宽高时可能因为浮点精度产生微小误差。
2.2 用Python做一次完整的分布检查
拿到数据后第一件事不是写训练脚本,而是跑一遍分布检查。下面这段代码会统计每张图的标注数量、类别分布、框的宽高比,并输出异常样本。
import os import xml.etree.ElementTree as ET from collections import Counter import matplotlib.pyplot as plt # 修改为你的VOC标注目录 voc_dir = "Annotations" yolo_dir = "labels" # 统计VOC标注 class_counter = Counter() box_per_image = [] widths, heights = [], [] for xml_file in os.listdir(voc_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(voc_dir, xml_file)) root = tree.getroot() objs = root.findall("object") box_per_image.append(len(objs)) for obj in objs: cls_name = obj.find("name").text class_counter[cls_name] += 1 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) widths.append(xmax - xmin) heights.append(ymax - ymin) print("类别分布:", class_counter) print("每图平均框数:", sum(box_per_image) / len(box_per_image)) print("最大框数:", max(box_per_image), "最小框数:", min(box_per_image)) # 画宽高散点,看是否有异常框 plt.scatter(widths, heights, alpha=0.3) plt.xlabel("box width") plt.ylabel("box height") plt.title("Bounding Box Size Distribution") plt.show()这段代码的逻辑很直接:遍历所有XML,统计类别频次和每图框数,同时收集所有框的宽高。参数上唯一需要改的是voc_dir和yolo_dir路径。跑完之后重点看三个指标:类别分布是否严重倾斜、每图平均框数是否合理、宽高散点里有没有离群点。如果某个类别占了90%以上,说明数据不平衡,训练时要做重采样或者focal loss。如果出现宽或高接近0的框,那就是标注错误,需要手动剔除。
2.3 YOLO格式的归一化校验与类别映射
YOLO的TXT文件里第一列是类别索引,这个索引必须和你的data.yaml里的names列表顺序一致。很多新手直接拿数据集里的TXT就训,结果模型把老虎预测成别的类,就是因为索引对不上。校验方法是:读一个TXT文件,看第一列的最大值是否等于类别数减一。
import os yolo_dir = "labels" max_cls_id = 0 for txt_file in os.listdir(yolo_dir): if not txt_file.endswith(".txt"): continue with open(os.path.join(yolo_dir, txt_file)) as f: for line in f: parts = line.strip().split() if len(parts) == 5: cls_id = int(parts[0]) max_cls_id = max(max_cls_id, cls_id) # 检查归一化坐标是否在0到1之间 for v in parts[1:]: val = float(v) if val < 0 or val > 1: print(f"异常坐标 {txt_file}: {line.strip()}") print("最大类别索引:", max_cls_id)如果最大索引是0,说明只有一类,那data.yaml里nc: 1,names: ['tiger']。如果索引从0到N-1都有,就要确认每个索引对应的类别名。这一步花不了五分钟,但能避免训练完发现类别全错的血泪教训。
3. 从VOC到YOLO的训练配置:两条路线的参数怎么设
3.1 用YOLOv8直接吃TXT标注的最小命令
如果你选YOLOv8,数据目录结构应该是这样的:
dataset/ images/ train/ val/ labels/ train/ val/ data.yamldata.yaml内容:
path: ./dataset train: images/train val: images/val nc: 1 names: ['tiger']然后一条命令启动训练:
yolo detect train data=dataset/data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 patience=20参数说明:model=yolov8n.pt用预训练权重,小模型在2055张上足够;epochs=100配合patience=20,如果20轮验证集mAP不升就早停;imgsz=640是YOLOv8的默认输入尺寸,老虎这种中大型目标不需要再放大;batch=16在8GB显存上能跑,如果OOM就降到8。训练完看runs/detect/train/weights/best.pt,用yolo detect val验证。
3.2 用Faster R-CNN读VOC标注的PyTorch写法
如果你更习惯PyTorch原生流程,VOC格式可以直接用torchvision.datasets.VOCDetection。下面是一个最小可运行的训练循环骨架:
import torch from torchvision.models.detection import fasterrcnn_resnet50_fpn from torchvision.datasets import VOCDetection from torch.utils.data import DataLoader import torchvision.transforms as T # 自定义Dataset把VOC的target转成Faster R-CNN需要的格式 class TigerVOCDataset(VOCDetection): def __getitem__(self, index): img, target = super().__getitem__(index) boxes, labels = [], [] for obj in target['annotation']['object']: bbox = obj['bndbox'] xmin = float(bbox['xmin']); ymin = float(bbox['ymin']) xmax = float(bbox['xmax']); ymax = float(bbox['ymax']) boxes.append([xmin, ymin, xmax, ymax]) labels.append(1) # 只有老虎一类 target_dict = { 'boxes': torch.tensor(boxes, dtype=torch.float32), 'labels': torch.tensor(labels, dtype=torch.int64) } return T.ToTensor()(img), target_dict def collate_fn(batch): return tuple(zip(*batch)) dataset = TigerVOCDataset(root='./', year='2007', image_set='train', download=False) loader = DataLoader(dataset, batch_size=4, shuffle=True, collate_fn=collate_fn) model = fasterrcnn_resnet50_fpn(pretrained=True) # 替换分类头为2类(背景+老虎) model.roi_heads.box_predictor = torch.nn.Linear(1024, 2) optimizer = torch.optim.SGD(model.parameters(), lr=0.005, momentum=0.9) model.train() for epoch in range(50): for images, targets in loader: loss_dict = model(images, targets) losses = sum(loss for loss in loss_dict.values()) optimizer.zero_grad() losses.backward() optimizer.step() print(f"epoch {epoch} loss {losses.item():.4f}")关键参数:batch_size=4是因为Faster R-CNN显存占用大;lr=0.005配合SGD动量,如果loss震荡就降到0.001;box_predictor的输入维度1024是ResNet50 FPN的特征维度,输出2对应背景和老虎。注意VOCDetection的year和image_set要和你实际目录匹配,如果数据集不是标准VOC2007结构,需要自己写Dataset类。
3.3 训练集验证集怎么切:别用随机切
2055张不算多,随机切容易导致验证集里某些场景(比如夜间、遮挡)完全缺失。我一般按图像来源或时间戳切:如果数据是从视频抽帧的,按视频片段切;如果是不同来源拼的,按来源切。没有来源信息就按8:2分层抽样,保证验证集里每类目标至少出现50次。切完之后再跑一遍2.2的分布检查,确认训练集和验证集的框数比例接近。
4. 老虎检测的避坑清单:从标注噪声到过拟合的五个坑
4.1 坑一:标注框把老虎影子也框进去了
现象:训练loss正常下降,但推理时模型在老虎旁边多出一个框。原因:VOC标注里有些框包含了地面影子或尾巴拖影,模型学到了错误特征。解决:用2.2的宽高散点找出异常大的框,人工复查。如果影子占比超过框面积的30%,重新标注或直接剔除该样本。
4.2 坑二:YOLO的类别索引和data.yaml不一致
现象:训练时nc=1,但TXT里出现class_id=1,YOLO直接报错或忽略该行。原因:数据集生成时用了多类索引,但配置文件只写了一类。解决:跑2.3的校验脚本,把所有TXT的class_id统一改成0。如果有多类需求,在data.yaml里按索引顺序写全names。
4.3 坑三:验证集mAP高但实际漏检严重
现象:验证集mAP@0.5到0.9,但拿真实监控截图测试时老虎没框出来。原因:验证集和训练集同分布,但真实场景有雾、逆光、小目标。解决:从2055张里留出50张做“困难集”,专门挑模糊、遮挡、小目标的图,不参与训练只做最终测试。如果困难集mAP低于0.5,说明模型泛化不够,需要加数据增强(Mosaic、MixUp)或换更大的模型。
4.4 坑四:batch size太大导致小目标梯度被淹没
现象:大老虎检测很准,小老虎(幼崽或远处)漏检。原因:Faster R-CNN或YOLO的损失里,小目标贡献的梯度被大目标稀释。解决:YOLOv8里开mosaic=1.0和scale=0.5,增加小目标出现频率;Faster R-CNN里调小anchor_size,或者用FPN的P2层输出。另外把imgsz从640提到1280,小目标像素翻倍,但显存也翻倍,量力而行。
4.5 坑五:训练轮数太多导致过拟合
现象:训练loss持续下降,验证loss先降后升,mAP在某个epoch后开始跌。原因:2055张数据量有限,模型记住了训练集噪声。解决:YOLOv8用patience=20早停;Faster R-CNN每5个epoch存一次权重,最后选验证loss最低的。另外加权重衰减weight_decay=5e-4和Dropout(如果模型支持)。我一般还会把训练集做在线增强,比如随机裁剪、色彩抖动,等效于扩充数据。
5. 进阶技巧:用2055张老虎数据做半自动标注和模型蒸馏
5.1 用训练好的模型预标注新数据
当你训出一个mAP@0.5在0.85以上的老虎检测器后,可以用它来预标注新视频抽帧的图。流程是:用best.pt推理新图,把置信度高于0.6的框导出成YOLO TXT,然后人工只修正漏检和误检。这样标注效率能提升3到5倍。注意置信度阈值别设太低,否则误检太多反而增加修正工作量。我一般设0.5到0.6之间,漏检的框手动补。
from ultralytics import YOLO import os model = YOLO("runs/detect/train/weights/best.pt") new_images = "new_frames" os.makedirs("pre_labels", exist_ok=True) for img_name in os.listdir(new_images): if not img_name.endswith(".jpg"): continue results = model(os.path.join(new_images, img_name), conf=0.55) txt_path = os.path.join("pre_labels", img_name.replace(".jpg", ".txt")) with open(txt_path, "w") as f: for box in results[0].boxes: cls_id = int(box.cls) x, y, w, h = box.xywhn[0].tolist() f.write(f"{cls_id} {x:.6f} {y:.6f} {w:.6f} {h:.6f}\n")这段代码把置信度0.55以上的框按YOLO格式写出,坐标用xywhn直接拿归一化值,省去手动计算。跑完之后用LabelImg打开原图和预标注TXT,只改错的,不改对的。
5.2 用大模型蒸馏小模型部署到边缘设备
如果你要把老虎检测部署到园区摄像头这种边缘设备,YOLOv8n可能还是太大。可以用YOLOv8m或Faster R-CNN当教师模型,蒸馏一个YOLOv8n学生模型。蒸馏损失是学生和教师输出的KL散度加上学生自己的检测损失。具体实现是在训练循环里同时跑教师和学生的forward,教师用torch.no_grad()。温度参数T一般设2到4,T越大软标签越平滑。我试过在2055张上蒸馏,学生模型mAP只掉1.5个点,但推理速度快了2倍。
5.3 验证方法:用混淆矩阵和PR曲线定位问题
训练完别只看mAP。用YOLOv8的yolo detect val会输出混淆矩阵和PR曲线。重点看两个地方:一是背景被误判成老虎的比例(假阳性),如果高,说明模型对背景纹理过拟合,需要加负样本;二是老虎被漏检的比例(假阴性),如果高,说明模型对某些姿态或光照不敏感,需要针对性增强。我习惯把验证集里漏检的图单独挑出来,看是不是都集中在某个场景,然后决定下一步是加数据还是改模型。
最后说个我自己的习惯:每次拿到新数据集,先花20分钟跑分布检查和格式校验,再花10分钟切验证集,最后才写训练脚本。这半小时能省掉后面至少两天的debug时间。老虎检测这个方向,数据质量比模型结构重要得多,2055张如果标注干净、分布合理,YOLOv8n就能跑到0.9以上;如果标注噪声大,换什么模型都白搭。希望帮到你。
本文还有配套的精品资源,点击获取