资讯动态

风筝检测数据集VOC+YOLO格式2260张:从解压到YOLOv8训练全流程

发布时间:2026/9/28 17:21:32 来源:尧图企业网站定制
简介本资源为风筝检测数据集面向计算机视觉方向的学习者与算法开发者适用于目标检测模型的训练、验证与课程实验。数据集同时提供Pascal VOC与YOLO两种标注格式包含jpg图片及一一对应的xml、txt标注文件标注类别仅一类kite共8790个矩形框使用labelImg工具完成标注可直接接入主流检测框架进行训练与评估。压缩包为7z格式共约2000个文件以1998个xml标注文件和2个说明类txt为主整体大小约268MB目录结构清晰便于按图片与标注对应关系快速检索使用。目前已有75人学习下载适合需要单一类别检测数据、希望快速验证模型效果或开展对比实验的读者参考使用。1. 风筝检测数据集 VOCYOLO 格式 2260 张 1 类别从拿到压缩包到跑通训练拿到一个标注好的数据集压缩包最怕的不是数据量小而是格式对不上、路径写错、类别编号错位训练跑起来 loss 不降反升回头查半天发现是 xml 里的 filename 和实际图片名差了一个下划线。风筝检测这个方向本身不算热门公开可用的高质量数据不多2260 张、单类别、同时给 VOC 和 YOLO 两套格式对想做小目标检测、想快速验证 YOLO 训练链路的人来说省掉了最耗时的标注环节。这篇笔记按我实际处理这类数据集的顺序写先搞清楚 VOC 和 YOLO 两套格式到底差在哪、为什么值得同时保留再讲怎么把压缩包拆开、校验、转成 YOLOv8 能直接吃的目录结构然后给出一份能跑通的训练配置和参数解释最后把我在单类别小数据集上踩过的坑逐条列出来。适合已经装好 CUDA 和 ultralytics、手里有这张压缩包、想尽快看到第一轮 mAP 的人。2. VOC 与 YOLO 双格式为什么这个数据集要同时给两套2.1 两套格式的本质差异不在文件后缀VOC 格式的核心是一张图对应一个 XML标注信息写在object节点里坐标是绝对像素值xmin/ymin/xmax/ymax类别用name文本表示。YOLO 格式的核心是一张图对应一个 txt每行是class_id x_center y_center width height全部归一化到 0~1类别用从 0 开始的整数表示。两者最容易被忽略的差异是坐标系原点VOC 的 y 轴向下、坐标是边界框左上角和右下角YOLO 的中心点坐标是框中心宽高是相对整图尺寸的比例。很多人转格式时只除了图像宽高忘了中心点要先算(xminxmax)/2结果框整体偏移半格训练时模型学到的位置先验就是错的。单类别数据集在这件事上反而更危险。多类别时类别错位会直接表现为某类 AP 为 0容易发现单类别只有0一个 idxml 里写的是kite还是风筝还是Kite转成 YOLO 后都是 0表面看没问题但如果你后续要合并其他数据集、或者用 COCO 预训练权重做微调类别名对不上就会在验证阶段报 key error。所以拿到双格式数据集第一件事不是急着训练而是确认两套格式的类别名和 id 映射是否自洽。2.2 2260 张单类别数据集的定位2260 张在检测数据集里属于小规模。作为参照VOC2007 训练验证集约 5000 张、COCO2017 超过 12 万张。这个量级适合做三件事验证自建训练流水线是否跑通、做迁移学习后的快速迭代、作为某个更大数据集的补充子集。不适合从零训练一个大 backbone也不适合做需要大量长尾样本的开放词汇检测。单类别意味着模型只需要学一个前景概念收敛会比多类别快但也更容易过拟合到背景纹理上——如果 2260 张里风筝出现的天空、草地、树枝背景高度重复模型可能记住的是背景而不是风筝本身。这一点在划分验证集时要特别注意尽量让验证集的背景分布和训练集有差异。2.3 双格式并存的实用价值同时保留 VOC 和 YOLO实际用起来是这样分工的VOC 的 XML 可读性好排查标注问题时直接打开看坐标和类别比 txt 直观YOLO 的 txt 是 ultralytics 系列、Darknet 系列直接吃的格式训练时不用再转。另外 VOC 格式方便和 LabelImg、Labelme 这类标注工具对接如果你后续要在这个数据集上追加标注从 XML 继续标比从 txt 反推回 XML 省事。所以我的习惯是原始 VOC 目录只读不动所有转换产物放到单独的datasets/kite_yolo下转换脚本保留出问题能一键重跑。3. 解压、校验与目录重组把压缩包变成可训练结构3.1 解压后先做三件事7z 压缩包解压后目录结构往往和训练框架期望的不一样。常见的是VOC2007/Annotations、VOC2007/JPEGImages、VOC2007/ImageSets/Main这一套也可能作者直接平铺成images/和labels/。先别改按下面三步确认。第一步数文件。图片数、xml 数、txt 数如果有应该一致2260 张图对应 2260 个 xml。数量对不上说明有图没标或标了没图这种样本在训练时会报missing label或直接被跳过。第二步抽查文件名。VOC 的 xml 里filename字段必须和实际图片文件名完全一致包括扩展名大小写。我遇到过.JPG和.jpg混用的情况Linux 下区分大小写训练时找不到图。第三步看类别名。把所有 xml 的name抽出来去重确认只有一种写法。# 统计图片、xml 数量并列出所有出现过的类别名 find VOC2007/JPEGImages -type f \( -iname *.jpg -o -iname *.png \) | wc -l find VOC2007/Annotations -name *.xml | wc -l grep -h name VOC2007/Annotations/*.xml | sort | uniq -c这段命令的逻辑很直接find按扩展名统计图片grep -h去掉文件名前缀只留匹配行sort | uniq -c统计每个类别名出现次数。如果输出里出现两种以上写法比如kite和Kite各占一半必须在转换前统一否则 YOLO 侧会生成两个 class id而你的data.yaml只写了一个类别训练直接报维度不匹配。3.2 用脚本把 VOC 转成 YOLO 并生成目录转换脚本我一般写成可重复执行的输入 VOC 根目录输出 YOLO 结构。核心是解析 XML、算归一化坐标、按比例划分 train/val。import os import glob import random import xml.etree.ElementTree as ET from PIL import Image VOC_ROOT VOC2007 OUT_ROOT kite_yolo CLASSES [kite] # 单类别顺序即 class_id VAL_RATIO 0.2 random.seed(42) # 固定种子保证每次划分一致 def convert_one(xml_path, out_img_dir, out_lbl_dir): tree ET.parse(xml_path) root tree.getroot() img_name root.find(filename).text img_path os.path.join(VOC_ROOT, JPEGImages, img_name) w, h Image.open(img_path).size lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASSES: continue cls_id CLASSES.index(name) 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.0 / w yc (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h # 裁剪到 [0,1]防止标注越界导致训练报错 xc, yc min(max(xc, 0), 1), min(max(yc, 0), 1) bw, bh min(max(bw, 0), 1), min(max(bh, 0), 1) lines.append(f{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) if not lines: return None # 图片软链接或复制到输出目录这里用复制保证可移植 import shutil shutil.copy(img_path, os.path.join(out_img_dir, img_name)) with open(os.path.join(out_lbl_dir, os.path.splitext(img_name)[0] .txt), w) as f: f.write(\n.join(lines)) return img_name xmls sorted(glob.glob(os.path.join(VOC_ROOT, Annotations, *.xml))) random.shuffle(xmls) n_val int(len(xmls) * VAL_RATIO) splits {val: xmls[:n_val], train: xmls[n_val:]} for split, files in splits.items(): img_dir os.path.join(OUT_ROOT, images, split) lbl_dir os.path.join(OUT_ROOT, labels, split) os.makedirs(img_dir, exist_okTrue) os.makedirs(lbl_dir, exist_okTrue) ok 0 for x in files: if convert_one(x, img_dir, lbl_dir): ok 1 print(split, converted:, ok, /, len(files))逻辑说明convert_one负责单文件转换先读图像真实宽高不要用 xml 里可能存在的size字段有些标注工具写的尺寸和实际图不符再遍历 object 算归一化坐标。random.seed(42)是关键数据集划分必须可复现否则你调参时换了验证集指标波动根本分不清是模型变了还是数据变了。VAL_RATIO0.2对 2260 张来说验证集约 452 张够看趋势如果做最终评估建议再单独切一份 test。参数说明CLASSES列表顺序决定 class_id单类别就一个元素但保留列表形式方便以后加类。坐标裁剪到 [0,1] 是防御性写法标注越界在人工标注里很常见不裁的话 YOLO 训练会警告甚至报错。输出用复制而非软链接是为了整个kite_yolo目录能直接打包搬走。3.3 生成 data.yaml 并做一次加载自检YOLOv8 训练靠data.yaml找数据路径写错是最常见的翻车点。path: /abs/path/to/kite_yolo train: images/train val: images/val nc: 1 names: 0: kitepath用绝对路径train/val相对path。写完先跑一次自检不训练只让 ultralytics 加载数据集yolo detect train datadata.yaml modelyolov8n.pt epochs1 imgsz640 batch8 \ workers0 device0 valFalsevalFalse跳过验证epochs1只跑一轮目的是确认数据能被读进来、标签能被解析。如果这一步报No labels found八成是labels/train目录名或层级不对报class index out of range就是 txt 里的 id 超过了nc。4. 用 YOLOv8 跑通第一轮训练配置、参数与观察点4.1 模型和输入尺寸怎么选2260 张单类别backbone 不要贪大。yolov8n参数量约 3.2Myolov8s约 11.2M。小数据集上 n 往往比 s 更稳因为 s 更容易在几百轮内过拟合。输入尺寸imgsz默认 640如果风筝在图中占比很小比如航拍远景可以提到 960 或 1280但显存和训练时间会明显上升。判断标准统计一下标注框的宽高分布如果中位数宽高小于 32 像素就值得提分辨率。# 快速看标注框尺寸分布决定 imgsz import glob, statistics ws, hs [], [] for f in glob.glob(kite_yolo/labels/train/*.txt): for line in open(f): _, _, _, w, h map(float, line.split()) ws.append(w); hs.append(h) print(width median:, statistics.median(ws)) print(height median:, statistics.median(hs))输出的是归一化值乘以imgsz就是像素。中位数乘 640 后如果小于 32说明小目标偏多考虑提分辨率或开mosaic增强。4.2 一份能直接用的训练命令yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ warmup_epochs3 \ mosaic1.0 \ close_mosaic10 \ patience30 \ device0 \ projectruns/kite \ nameexp1参数逐个说lr00.01是初始学习率用预训练权重微调时这个值偏大如果 loss 前几轮就震荡降到 0.001。lrf0.01是最终学习率相对初始值的比例余弦退火到 0.0001。warmup_epochs3让学习率从很小线性升上来避免一开始就大步长破坏预训练权重。mosaic1.0是四图拼接增强对小数据集提升明显但close_mosaic10表示最后 10 轮关掉让模型在真实分布上收尾。patience30是早停30 轮验证指标不升就停省时间也防过拟合。4.3 训练过程中盯哪几个数启动后先看第一轮有没有报错然后重点盯三个输出box_loss、cls_loss、mAP50。单类别时cls_loss会降得很快如果它一直不降检查标签文件是不是空的或者类别 id 写错。box_loss反映定位精度正常应该在前 20 轮快速下降然后趋缓。mAP50在 2260 张上用预训练权重微调通常 50 轮内能到 0.7 以上如果 100 轮还低于 0.5先别调模型回去查数据——大概率是验证集里有训练集没覆盖的背景或者标注框有系统性偏移。验证阶段还会输出混淆矩阵。单类别时矩阵是 2x2看的是kite被预测成背景的比例。如果这个比例高说明漏检严重可能是小目标太多或置信度阈值设太高可以在推理时把conf从默认 0.25 降到 0.1 试试。5. 单类别小数据集避坑5 条血泪记录5.1 现象训练 loss 正常下降但 mAP 始终为 0原因验证集的标签路径对但图片路径不对模型在验证时读到的是空白或全黑图预测全为背景和真实标签完全错开。ultralytics 在图片缺失时不一定报错可能静默跳过或用占位图。解决训练前用yolo detect train ... valFalse跑一轮确认数据加载再单独跑一次yolo detect val modelbest.pt datadata.yaml看验证集图片数是否等于你划分的数量。数量对不上就查images/val和labels/val的文件名是否一一对应。5.2 现象训练到一半突然报 CUDA out of memory原因mosaic增强会把四张图拼成一张实际输入尺寸不变但内容密度变大显存占用比单图高另外batch16在 640 分辨率下对 8G 显存偏大。解决先把batch降到 8再不行降到 4同时把workers设为 0 排除多进程读图的干扰。如果还爆把imgsz降到 512。不要一上来就换大显存卡先确认是不是增强和 batch 的组合问题。5.3 现象验证集 mAP 比训练集高很多原因2260 张里如果按随机划分验证集可能恰好抽到了背景简单、目标清晰的样本而训练集里混入了大量遮挡、模糊的难例。单类别小数据集上这种分布不均很常见。解决不要用纯随机划分。按标注框数量或目标像素面积分层抽样让训练集和验证集的难度分布接近。简单做法是把每张图的框面积中位数算出来排序后每隔 5 张抽 1 张进验证集。5.4 现象推理时框位置整体偏移原因转换脚本里用了 xml 的size字段而不是实际图像尺寸而标注时的尺寸和实际图不一致常见于图片被缩放后没更新 xml。解决转换时一律用PIL.Image.open(img_path).size读真实尺寸。已经转完的写个校验脚本抽查 20 张把 txt 里的框反算回像素画到图上肉眼确认。5.5 现象训练日志里cls_loss为 nan原因标签文件里有空行或坐标值超出 [0,1]归一化后出现负数或大于 1 的值计算损失时 log(0) 导致 nan。解决转换脚本里加坐标裁剪见 3.2 的min(max(...))并在转换后跑一遍校验删掉空 txt 和越界行。已经生成的标签可以用一行命令扫awk NF!5 || $20 || $21 || $30 || $31 || $40 || $41 || $50 || $51 {print FILENAME: $0} kite_yolo/labels/train/*.txt输出为空说明全部合法有输出就按文件名定位修正。6. 把 2260 张用到极致增强策略与验证技巧数据量小的时候增强不是可选项而是必需项。除了 YOLOv8 默认开的 mosaic、HSV 抖动、随机翻转我还会针对风筝这个场景加两样一是随机缩放因为风筝在画面里的尺度变化大从近景特写到远景小点都有二是随机旋转小角度风筝在空中姿态多变水平框对旋转不敏感但小角度旋转能让模型见到更多姿态组合。ultralytics 的配置里可以通过scale、degrees参数控制scale0.5表示随机缩放 0.5~1.5 倍degrees10表示正负 10 度旋转。验证阶段有个容易被忽略的点单类别数据集的 mAP 对置信度阈值很敏感。默认conf0.001算 mAP 时会把大量低置信预测纳入如果模型输出很发散mAP 会被拉低。我一般会在验证时固定conf0.25看实际可用指标同时看conf0.001的理论上限两个数差太多说明模型置信度校准不好需要更多数据或更强增强。最后说一个我自己的习惯每次在这个数据集上改完配置不管结果好坏都把data.yaml、训练命令、results.csv和best.pt的路径记到同一个笔记里。2260 张跑一轮 150 epoch 在单卡上大概几十分钟到一小时试错成本不高但不记录的话两周后你根本想不起来当时lr0设的是 0.01 还是 0.001。这个数据集的价值不在于它有多大而在于它足够小、足够干净能让你把从 VOC 到 YOLO 再到训练评估的整条链路快速走通走通之后换任何数据集都是同一套流程。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑