ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

羽毛球目标检测数据集实战:从YOLO训练到三格式转换

羽毛球目标检测数据集实战:从YOLO训练到三格式转换 1. 一个人工智能项目为什么非要从“数据集”讲起拿到这个标题的时候我第一个反应是这不就是又一份“标注好的目标检测数据集”吗但仔细一看2879张图、识别率84.4%、三格式全支持yolo、coco json、voc xml还有配套的模型训练代码——这套东西放在一起就已经不是“数据集”三个字能概括的了。它是一个可以直接复制、直接跑通的羽毛球场景目标检测完整方案。对于一个正在学目标检测、或者正在做体育类AI项目的人来说这省掉的不是一点点功夫。很多人第一次接触目标检测上来就找现成的预训练权重然后拿一张图片去“测着玩”。但真正落到实际项目里你会发现**模型效果的上限往往在数据集里就已经定死了。**数据集的标注是否规范、场景是否丰富、类别覆盖是否完整直接决定你能把模型训到什么程度。所以这篇文章我就围绕“带标注的羽毛球运动员、裁判、羽毛球识别数据集”这个项目把里面涉及的目标检测基础、标注格式转换、训练流程、踩坑经验一条龙讲清楚。不管你是学生做毕业设计还是工程师做体育视频分析都能从这里找到可以直接用的东西。先把这个数据集的价值说透它一共2879张图像覆盖三类目标——羽毛球运动员、裁判、羽毛球。这三类目标在画面里的尺度差异非常大运动员和裁判是中等偏大的目标羽毛球本身却是一个小目标往往只有几十个像素。这种“大目标极小目标”混合的场景恰恰是目标检测模型最容易翻车的地方。所以这个数据集不只是“能用来训练”这么简单它本身就是一套绝佳的练手素材可以用来研究小目标检测、多尺度特征融合、不同检测头的取舍等等问题。文章后面我会讲几块东西先把这个数据集的构成和标注格式讲清楚再带你从头跑一遍模型训练然后把yolo、coco json、voc xml这三种格式之间的转换逻辑梳理明白最后分享一些我在训练过程中遇到的实际问题和排查经验。2. 数据集硬核拆解2879张图背后藏着哪些门道2.1 图像构成与场景覆盖先看家底。2879张图像全部来自真实的羽毛球比赛和训练场景。这意味着什么意味着数据里天然包含室内场馆光照不均、背景观众席干扰、高速运动带来的模糊、运动员姿态剧烈变化等等复杂情况。这些都不是合成数据能模拟出来的也正是真实项目里最让人头疼的部分。从目标分布来看数据集里每一张图都同时出现运动员和羽毛球裁判则根据比赛视角不同而出现。羽毛球作为目标的特点是体积小、速度快、形状在飞行过程中会变换球头朝前、羽毛展开、旋转过程中的模糊态。这种目标对检测器的要求很苛刻——它既考验模型的空间分辨率也考验特征提取网络对小目标的敏感程度。从标注质量上看这个数据集是人工标注的。这一点非常重要。市面上确实存在一些自动标注工具但自动标注在羽毛球这种快速运动的小目标上错误率很高经常出现漏标、框偏的情况。人工标注的准确性虽然达不到像素级但在目标检测任务里已经属于高质量标注了。我实际打开图片看过标注框贴合度整体不错羽毛球即使在飞行模糊状态下框的位置也标得比较准。2.2 84.4%识别率是怎么算出来的标题里写的“识别率84.4%”很多人可能不理解这个数字的含义。这里面的门道得说清楚它是指在测试集上模型预测结果与真实标注框的IoU交并比超过一定阈值通常是0.5时判定为正确检测的比例。IoU就是一个预测框和真实框重叠程度的衡量指标。打个比方你用红笔圈出一个目标标准答案是蓝笔圈的两个圈重合面积越大说明你圈得越准。那么84.4%这个数字算不算好分场景看。如果放在物体位置较为固定、目标较大的工业检测场景84.4%确实不算亮眼。但放在羽毛球这种高速运动、目标尺度悬殊的场景里这个数字已经相当能打了。因为小目标检测本身就是目标检测领域公认的难题——你拿当前最火的YOLOv8去检测一张分辨率普通图片里只有30x30像素的羽毛球置信度能稳定上0.7就已经很不容易。这个数据集能做到84.4%说明标注质量过关模型训练流程也经得起验证。2.3 三格式支持的背后逻辑这个数据集一次给了yolo、coco json、voc xml三种格式。很多人拿到手里会蒙这三种格式到底什么区别我是不是只用一个就行一句话概括三种格式是三种不同的“坐标系”和“描述方式”本质都是告诉模型“目标在哪里、是什么”。YOLO格式一张图片对应一个同名txt文件每一行代表一个目标格式是类别id 中心点x 中心点y 宽度w 高度h。注意这里x、y、w、h都是相对于图片宽高的归一化值范围在0到1之间。VOC XML格式一个xml文件对应一张图片用bndbox标签记录目标左上角和右下角的绝对像素坐标。COCO JSON格式所有标注信息汇总到一个json文件里里面有images、annotations、categories三个核心字段用的是绝对像素坐标。这三者之间的转换并不复杂但容易出错的地方在于坐标系的换算。YOLO格式的归一化坐标需要乘以图片宽度和高度才能还原成绝对像素坐标而VOC和COCO虽然都用绝对坐标但一个是“左上角右下角”另一个是“左上角宽度高度”换算的时候很容易把坐标搞混。注意在做三格式转换时最容易踩的坑就是YOLO坐标没有还原就拿来当像素坐标用。一定要记住YOLO里的x和y是中心点不是左上角。3. 从零跑通训练YOLO模型训练代码实战3.1 环境配置Anaconda一次性搞定训练YOLO系列模型现在最推荐的方式是用YOLOv8的官方框架Ultralytics。它的环境配置比之前的YOLOv5更简单依赖管理也做得更干净。我自己习惯用Anaconda来管理Python环境这样不同项目之间不会互相污染包版本。先创建一个独立的conda环境conda create -n badminton python3.9 -y conda activate badminton接下来安装PyTorch。这一步要注意PyTorch的CUDA版本一定要和你的显卡驱动匹配。如果你的显卡是NVIDIA的先在命令行输入nvidia-smi查看驱动支持的CUDA版本再去PyTorch官网选择对应的安装命令。比如我这里用CUDA 11.8版本pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118最后安装Ultralytics框架pip install ultralytics装完之后可以跑一条命令验证环境是否正常yolo predict sourcetest.jpg modelyolov8n.pt如果能看到预测结果输出说明环境OK。3.2 数据组织把数据集切成标准结构YOLO训练对数据目录结构有固定要求。先把数据集按8:1:1的比例划分成训练集、验证集、测试集。这个比例是目标检测任务里比较常见的划分方式80%的数据用于训练10%用于验证10%用于测试。划分之后目录结构长这样dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── badminton.yaml其中badminton.yaml是数据配置文件内容如下names: 0: player 1: umpire 2: shuttle nc: 3 path: ./dataset train: images/train val: images/val test: images/test这里要注意names的顺序必须和标注文件里的类别id保持一致。如果你自己转换格式的时候把类别顺序改了模型训练时就会张冠李戴。3.3 训练启动参数设置与显存管理执行训练命令yolo train databadminton.yaml modelyolov8s.pt epochs100 batch16 imgsz640 device0这里有几个参数值得展开讲modelyolov8s.pt使用YOLOv8s作为基础模型。为什么不选nano或small因为羽毛球是小目标模型太浅的话特征提取能力不够但也不能一上来就选large或xlarge2879张图的规模喂不饱大模型容易过拟合。s档是平衡点。imgsz640输入图像缩放尺寸。YOLOv8默认就是640。但如果你发现羽毛球太小检测不到可以考虑用896或1024代价是显存占用翻倍、训练时间变长。batch16批大小。这个值受显卡显存限制。8GB显存跑yolov8s加640分辨率16的批大小基本是上限了再大就会爆显存。训练过程会自动输出详细日志包括每个epoch的box_loss、cls_loss、dfl_loss以及precision、recall、mAP50、mAP50-95这些评估指标。不要只盯着一个指标看特别是mAP50-95这个指标它比mAP50更严格综合衡量了不同IoU阈值下的检测精度更能反映真实水平。3.4 训练开销与时间预期2879张图看起来不多但在目标检测任务里是能跑出有效结果的量级。用一张RTX 3060 12GB跑yolov8s640分辨率100个epoch大约需要3到5个小时。如果换成RTX 4090能压缩到1小时以内。如果你手里的卡是入门级可以参考这个策略先把epochs降到50跑通全流程再根据验证集指标决定是否加训。下面这个表格对不同配置下的训练时间做个粗略预估显卡显存模型图片尺寸预估训练时间100轮RTX 306012GByolov8s6403-5小时RTX 40608GByolov8n6402-3小时RTX 409024GByolov8s64030-50分钟无NVIDIA显卡内存有限yolov8n4168-12小时CPU如果只能用CPU训练建议把imgsz降到416batch降到4model换成yolov8n否则训练速度会慢到让人崩溃。3.5 模型预测与结果导出训练完成后模型权重保存在runs/detect/train/weights/best.pt。用这个权重对测试图片进行预测yolo predict sourcetest.jpg modelruns/detect/train/weights/best.pt conf0.25conf0.25是置信度阈值。这个值决定了“模型有多大的把握才输出检测结果”。值设得高漏检多但误检少值设得低什么都框出来但假阳性多。实际调参时可以用这个思路优先关注recall查全率的场景把conf设低一点优先关注precision查准率的场景把conf设高一点。4. 三种标注格式转换实操从零手写也不难4.1 YOLO转COCO JSON全流程先说最常用的YOLO转COCO。COCO格式的核心是那个大json文件结构长这样images每张图片的信息列表包括id、file_name、width、heightannotations每个目标框的信息包括id、image_id、category_id、bbox、areacategories类别列表包含id和name关键换算逻辑是YOLO格式的cx、cy、w、h全部归一化到0-1乘以图片的width、height得到像素坐标。然后转换成COCO要求的左上角x、左上角y、框宽、框高。我写过一个转换脚本核心函数长这样import os import json from PIL import Image def yolo_to_coco(img_dir, label_dir, class_names, output_json): categories [] for idx, name in enumerate(class_names): categories.append({id: idx, name: name}) images [] annotations [] ann_id 1 for img_file in sorted(os.listdir(img_dir)): if not img_file.endswith((.jpg, .jpeg, .png)): continue img_id len(images) 1 img_path os.path.join(img_dir, img_file) im Image.open(img_path) width, height im.size images.append({ id: img_id, file_name: img_file, width: width, height: height }) label_file os.path.join(label_dir, os.path.splitext(img_file)[0] .txt) if not os.path.exists(label_file): continue with open(label_file, r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) ! 5: continue cat_id int(parts[0]) cx float(parts[1]) * width cy float(parts[2]) * height bw float(parts[3]) * width bh float(parts[4]) * height x cx - bw / 2 y cy - bh / 2 annotations.append({ id: ann_id, image_id: img_id, category_id: cat_id, bbox: [x, y, bw, bh], area: bw * bh, iscrowd: 0 }) ann_id 1 with open(output_json, w) as f: json.dump({images: images, annotations: annotations, categories: categories}, f)这个脚本的核心就一句话归一化坐标乘以图片实际尺寸还原绝对值。说起来简单但做不好的人不在少数因为很多人忘了去查询图片的实际尺寸而是硬编码了一个固定值这就会导致所有标注框位置偏移。4.2 COCO JSON转VOC XML的注意点COCO转VOC的换算逻辑稍微简单一些COCO的bbox是[x, y, width, height]VOC需要的是[xmin, ymin, xmax, ymax]所以只需要把x width得到xmax把y height得到ymax就行。一个容易忽略的点是VOC的XML文件对坐标值要求是整数。COCO标注里经常出现浮点数保存成XML前要四舍五入。别小看这个细节有的训练框架在读取XML时遇到浮点坐标会直接报类型错误。4.3 格式转换中的坐标系陷阱关于坐标转换这张表可以当作速查手册格式坐标描述归一化数据类型YOLO中心点x, 中心点y, 宽w, 高h是0到1浮点VOC XML左上角xmin, 左上角ymin, 右下角xmax, 右下角ymax否绝对像素整数COCO JSON左上角x, 左上角y, 宽w, 高h否绝对像素浮点只要你掌握了“归一化坐标、绝对像素坐标、左上角右下角、左上角宽高”这四个概念之间的转换关系三种格式随便切。5. 工具选型LabelImg还是第三方标注平台5.1 LabelImg打标流程与YOLO格式导出自己补充标注数据时大多数人第一选择是LabelImg——开源、免费、界面简单。安装方式很直接pip install labelImg labelImg启动后在菜单里选择PascalVOC模式或YOLO模式前者标完生成xml后者标完生成txt。我个人建议用PascalVOC模式标注最后再统一转成YOLO格式因为VOC格式保留了图片尺寸信息出错了容易排查而YOLO格式只有归一化数值出了错你都看不出来是哪里不对。操作的时候有几个快捷键必须记熟W创建标注框D切换到下一张图A切换到上一张图CtrlS保存用LabelImg标注羽毛球时最烦的是框小球。我的经验是先用W创建一个大概的框再用鼠标微调四边不要试图一上来就框得很准。另外羽毛球运动模糊时人眼都看不清宁可框一个保守的小框也不要框住一大片背景因为背景混入会严重影响分类器的特征学习。5.2 自动标注辅助加速标注的现代方案我自己在做一个补充标注任务时用了一个更省力的方案先用已经训练好的模型对未标注图片做自动预标注跑一遍predict然后输出结果转成LabelImg能打开的VOC格式人工只需要负责修正和确认。这个流程能省掉60%以上的重复劳动。不过对新手还是建议老老实实手动标一批基础数据因为自动预标注的前提是你已经有一个效果还不错的权重文件——这本身就是一个先有鸡还是先有蛋的问题。6. 训练过程实录与性能调优6.1 我踩过的三个典型训练坑第一坑类别id错位。我在第一次训练时用的标注文件里类别顺序是0: player, 1: umpire, 2: shuttle但数据集配置文件里写成了0: shuttle, 1: player, 2: umpire。结果模型把羽毛球认成了运动员mAP掉到了40%左右。这个问题非常隐蔽因为训练日志不会报错只有看预测结果才看得出来。第二坑验证集和训练集重叠。数据划分时没有做好去重导致某个文件夹里的图片既在训练集又在验证集。最终指标看起来很高但换到新视频上就原形毕露。正确的做法是划分数据前先把所有图片按文件名排好序再用随机抽样的方式分配而不是手工拖拽文件夹。第三坑batch设置过大爆显存。听起来是新手问题但我在调高分辨率测试时也遇到过。解决方案是开启梯度累积yolo train databadminton.yaml modelyolov8s.pt epochs50 batch16 imgsz896 device0如果显存不够可以先设batch4跑通流程再逐步往上加。甚至可以通过改Ultralytics配置文件里的accumulate参数用小batch配合梯度累积模拟大batch的效果。6.2 参数调优的核心思路除了训练轮数、批大小、输入尺寸还有几个超参数值得关注学习率YOLOv8默认是0.01。如果训练loss震荡剧烈可以降到0.005如果收敛特别慢可以提到0.02。权重衰减默认是0.0005。数据量较小的时候可以适度提高到0.001来抑制过拟合。数据增强YOLOv8内置了mosaic、翻转、色彩变换等增强策略。2879张图本身不算多数据增强策略可以全部保留能有效提升模型的泛化能力。我做实验时试过一组对比同样是50个epoch开mosaic增强的版本比关掉mosaic的版本mAP50高出3到5个百分点。尤其是在羽毛球这种小目标上mosaic把不同图片拼接在一起训练极大增加了目标尺度的多样性。6.3 小目标检测的专项优化策略这个数据集里羽毛球是小目标训练中会遇到“看着loss在降但小目标就是测不准”的问题。几个亲测有效的策略一是提升输入分辨率。把imgsz从640提到960小目标的特征像素变多了检测率能明显提升。代价是显存占用和学习速度。二是使用更深的检测头。YOLOv8的Head针对不同尺度目标已经优化过但如果你用的是旧版YOLOv5可以考虑给小目标增加一个更小的特征图分支也就是常见的“P2检测头”。三是调整Anchor。YOLOv8已经是anchor-free方案不需要手动设置anchor了但如果你用的是YOLOv5要根据数据集的真实框尺寸重新聚类anchorpython train.py --data badminton.yaml --cfg models/yolov5s.yaml --hyp data/hyps/hyp.scratch-low.yaml --epochs 100聚类锚框时算出来羽毛球这个类别的框普遍偏小会直接影响最终检测效果。7. 常见问题与排查技巧速查表7.1 训练阶段高频问题排查问题现象可能原因解决方案训练前报错“No labels found”标注文件目录结构不对或txt内容为空检查labels路径确认txt文件内容非空loss一直是nan学习率过大或数据中包含异常值降低学习率检查图片是否损坏mAP极低低于30%类别id错位打开一张预测图看框和类别是否对应训练loss下降但val指标不动过拟合增大数据增强增加权重衰减降低模型复杂度显存溢出输入尺寸或batch过大降低imgsz或batch开启梯度累积预测时结果为空置信度阈值过高调低conf比如0.05先看全量输出7.2 标注数据常见问题排查问题现象可能原因解决方案YOLO训练时某个类完全学不出来该类别的标注框太少检查类别分布做数据扩充预测框整体偏移坐标归一化错误重新检查YOLO转像素坐标的逻辑标注框比目标大很多人工标注质量问题用LabelImg逐张复查或删掉明显错误框8. 最终实操心得这套羽毛球数据集的完整流程跑下来我对“小目标检测”这件事有了更深的体感。很多人以为目标检测就是把模型下载下来训练一把但真正决定失败与否的往往是数据准备阶段——标注格式转换错一位小数模型就废了一半类别id错一位直接学成“羽毛球运动员识别羽毛球的迷惑行为”。下次你拿到任何带标注的数据集先别看脸上写着多少mAP先按我上面讲的检查顺序过一遍数据目录结构、标注坐标格式、类别映射表、训练验证集划分这四个地方不出错再谈模型调优。如果你想把这个项目继续往前推还有两个方向可以玩一是给数据集加上“动作状态”属性从单纯的检测升级到动作识别二是用TensorRT做模型量化加速把训练好的模型部署到边缘设备上做到实时视频流检测。我自己下一个准备实验的方向是把视频连续帧的检测结果做时序关联用来追踪一颗羽毛球在整场比赛中的运动轨迹——如果你也在折腾这个数据集欢迎一起交流踩坑经验。
返回列表