ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

导盲犬拐杖检测数据集VOC+YOLO双格式4635张:YOLOv8训练与小目标避坑指南

导盲犬拐杖检测数据集VOC+YOLO双格式4635张:YOLOv8训练与小目标避坑指南 简介本数据集面向计算机视觉开发者与目标检测学习者聚焦导盲犬与盲杖两类目标的识别任务可用于辅助出行场景下的智能感知模型训练与算法验证。资源同时提供Pascal VOC与YOLO两种标注格式包含4635张jpg图片及一一对应的xml与txt标注文件标注类别为guide dog与whiteCane框数分别为1620与4430总框数6050均使用labelImg按矩形框规则标注。压缩包为7z格式共2000个文件以1999个xml标注文件和1个说明txt为主整体约286.53MB目录结构便于直接接入常见检测框架。需注意部分图片存在增强样本且若干场景由视频截取、画面连续使用前建议仔细核查。已有90人学习关注适合作为导盲犬与盲杖检测课题的标注数据参考。1. 导盲犬拐杖检测数据集4635 张 VOCYOLO 双格式到底能做什么导盲犬拐杖检测数据集 VOCYOLO 格式 4635 张 2 类别这个标题里其实藏着三个关键信息数据规模、标注格式、目标类别数。如果你正在做辅助视障人群的智能硬件或者想拿一个真实场景的小目标数据集练手 YOLO 训练这个数据集值得认真看一眼。4635 张图像、2 个类别、同时提供 VOC 和 YOLO 两种标注格式意味着你不需要自己从零标注也不用纠结格式转换脚本写没写对——拿到手就能直接进训练流程。但这里有个容易被忽略的点2 类别具体是什么从标题「导盲犬拐杖检测」推断大概率是「导盲犬」和「拐杖」两类目标。这个组合在真实场景里并不简单——导盲犬和拐杖经常同时出现、相互遮挡拐杖本身细长、像素占比小属于典型的小目标检测难题。4635 张的体量不算大但对于验证一个垂直场景的检测方案是否可行已经够用了。这篇文章面向的是想快速跑通训练、拿到可量化指标的从业者。我会从数据集结构讲起说清 VOC 和 YOLO 两种格式的差异与转换逻辑然后给出 YOLOv8 训练的完整命令和参数配置最后把我在小目标检测上踩过的坑一条条列出来。新手能照着步骤复现熟手可以直接跳到参数调优和避坑部分。2. 拆开这个数据集VOC 与 YOLO 双格式的结构与转换逻辑2.1 VOC 格式的目录结构与标注文件长什么样VOC 格式是目标检测领域的老牌标准目录结构通常是这样的dataset/ ├── JPEGImages/ # 所有图像文件 │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... ├── Annotations/ # 对应的 XML 标注文件 │ ├── 000001.xml │ ├── 000002.xml │ └── ... ├── ImageSets/ │ └── Main/ │ ├── train.txt # 训练集图像 ID 列表 │ ├── val.txt # 验证集图像 ID 列表 │ └── trainval.txt # 训练验证集 └── SegmentationClass/ # 语义分割标注检测任务用不到每个 XML 文件对应一张图核心字段是object里的类别名和bndbox里的边界框坐标。坐标用xmin, ymin, xmax, ymax表示原点在图像左上角单位是像素。这里有个血泪经验VOC 的坐标是包含边界的有些标注工具会写出xmax width的情况转换时要检查是否越界。annotation folderJPEGImages/folder filename000001.jpg/filename size width640/width height480/height depth3/depth /size object namedog/name bndbox xmin112/xmin ymin96/ymin xmax384/xmax ymax420/ymax /bndbox /object object namecane/name bndbox xmin400/xmin ymin200/ymin xmax420/xmax ymax460/ymax /bndbox /object /annotation上面这个 XML 里dog的框是 272×324 像素cane的框只有 20×260 像素。拐杖的宽度只有 20 像素在 640 宽度的图像里占比约 3%这就是典型的小目标。训练时如果输入分辨率降到 416拐杖的宽度只剩 13 像素特征提取网络很容易把它当噪声滤掉。2.2 YOLO 格式的归一化坐标与类别索引YOLO 格式的标注是每张图一个.txt文件每行一个目标格式为class_id x_center y_center width height所有坐标都归一化到 0~1 之间x_center和y_center是框中心点相对于图像宽高的比例width和height是框宽高相对于图像宽高的比例。类别用从 0 开始的整数索引表示需要一个classes.txt或data.yaml来定义索引到类别名的映射。# VOC XML 转 YOLO TXT 的核心逻辑 import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, img_width, img_height, class_map): tree ET.parse(xml_path) root tree.getroot() 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 / img_width y_center (ymin ymax) / 2.0 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height # 边界裁剪防止越界 x_center max(0, min(1, x_center)) y_center max(0, min(1, y_center)) w max(0, min(1, w)) h max(0, min(1, h)) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines class_map {dog: 0, cane: 1}这段代码的关键在最后四行的边界裁剪。我见过不少转换脚本直接除完就写文件结果遇到标注框超出图像边界的样本归一化坐标出现负数或大于 1 的值YOLO 训练时直接报错或者产生诡异的损失曲线。加上max(0, min(1, ...))这层保护能省掉很多排查时间。2.3 两种格式的选型理由与转换时机VOC 格式的优势在于通用性强很多标注工具默认导出 VOC而且 XML 结构清晰、可读性好适合人工检查和修改。YOLO 格式的优势在于训练时读取效率高一个 txt 文件几行就搞定不需要解析 XML 树。实际工作中我一般这样处理拿到 VOC 格式的数据集后先写脚本转成 YOLO 格式然后用 YOLO 训练。转换脚本跑完后必须做两件事——检查类别分布是否均衡检查有没有空标注文件。空标注文件在 YOLO 训练时会被当成负样本如果数量太多会拉低召回率。# 统计每个类别的标注数量 cat labels/*.txt | awk {print $1} | sort | uniq -c # 输出示例 # 3200 0 (dog) # 2850 1 (cane) # 找出空标注文件 find labels/ -name *.txt -empty | wc -l如果发现某个类别数量明显偏少比如拐杖只有几百个标注就需要考虑数据增强或者调整损失函数的类别权重。4635 张图里如果拐杖标注数量低于 1500训练时模型会倾向于把拐杖漏检因为背景样本远多于正样本。3. 用 YOLOv8 跑通训练从环境配置到第一轮收敛3.1 环境搭建与依赖安装的版本选择YOLOv8 对环境的要求不算苛刻但版本不匹配会导致一些玄学问题。我一般用 Python 3.9 或 3.10PyTorch 选 2.0 以上的稳定版CUDA 版本根据显卡驱动来定。如果你用的是 V100 或者消费级显卡CUDA 11.8 是比较稳妥的选择。# 创建虚拟环境 conda create -n yolo_train python3.10 -y conda activate yolo_train # 安装 PyTorch以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 ultralytics pip install ultralytics # 验证安装 yolo checksyolo checks会输出当前环境的信息包括 PyTorch 版本、CUDA 是否可用、GPU 型号。如果 CUDA 显示不可用先检查驱动版本和 CUDA 版本是否匹配不要急着装一堆乱七八糟的包。我见过有人因为 CUDA 不可用重装了三次系统最后发现只是驱动版本低了。3.2 data.yaml 的字段含义与路径配置YOLOv8 训练需要一个data.yaml文件来指定数据集路径和类别信息。这个文件看着简单但路径写错是新手翻车最多的地方。# data.yaml path: /home/user/datasets/guide_dog_cane # 数据集根目录 train: images/train # 训练集图像目录相对 path val: images/val # 验证集图像目录相对 path test: images/test # 测试集图像目录可选 nc: 2 # 类别数 names: # 类别名列表索引从 0 开始 0: dog 1: canepath写绝对路径train和val写相对于path的路径。YOLOv8 会自动把images替换成labels来找标注文件所以目录结构必须是guide_dog_cane/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/如果你的标注文件和图像文件不在对应的labels目录下训练时会报「No labels found」然后直接退出。这个报错信息很明确但如果你没注意到images和labels的对应关系可能会在路径上绕很久。3.3 训练命令与关键参数设置YOLOv8 的训练命令很简洁但参数设置直接决定模型能不能收敛、收敛得好不好。yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience30 \ device0 \ workers8 \ projectruns/train \ nameguide_dog_cane_v1 \ pretrainedTrue \ optimizerSGD \ cos_lrTrue \ close_mosaic10逐条说明这些参数的含义和调整逻辑modelyolov8s.pt选的是 small 版本参数量约 11M在 4635 张的数据集上不容易过拟合。如果你用yolov8n.ptnano 版训练速度快但小目标检测能力弱用yolov8m.pt或yolov8l.pt参数量上去了但 4635 张图可能喂不饱容易过拟合。我一般先用 s 版跑一轮看指标不够再换 m 版。imgsz640是输入分辨率。前面分析过拐杖宽度只有 20 像素左右如果降到 416拐杖只剩 13 像素检测难度大幅上升。640 是精度和速度的平衡点如果显存够用可以试 768 或 896小目标召回率会有提升。batch16根据显存调整。V100 32G 可以开到 32 甚至 64消费级 8G 显卡建议 8 或 16。batch 太小会导致 BN 层统计量不稳定训练损失震荡batch 太大则可能降低模型泛化能力。lr00.01是初始学习率配合optimizerSGD和cos_lrTrue使用。如果你用 Adam 优化器初始学习率要降到 0.001 左右。lrf0.01是最终学习率因子余弦退火会把学习率从 0.01 降到 0.0001。patience30表示 30 个 epoch 验证指标不提升就早停。4635 张图训练 150 个 epoch 通常够用如果 30 轮没提升继续训下去大概率是过拟合。close_mosaic10表示最后 10 个 epoch 关闭 Mosaic 增强。Mosaic 会把 4 张图拼成 1 张增强小目标的同时也会让拐杖这种细长目标被裁切得七零八落。最后 10 轮关掉让模型在真实分布上微调指标通常会有一次明显提升。3.4 训练过程监控与指标解读训练启动后终端会输出每个 epoch 的损失和指标。重点看三个指标box_loss、cls_loss、mAP50。box_loss是边界框回归损失正常情况应该从 1.5 左右逐步降到 0.5 以下。如果它一直震荡不降检查标注框是否有大量越界或宽高为 0 的异常样本。cls_loss是分类损失2 类别的任务应该很快降到 0.3 以下。如果它居高不下可能是类别标注不一致——比如同一类目标在不同图片里被标成了不同名字。mAP50是 IoU 阈值 0.5 时的平均精度这是最直观的指标。4635 张图、2 类别YOLOv8s 在 640 分辨率下mAP50 跑到 0.85 以上算正常0.9 以上算不错。如果低于 0.7优先检查标注质量而不是调模型。# 训练完成后在验证集上评估 yolo detect val \ modelruns/train/guide_dog_cane_v1/weights/best.pt \ datadata.yaml \ imgsz640 \ batch16 \ conf0.001 \ iou0.6conf0.001是为了计算完整的 PR 曲线评估时不要设太高。iou0.6是 NMS 的 IoU 阈值拐杖和导盲犬经常重叠这个值设太低会导致拐杖被导盲犬的框抑制掉。4. 小目标检测的避坑指南拐杖为什么总被漏检4.1 现象拐杖召回率远低于导盲犬训练完看混淆矩阵导盲犬的召回率 0.92拐杖只有 0.65。验证集的可视化结果里拐杖要么完全没框要么框得歪歪扭扭。这是小目标检测的典型表现不是模型不行是数据分布和训练策略需要调整。原因有三个层面。第一拐杖的像素占比太小YOLOv8 的 P3 特征图 stride 是 8640 输入下 P3 特征图是 80×80拐杖宽度 20 像素映射到 P3 上只有 2.5 个格子特征响应很弱。第二拐杖和背景的对比度低很多图片里拐杖是白色或金属色背景是浅色地面边缘特征不明显。第三拐杖的标注框宽高比极端20×260 的框宽高比是 1:13YOLO 的 anchor 预设里没有这么极端的比例。解决思路提高输入分辨率到 896让拐杖在 P3 上有更多像素在数据增强里加入针对性的裁剪和缩放增加小目标样本调整 anchor 或使用 anchor-free 的检测头。4.2 现象训练中 BN 层崩溃导致损失变 NaN训练到第 40 个 epoch 左右损失突然变成 NaN终端输出一堆RuntimeError: CUDA error。这是 BN 层统计量爆炸的典型表现在小目标数据集上不算罕见。原因通常是某个 batch 里所有样本的拐杖标注都特别小或者某个 batch 的输入图像经过增强后出现了极端值。BN 层在计算均值和方差时如果某个通道的激活值方差接近 0归一化时会除以一个极小数导致数值爆炸。解决办法把batch调大让每个 batch 里有更多样本统计量更稳定在data.yaml里加上rectFalse强制方形输入避免不同宽高比的图像混在一个 batch 里如果还不行把model换成带Dropout的版本或者在 BN 层前加梯度裁剪。# 在训练命令中加入梯度裁剪 yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch32 \ rectFalse \ ...4.3 现象验证集 mAP 高但实际推理漏检严重验证集 mAP50 跑到 0.88但拿几张新图片跑推理拐杖十有八九检测不到。这种「指标好看、落地翻车」的情况多半是验证集和实际场景的分布不一致。4635 张图如果是从视频里抽帧得到的相邻帧之间的图像高度相似随机划分训练集和验证集会导致验证集里有很多「见过的」样本mAP 虚高。正确的做法是按视频片段划分或者按拍摄场景划分确保验证集里的场景在训练集里没出现过。另一个原因是推理时的conf阈值设太高。验证时用conf0.001算 mAP推理时默认conf0.25拐杖的置信度普遍偏低0.25 的阈值直接把拐杖滤掉了。实际部署时把conf降到 0.1 甚至 0.05先保证召回再通过后处理过滤误检。from ultralytics import YOLO model YOLO(runs/train/guide_dog_cane_v1/weights/best.pt) results model.predict( sourcetest_images/, conf0.1, # 降低置信度阈值 iou0.5, # NMS IoU 阈值 imgsz896, # 提高推理分辨率 saveTrue, save_txtTrue )4.4 现象类别不平衡导致拐杖被当成背景统计标注数量发现导盲犬有 3200 个框拐杖只有 1500 个框比例超过 2:1。YOLO 的损失函数里分类损失是交叉熵类别多的那一类会主导梯度拐杖的梯度被淹没。解决办法有三个。一是在data.yaml里给拐杖设置更高的cls权重但 YOLOv8 的官方接口没有直接暴露这个参数需要改源码里的v8DetectionLoss。二是用copy_paste增强把拐杖的标注框复制到更多图片里增加正样本数量。三是用focal_loss替代交叉熵降低易分类样本的权重让模型更关注难样本。我一般先用copy_paste增强改动最小、效果最直接。在data.yaml里加上# data.yaml 中增加增强配置 augment: True copy_paste: 0.3 # 30% 的概率执行 copy-paste 增强copy_paste会把一张图里的拐杖抠出来贴到另一张图上同时更新标注框。这个增强对小目标和类别不平衡特别有效但要注意粘贴的位置不能太离谱否则会引入噪声。4.5 现象模型导出 ONNX 后推理结果和 PyTorch 不一致训练完导出 ONNX 部署发现同一个输入PyTorch 输出 3 个框ONNX 输出 1 个框。这种不一致通常来自导出时的动态轴设置和 NMS 后处理。YOLOv8 导出 ONNX 时默认把 NMS 也导出到模型里但 NMS 的conf和iou阈值是在导出时固定的。如果你在 Python 推理时改了阈值ONNX 模型不会跟着变。# 导出 ONNX指定 NMS 阈值 yolo export \ modelruns/train/guide_dog_cane_v1/weights/best.pt \ formatonnx \ imgsz640 \ simplifyTrue \ opset12 \ nmsTrue \ conf0.1 \ iou0.5导出后先用onnxruntime跑一遍验证和 PyTorch 的输出逐框对比。如果框的数量和坐标对不上检查opset版本和simplify选项。opset12兼容性最好simplifyTrue会优化计算图但偶尔会引入数值误差。5. 从 4635 张到可部署模型验证策略与迭代技巧5.1 用分层抽样划分验证集避免指标虚高4635 张图如果随机划分验证集里可能混入了和训练集同一场景的样本mAP 会虚高 5 到 10 个百分点。我一般用分层抽样先按拍摄场景或视频来源分组然后从每组里抽固定比例作为验证集。import os import random from collections import defaultdict # 假设图像文件名里包含场景标识如 scene01_0001.jpg scene_groups defaultdict(list) for fname in os.listdir(images/all): scene fname.split(_)[0] scene_groups[scene].append(fname) train_list, val_list [], [] for scene, files in scene_groups.items(): random.shuffle(files) split int(len(files) * 0.8) train_list.extend(files[:split]) val_list.extend(files[split:]) print(f训练集: {len(train_list)}, 验证集: {len(val_list)})这样划分后验证集里的场景在训练集里没出现过mAP 更能反映真实泛化能力。如果分层后 mAP 掉了 10 个点以上说明模型过拟合了训练场景需要加数据增强或者换更大的模型。5.2 用 TTA 和模型集成提升小目标召回单模型跑完 mAP50 到 0.88 后如果还想再往上提TTA测试时增强是最省事的办法。YOLOv8 的val和predict都支持augmentTrue会对输入做翻转、缩放等多尺度变换然后融合结果。# 开启 TTA 评估 yolo detect val \ modelruns/train/guide_dog_cane_v1/weights/best.pt \ datadata.yaml \ imgsz640 \ augmentTrue \ conf0.001TTA 通常能提升 1 到 3 个点的 mAP代价是推理速度慢 3 到 5 倍。如果部署环境对延迟不敏感这个 trade-off 很划算。模型集成是另一个思路用 YOLOv8s 和 YOLOv8m 各训一个模型推理时把两个模型的框做加权融合。拐杖这种小目标不同模型漏检的位置往往不一样融合后召回率会有明显提升。集成代码不复杂核心是对两组框做 NMS 后再合并from ultralytics import YOLO import numpy as np model_s YOLO(runs/train/v8s/weights/best.pt) model_m YOLO(runs/train/v8m/weights/best.pt) results_s model_s.predict(test.jpg, conf0.1, imgsz640) results_m model_m.predict(test.jpg, conf0.1, imgsz640) # 提取两组框做加权框融合WBF boxes_s results_s[0].boxes.data.cpu().numpy() boxes_m results_m[0].boxes.data.cpu().numpy() # 后续做 WBF 或 NMS 融合此处省略具体实现5.3 拐杖检测的置信度阈值调优方法拐杖的置信度分布和导盲犬不一样导盲犬的置信度普遍在 0.7 以上拐杖很多在 0.3 到 0.5 之间。如果统一用 0.25 的阈值拐杖会漏掉一大半。我一般对每个类别单独设阈值。先跑一遍验证集导出所有框的置信度和类别然后画每个类别的 PR 曲线找到 F1 分数最高的阈值点。import numpy as np from sklearn.metrics import precision_recall_curve, f1_score # 假设 confidences 和 labels 是验证集上所有框的置信度和真实标签 # 这里只演示逻辑实际需要从 YOLO 的验证输出里提取 precisions, recalls, thresholds precision_recall_curve(labels, confidences) f1_scores 2 * (precisions * recalls) / (precisions recalls 1e-8) best_threshold thresholds[np.argmax(f1_scores)] print(f最佳阈值: {best_threshold:.3f}, F1: {max(f1_scores):.3f})实际部署时导盲犬用 0.3 的阈值拐杖用 0.15 的阈值然后在后处理里加一条规则如果拐杖框和导盲犬框的 IoU 大于 0.5保留导盲犬框拐杖框降权。这样既能保证拐杖召回又不会因为阈值降低引入太多误检。5.4 一个容易被忽略的技巧用背景图做负样本挖掘4635 张图里如果全是正样本模型没见过「没有导盲犬和拐杖」的场景推理时会把一些类似拐杖的物体比如栏杆、树枝误检成拐杖。解决办法是加入负样本——不含任何目标的背景图。负样本不需要多占总数据量的 5% 到 10% 就够了。可以从原始视频里抽一些没有导盲犬和拐杖的帧或者用不含目标的图像。标注文件是空的 txtYOLO 训练时会自动把这些图当成负样本降低误检率。# 负样本的标注文件是空的 touch labels/train/negative_001.txt # 对应的图像文件放在 images/train/negative_001.jpg加入负样本后mAP 可能不会明显提升但误检率会下降。实际部署时误检比漏检更让人头疼——导盲犬拐杖检测系统如果频繁把栏杆识别成拐杖用户很快就会失去信任。5.5 迭代节奏先跑通再调优别在第一轮死磕参数我带过几个做这个方向的人最常见的翻车方式是第一轮训练就死磕参数调了三天还没跑出一个完整的 baseline。正确的节奏是先用默认参数跑 50 个 epoch拿到一个能看的 mAP然后分析混淆矩阵和验证集可视化找到最弱的那一类再针对性地调参。第一轮的目标不是刷到最高 mAP而是确认数据管道没问题、标注没有大错、训练能正常收敛。我一般第一轮用yolov8n.pt、imgsz640、epochs50半小时到一小时就能跑完。看到 mAP50 在 0.7 以上说明数据质量过关再换yolov8s.pt跑 150 个 epoch。如果第一轮 mAP 低于 0.5不要急着调模型先检查标注。把验证集的预测结果画出来看看是框的位置不对还是类别标错了。我遇到过标注文件里把dog写成Dog的情况YOLO 把Dog当成一个新类别训练时类别数对不上mAP 直接崩到 0.1。这种问题调参调不出来只能回去改标注。最后一句话是我自己的习惯每次训练完把data.yaml、训练命令、mAP 曲线和混淆矩阵截图存到一个文件夹里命名带上日期和版本号。下次再训的时候翻记录比翻记忆靠谱。希望帮到你。本文还有配套的精品资源点击获取
返回列表