简介:这份数据集面向计算机视觉开发者与溺水检测算法研究者,提供游泳者与溺水两类目标的检测训练素材,可用于水域安全监控、智能救生等场景的模型训练与验证。资源共2000个文件,包含895张jpg图片、895个VOC格式xml标注文件、897个txt文件(含YOLO格式标注及类别说明),压缩包约315.71MB,采用7z格式打包,兼容Pascal VOC与YOLO两种主流训练流程。标注由labelImg完成,共2类:swimmer标注框1433个,drowning标注框97个,总框数1530,数据从30段视频中截取标注。目前已有1886人学习下载。需要特别注意的是,溺水状态本身难以确认,建议下载后按自身标准重新校正标注;若仅需原始视频素材,也可通过资源页获取配套视频文件,便于自行截帧扩充样本。
1. 895 张游泳者溺水数据集:VOC 与 YOLO 双格式到底怎么选
如果你正在做水域安全监控、泳池防溺水预警或者公开水域救援辅助系统,大概率绕不开一个现实问题:公开的溺水相关数据集太少了。落水姿态、水面反光、人体半淹没、遮挡严重,这些场景在通用 COCO 或 VOC 数据集里几乎找不到足够样本。这份游泳者溺水数据集给的是 895 张图像、2 个类别,同时提供 VOC 和 YOLO 两套标注格式,属于典型的“小而专”的检测数据集。它解决的不是通用目标检测问题,而是让你在溺水/非溺水这个二分类检测任务上有一个能直接跑通训练流程的起点。适合谁?适合已经搭好 YOLO 训练环境、想快速验证溺水检测可行性的人,也适合拿它做数据增强和迁移学习实验的从业者。895 张不算多,但双格式省去了你自己写转换脚本的功夫,这一点在实际项目里比想象中值钱。
2. VOC 与 YOLO 双格式拆解:标注结构、类别映射与选型逻辑
2.1 两种格式的文件组织差异
VOC 格式的核心是Annotations目录下的 XML 文件,每张图对应一个 XML,里面记录filename、size、object列表,每个 object 包含name、bndbox(xmin、ymin、xmax、ymax)。YOLO 格式则是labels目录下的 txt 文件,每行一个目标,格式为class_id x_center y_center width height,全部是归一化到 0~1 的相对坐标。这份数据集同时给了两套,意味着你可以直接用 YOLO 的 txt 跑训练,也可以用 XML 做数据分析或转成 COCO、CSV 做统计。
常见做法是:先看labels里的类别索引对应关系。2 类别通常是0和1,但具体哪个是溺水、哪个是非溺水,必须去classes.txt或data.yaml里确认。我一般会先跑一段统计脚本,把每类的框数量、每张图的平均目标数、宽高分布拉出来,避免训练完才发现类别映射反了。
2.2 类别不平衡与图像尺寸检查
895 张图、2 类别,大概率存在类别不平衡。溺水样本可能远少于非溺水样本,或者反过来。先统计:
import os from collections import Counter label_dir = "labels/train" counter = Counter() img_with_obj = 0 for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f)) as fp: lines = [l.strip() for l in fp if l.strip()] if lines: img_with_obj += 1 for line in lines: cls_id = int(line.split()[0]) counter[cls_id] += 1 print("类别分布:", counter) print("有目标图像数:", img_with_obj)这段代码遍历labels/train下所有 txt,统计每个类别的框数量和有目标的图像数。如果某一类框数占比低于 20%,训练时就要考虑用copy_paste、mosaic或者类别权重来缓解。参数上,label_dir换成你的实际路径即可,YOLOv5/v8 的标签目录通常和图像目录平级。
图像尺寸也要查。VOC XML 里有width和height,YOLO 归一化坐标反推也能得到。如果图像长宽比差异大,比如有的 1920×1080、有的 640×480,直接 resize 到 640×640 会带来形变。常见做法是保持长边缩放、短边 padding,YOLO 训练时rect=True可以缓解,但数据量小的时候更建议统一预处理成方形。
2.3 选 VOC 还是 YOLO:看你的下游框架
如果你用 YOLOv5/v8/v11 训练,直接用 YOLO 格式,配一个data.yaml:
path: ./swimmer_drown train: images/train val: images/val nc: 2 names: ['drowning', 'non_drowning']nc是类别数,names顺序必须和 txt 里的class_id一致。如果拿 VOC XML 去跑 MMDetection 或 Detectron2,就用 VOC 格式,但要注意 VOC 默认类别名和你的任务是否匹配。这份数据集给了双格式,省掉了xml2txt的转换步骤,但转换脚本本身不难,难的是转换后坐标对齐和类别映射不出错。我一般会保留 VOC 做数据审计,YOLO 做训练输入,两边交叉验证。
提示:不要混用两套格式的类别顺序。VOC XML 里的
name是字符串,YOLO txt 里是整数索引,一旦映射错,训练 loss 会正常下降但 mAP 极低。
3. 用 YOLOv8 跑通溺水检测:环境、配置与训练命令
3.1 环境搭建与依赖版本
YOLOv8 用 ultralytics 包,Python 3.8+,PyTorch 1.8+。常见做法是建虚拟环境:
conda create -n drown python=3.10 -y conda activate drown pip install ultralytics opencv-python matplotlibultralytics会自动装 torch、torchvision。如果你有 CUDA,确认torch.cuda.is_available()返回 True。没有 GPU 也能跑,但 895 张图在 CPU 上训练会非常慢,建议至少一张 8G 显存的卡。V100、3090、4090 都行,batch size 根据显存调,8G 卡用batch=8或batch=16,24G 卡可以上batch=32。
3.2 数据目录组织与 data.yaml 配置
YOLO 训练要求目录结构清晰:
swimmer_drown/ images/ train/ val/ labels/ train/ val/ data.yaml把数据集里的图像按 8:2 或 7:3 分到 train/val,对应的 txt 也放好。如果原始数据没有划分,用脚本随机分:
import os, random, shutil img_dir = "images/all" lbl_dir = "labels/all" train_ratio = 0.8 random.seed(42) files = [f for f in os.listdir(img_dir) if f.endswith(('.jpg', '.png'))] random.shuffle(files) split = int(len(files) * train_ratio) for i, f in enumerate(files): subset = "train" if i < split else "val" shutil.copy(os.path.join(img_dir, f), f"images/{subset}/{f}") lbl = os.path.splitext(f)[0] + ".txt" src_lbl = os.path.join(lbl_dir, lbl) if os.path.exists(src_lbl): shutil.copy(src_lbl, f"labels/{subset}/{lbl}")random.seed(42)保证可复现,train_ratio控制比例。注意图像和标签必须同名,否则 YOLO 会跳过。划分后检查labels/val里有没有空文件,空文件表示该图无目标,YOLO 默认会忽略,但最好确认一下是不是漏标。
3.3 训练命令与关键参数
yolo detect train \ data=swimmer_drown/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/drown \ name=exp1model=yolov8n.pt是轻量版预训练权重,数据量小的时候用 n 或 s 就够了,x 版容易过拟合。epochs=100配合patience=20,20 轮没提升就早停。lr0=0.01是初始学习率,小数据集可以降到 0.005。imgsz=640是输入尺寸,如果原图分辨率很高且小目标多,可以上 1280,但显存翻倍。训练完在runs/drown/exp1/weights/下拿best.pt。
3.4 验证与指标解读
yolo detect val \ model=runs/drown/exp1/weights/best.pt \ data=swimmer_drown/data.yaml \ imgsz=640重点看mAP50和mAP50-95。二分类任务里,如果某一类 AP 接近 0,先查类别映射,再查该类样本数。混淆矩阵在runs/drown/exp1/下,看溺水类有多少被误判成非溺水。895 张图训练出来的模型,mAP50 能到 0.7 以上就算正常,低于 0.5 大概率是标注或划分有问题。
4. 避坑与排查:895 张小数据集训练最常见的 5 个翻车点
4.1 现象:loss 正常下降但 mAP 一直是 0
原因:YOLO txt 里的class_id从 1 开始,而 YOLO 要求从 0 开始。VOC 转 YOLO 时如果没减 1,所有类别索引偏移,模型学到的类别和data.yaml对不上。
解决:检查 txt 第一列,确认最大值是nc-1。如果是 1 和 2,全部减 1。批量修正:
python -c " import os for f in os.listdir('labels/train'): if f.endswith('.txt'): p = os.path.join('labels/train', f) lines = open(p).readlines() new = [] for l in lines: parts = l.split() parts[0] = str(int(parts[0]) - 1) new.append(' '.join(parts) + '\n') open(p, 'w').writelines(new) "4.2 现象:训练时提示 “No labels found”
原因:图像和标签路径不匹配,或者data.yaml里的train指向了图像目录但标签目录名不是labels。YOLO 默认在图像路径里把images替换成labels,如果目录结构不是标准命名就会找不到。
解决:要么改成标准images/和labels/结构,要么在data.yaml里显式写train: images/train和val: images/val,并确保同级有labels目录。用yolo detect train前先跑yolo detect val看能不能加载数据。
4.3 现象:验证集 mAP 波动大,每次训练结果差很多
原因:895 张图划分验证集后可能只有 100 多张,随机划分导致某次验证集里溺水样本特别少。小数据集上这种波动是玄学,但可以通过固定随机种子和分层抽样缓解。
解决:划分时按类别分层,保证 train/val 里两类比例接近。用sklearn.model_selection.train_test_split的stratify参数,或者手动按类别分组再抽。另外训练时设seed=42,deterministic=True。
4.4 现象:模型把水面反光、泳池边沿误检成溺水
原因:负样本不够多样,或者标注时把非溺水目标也框进去了。895 张里如果背景单一,模型会学到虚假关联。
解决:加负样本,或者用copy_paste增强把目标贴到不同背景。YOLOv8 支持copy_paste=0.3,但小数据集上增强太猛会过拟合。更稳的做法是手动收集一些泳池、水面、人群的负样本图,不放标签,让模型学会抑制。
4.5 现象:训练到一半 BN 层崩溃,loss 变 NaN
原因:batch size 太小,BN 统计量不稳定。8G 卡上batch=4或batch=8时容易出现,尤其是图像尺寸 640 以上。
解决:换batch=16以上,或者用model=yolov8n.pt时冻结 backbone 前几层。YOLOv8 默认用bn,如果显存不够,可以改用rect=True减少 padding,或者降imgsz=512。实在不行换 GroupNorm,但 YOLO 官方不直接支持,需要改源码。
5. 小数据集的进阶玩法:迁移学习、增强策略与推理部署
895 张图直接训 from scratch 基本没戏,必须用预训练权重。yolov8n.pt在 COCO 上预训练过,已经学到了边缘、纹理、人体部件特征,迁移到溺水检测上收敛快很多。我一般会先冻结 backbone 训 20 轮,再解冻全量微调 80 轮。冻结用freeze=10,表示冻结前 10 层。
yolo detect train \ data=swimmer_drown/data.yaml \ model=yolov8n.pt \ epochs=20 \ freeze=10 \ lr0=0.001 \ batch=16 \ project=runs/drown \ name=freeze_stage yolo detect train \ data=swimmer_drown/data.yaml \ model=runs/drown/freeze_stage/weights/best.pt \ epochs=80 \ lr0=0.0005 \ batch=16 \ project=runs/drown \ name=finetune_stage冻结阶段学习率低一点,避免破坏预训练特征。微调阶段用更小的学习率,配合余弦退火。增强策略上,hsv_h=0.015、hsv_s=0.7、hsv_v=0.4是默认值,小数据集可以适当加大hsv_v到 0.5,模拟不同光照。degrees=10做小角度旋转,translate=0.1、scale=0.5做平移缩放。mosaic=1.0默认开,但最后 10 轮建议关掉,让模型适应真实分布。
推理部署时,best.pt可以直接用:
from ultralytics import YOLO model = YOLO("runs/drown/finetune_stage/weights/best.pt") results = model.predict("test.jpg", conf=0.25, iou=0.45) for r in results: for box in r.boxes: cls = int(box.cls) conf = float(box.conf) xyxy = box.xyxy.tolist() print(f"类别: {model.names[cls]}, 置信度: {conf:.2f}, 框: {xyxy}")conf=0.25是置信度阈值,溺水检测建议调低到 0.2 提高召回,宁可误报不可漏报。iou=0.45是 NMS 阈值,目标密集时调低。如果部署到边缘设备,用yolo export format=onnx导出 ONNX,再用 TensorRT 或 OpenVINO 加速。
最后说一个血泪经验:小数据集上不要迷信高 mAP,一定要拿真实场景视频抽帧测试。我见过验证集 mAP50 到 0.85 的模型,在实拍视频里因为水面反光和镜头畸变直接崩掉。从那以后我每次训完都强制走一遍视频推理,看连续帧的检测稳定性,再决定要不要上线。希望帮到你。
本文还有配套的精品资源,点击获取