
简介这份资源面向计算机视觉初学者与目标检测开发者提供一套可直接用于训练的网球场景数据集覆盖网球与运动员两类目标的识别任务适配yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流算法。压缩包共2000个文件约87.8MB其中1707个xml文件为VOC格式标注293个txt文件为YOLO格式标注两种格式分别存放便于按需选用同时附带data.yaml配置文件数据集已划分完毕可直接投入模型训练与验证测试。YOLO格式采用类别索引加归一化中心点与宽高的标准写法坐标范围在0到1之间方便快速接入训练流程。目前已有131人学习下载适合想练习目标检测全流程、验证算法效果或搭建网球场景识别demo的读者参考使用。1. 网球数据集落地1956 张带标签图像能直接喂给 YOLO 吗上周有个做体育视频分析的朋友甩给我一个压缩包说想拿它训练一个网球检测模型问我能不能直接跑。我打开一看1956 张图像每张都配了标签目录里还躺着data.yaml文件名清一色img_0910_xxx这种流水号。这不是那种网上随便爬来、标签乱七八糟的野数据集而是已经按 YOLO 训练规范整理过的成品。对于想快速验证 yolov8 目标识别流程、又不想花两天时间清洗数据的人来说这种资源省掉的是最枯燥的环节。它适合三类人刚入门 yolo 系列算法、想跑通训练到推理全链路的手里有业务场景、需要拿现成数据做 baseline 的以及想对比 yolov5 和 yolov8 在同一份数据上收敛差异的。但“能直接喂”不等于“无脑喂”标签格式、划分比例、类别索引这些细节没对齐照样翻车。2. 拆开压缩包目录结构、标签格式与 data.yaml 的对应关系2.1 两种标签格式并存先搞清楚用哪套这个数据集最实在的地方是同时给了 YOLO 格式的 txt 和 VOC 格式的 xml分别放在两个文件夹里。YOLO 格式每行是class x_center y_center width height坐标全部归一化到 0 到 1 之间类别索引从 0 开始。VOC 格式则是标准的object节点里面写name和bndbox的绝对像素坐标。如果你用 yolov5 到 yolo11 这一系直接走 txt 那套xml 留着做备份或者转其他框架用。常见做法是训练前先确认 txt 文件夹里的类别索引和data.yaml里的names列表顺序完全一致差一位模型学出来的就是错位的类别。2.2 data.yaml 里到底写了什么data.yaml是 YOLO 训练入口的配置文件一般长这样path: ./tennis_dataset train: images/train val: images/val nc: 1 names: [tennis]path是数据集根目录train和val是相对路径指向划分好的图像文件夹。nc是类别数网球检测通常就是 1 类。names是类别名列表顺序必须和 txt 里的 class 索引对得上。拿到手第一件事不是急着跑训练而是打开这个文件把path改成你本地解压后的绝对路径或相对路径否则训练脚本找不到图。2.3 图像和标签的配对逻辑YOLO 训练时脚本会根据图像路径自动去找同名的 txt 标签文件。比如images/train/img_0910_201.jpg对应labels/train/img_0910_201.txt。这个数据集已经按 train 和 val 分好了你不需要再跑划分脚本。但要注意如果图像是 jpg标签是 txt文件名主干必须一模一样多一个下划线、少一个数字那张图就会被当成无标签样本跳过。我一般会先跑一段小脚本核对配对率低于 99% 就先修文件名。import os img_dir images/train lbl_dir labels/train imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir)} lbls {os.path.splitext(f)[0] for f in os.listdir(lbl_dir)} missing imgs - lbls print(f图像数: {len(imgs)}, 标签数: {len(lbls)}, 缺标签: {len(missing)}) if missing: print(前10个缺标签文件:, list(missing)[:10])这段脚本做的是集合差运算把图像文件名主干和标签文件名主干分别提取成集合差集就是没有对应标签的图像。os.path.splitext去掉扩展名set去重并支持快速差运算。输出里如果缺标签是 0说明配对完整如果不是 0把缺的名单打出来手动补标签或者删图。3. 从零跑通 yolov8 训练环境、命令与参数怎么改3.1 环境准备与依赖安装yolov8 用 ultralytics 这个库安装比早期版本省心很多。我一般用 conda 建一个干净环境Python 选 3.10然后 pip 装 ultralytics。显卡驱动和 CUDA 版本要匹配不然训练时 torch 会回退到 CPU速度差几十倍。装完之后跑一行yolo checks它会打印环境诊断包括 torch 版本、CUDA 是否可用、显卡型号。conda create -n yolo_tennis python3.10 -y conda activate yolo_tennis pip install ultralytics yolo checksyolo checks是 ultralytics 自带的诊断命令会输出 torch 和 CUDA 的匹配状态。如果显示CUDA:0 (NVIDIA ...)就说明 GPU 可用如果只显示 CPU先去查驱动别急着跑训练。3.2 训练命令与关键参数假设你已经把data.yaml里的path改好了训练命令就一行yolo detect train \ data./tennis_dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ projecttennis_runs \ nameexp1modelyolov8n.pt用的是 nano 版本参数量小适合先跑通流程。epochs100是训练轮数1956 张图不算多100 轮一般能收敛。imgsz640是输入分辨率网球在画面里通常不大640 够用想提精度可以上 1280但显存翻倍。batch16看显存调8G 显存跑 640 分辨率大概能到 16不够就降到 8。device0指定第一块 GPU。project和name控制输出目录跑完的权重、曲线、混淆矩阵都在tennis_runs/exp1下面。3.3 训练过程看什么指标训练启动后终端会打印每一轮的 box_loss、cls_loss、dfl_loss 和 mAP。box_loss 管边界框回归cls_loss 管分类dfl_loss 是分布焦点损失yolov8 特有的。重点看 mAP50 和 mAP50-95前者是 IoU 阈值 0.5 时的平均精度后者是 0.5 到 0.95 多阈值平均。网球检测这种单类任务mAP50 跑到 0.9 以上算正常。如果 cls_loss 一直不降大概率是类别索引和names对不上回去查data.yaml。3.4 验证与推理训练完在tennis_runs/exp1/weights/best.pt拿到最优权重。验证命令yolo detect val \ modeltennis_runs/exp1/weights/best.pt \ data./tennis_dataset/data.yaml \ imgsz640推理单张图yolo detect predict \ modeltennis_runs/exp1/weights/best.pt \ source./test_image.jpg \ conf0.25 \ saveTrueconf0.25是置信度阈值低于这个值的框不显示。网球场景如果误检多调到 0.4 或 0.5如果漏检多降到 0.15。saveTrue会把画了框的图存到runs/detect/predict目录。4. 避坑排查标签、路径、显存这三处最容易翻车4.1 训练 loss 为 nan 或全零现象启动训练后 box_loss 直接是 nan或者几轮下来所有 loss 都是 0。原因通常是标签文件里有空行、坐标超出 0 到 1 范围或者某张图的 txt 里写了非法字符。解决写个脚本扫一遍所有 txt检查每行是否五个字段、坐标是否在 0 到 1 之间。import os lbl_dir labels/train bad [] for f in os.listdir(lbl_dir): if not f.endswith(.txt): continue with open(os.path.join(lbl_dir, f)) as fh: for i, line in enumerate(fh): parts line.strip().split() if len(parts) ! 5: bad.append((f, i, 字段数不对)) continue try: vals list(map(float, parts)) except ValueError: bad.append((f, i, 非数字)) continue if not all(0 v 1 for v in vals[1:]): bad.append((f, i, 坐标越界)) print(问题行数:, len(bad)) for b in bad[:20]: print(b)这段脚本逐行读标签先查字段数是不是 5再查后四个坐标是否都在 0 到 1 之间。parts[0]是类别索引不参与范围检查。输出问题行后手动修或者直接删掉对应行。4.2 训练时提示找不到标签现象终端反复打印No labels found或者 mAP 一直是 0。原因多半是data.yaml里的path写错了或者train路径指向的文件夹里只有图没有标签。解决先确认path是绝对路径或正确的相对路径再确认images/train旁边有labels/train且文件名主干一一对应。ultralytics 默认会在图像同级目录找labels文件夹如果目录结构是images/train和labels/train分开的data.yaml里不用额外配labels路径它会自动推导。4.3 显存溢出中断训练现象跑了几轮突然报CUDA out of memory。原因batch或imgsz设大了或者没开混合精度。解决先把batch减半还不行就把imgsz从 640 降到 416。另外加ampTrue开启自动混合精度能省不少显存。yolov8 默认就开 amp但有些环境需要手动确认。4.4 验证集 mAP 远低于训练集现象训练集 mAP 0.95验证集只有 0.6。原因数据集划分不均匀或者验证集里有些图训练时见过。这个数据集已经分好 train 和 val但如果你自己重新划分要确保同一段视频的连续帧不要跨集。网球比赛视频里相邻帧几乎一样随机划分会导致验证集泄漏。解决按视频来源或时间戳划分而不是随机抽。4.5 推理时框位置偏移现象预测出来的框整体偏左上或偏右下。原因训练时用了rectTrue做矩形推理但验证或推理时没开或者图像预处理 resize 方式不一致。解决训练和推理的imgsz保持一致推理时加rectTrue让长边对齐短边填充。如果还偏检查原图是否有 EXIF 旋转信息PIL 读图默认会应用旋转opencv 不会混用会导致坐标错位。5. 进阶技巧用这份数据做模型对比与标签格式转换5.1 同一份数据跑 yolov5 和 yolov8 的差异这个数据集标称兼容 yolov5 到 yolo11实际跑下来yolov8 的收敛速度比 yolov5 快大概 20%mAP50 高 1 到 2 个点。但 yolov5 的显存占用更低同样 8G 显存能跑更大的 batch。如果你想做对比实验建议固定imgsz640、epochs100、batch16只换模型权重。yolov5 用yolov5s.ptyolov8 用yolov8n.pt参数量接近对比公平。跑完把两个results.csv拉出来画曲线重点看第 50 轮之后的 mAP 走势。5.2 从 YOLO txt 转 VOC xml虽然数据集已经给了 xml但如果你要往其他框架迁或者想用 labelImg 可视化检查自己转一遍更放心。转换脚本核心是读图拿宽高再把归一化坐标乘回去。import os from PIL import Image import xml.etree.ElementTree as ET img_dir images/train lbl_dir labels/train out_dir voc_xml os.makedirs(out_dir, exist_okTrue) for f in os.listdir(lbl_dir): if not f.endswith(.txt): continue stem os.path.splitext(f)[0] img_path os.path.join(img_dir, stem .jpg) if not os.path.exists(img_path): continue w, h Image.open(img_path).size root ET.Element(annotation) ET.SubElement(root, filename).text stem .jpg size ET.SubElement(root, size) ET.SubElement(size, width).text str(w) ET.SubElement(size, height).text str(h) with open(os.path.join(lbl_dir, f)) as fh: for line in fh: cls, xc, yc, bw, bh line.strip().split() xc, yc, bw, bh map(float, (xc, yc, bw, bh)) xmin int((xc - bw / 2) * w) ymin int((yc - bh / 2) * h) xmax int((xc bw / 2) * w) ymax int((yc bh / 2) * h) obj ET.SubElement(root, object) ET.SubElement(obj, name).text tennis bnd ET.SubElement(obj, bndbox) ET.SubElement(bnd, xmin).text str(xmin) ET.SubElement(bnd, ymin).text str(ymin) ET.SubElement(bnd, xmax).text str(xmax) ET.SubElement(bnd, ymax).text str(ymax) ET.ElementTree(root).write(os.path.join(out_dir, stem .xml))Image.open(img_path).size返回(宽, 高)顺序别搞反。归一化坐标转绝对坐标的公式是(中心 - 宽/2) * 图宽得到左上角(中心 宽/2) * 图宽得到右下角。ET.SubElement逐层建 XML 节点最后write落盘。跑完随便抽几个 xml 用 labelImg 打开框和原图对得上就说明转换正确。5.3 用 TensorRT 加速推理的注意点如果你要把模型部署到边缘设备TensorRT 是常见选择。yolov8 导出 engine 文件yolo export modeltennis_runs/exp1/weights/best.pt formatengine imgsz640 halfTruehalfTrue用 FP16 精度速度比 FP32 快接近一倍精度掉得很少。导出后的.engine文件绑定特定显卡架构换卡要重新导。推理时yolo detect predict modelbest.engine sourcexxx第一次加载会慢因为要反序列化。有个坑TensorRT 对动态 batch 支持有限导出时如果没指定dynamicTrue推理时 batch 只能为 1。网球视频流场景一般 batch 1 也够用但如果你要批量处理导出时加上dynamicTrue并指定batch8。从那以后我每次拿到新数据集都强制先跑一遍标签配对检查和坐标范围扫描再启动训练。这两步花不到两分钟但能省掉后面几小时的无效等待。希望帮到你。本文还有配套的精品资源点击获取