YOLO溺水检测数据集:从YOLOv5训练到避坑指南
简介面向溺水检测场景的YOLO系列目标检测数据集涵盖溺水、出水、游泳等典型状态适用于使用YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等算法进行模型训练与验证。压缩包内共1018个文件大小为14.6MB包括339张JPG原始图像以及对应的339个XML与339个TXT标注文件另附1个YAML配置文件。TXT文件采用YOLO格式记录归一化的目标框坐标XML文件则按照VOC格式保存便于不同算法框架直接读取数据集已预先划分好训练与验证集下载后即可开始训练和测试。目前已有302人学习使用适合目标检测学习者、算法研究人员以及智慧安防项目开发者快速构建溺水检测demo或完善安全监控方案。1. 溺水检测数据集训练 YOLO 系列模型的第一道门槛做溺水检测的同行应该都有体会模型结构再先进没有干净、带标准标签的数据集训练出来的东西就是黑匣子测试集上跑得欢到了真实泳池场景就翻车。这份 yolo 溺水数据集共 339 张标注图像覆盖出水、溺水、游泳三类场景且同时提供 YOLO 格式txt和 VOC 格式xml标签省去了自己写转换脚本的功夫。对于要快速验证 YOLOv5、YOLOv8、YOLOv9、YOLOv10、YOLO11 的检测效果或者做算法对比实验的从业者来说这份数据可以直接喂给训练管道不用在数据清洗和格式转换上耗时间。新手拿来熟悉训练流程老手拿来当基准集都合适。下面从标签格式、目录结构、训练配置到避坑完整拆一遍。2. 双标签格式YOLO 与 VOC 坐标系的换算逻辑这份数据集最方便的地方是两种标签格式都齐了YOLO 用的 txt 归一化坐标VOC 用的 xml 绝对像素坐标。理解两者之间的对应关系能让你在切换框架或做数据增强时少踩一半的坑。2.1 YOLO 格式标签的归一化坐标打开任意一个 txt 标签文件比如img_0700_282.txt内容长这样0 0.482031 0.416667 0.105469 0.180556 0 0.730469 0.444444 0.085938 0.158333 1 0.350000 0.538194 0.090625 0.187500每一行对应一个目标框五个字段分别是class x_center y_center width height。class 是类别索引从 0 开始x_center 和 y_center 是框中心点相对图像宽高的比例width 和 height 同理都是归一化到 0~1 的小数。我的理解是这套归一化设计就是为了跟图像实际分辨率解耦。模型训练时输入尺寸是 640×640但原始图像可能是 1280×720 或 1920×1080只要标签是比例制任意缩放都不会导致框偏移。这点要注意如果你拿到的是像素坐标比如 XML在训练前必须手动归一化否则 loss 会异常大且不收敛。2.2 VOC 格式的 xml 像素坐标对应的 VOC 标签里坐标是绝对的annotation filenameimg_0700_282.jpg/filename size width1280/width height720/height /size object namedrowning/name bndbox xmin587/xmin ymin224/ymin xmax722/xmax ymax354/ymax /bndbox /object /annotation从 xml 转 yolo 的换算公式是x_center (xmin xmax) / 2 / widthy_center (ymin ymax) / 2 / heightw (xmax - xmin) / widthh (ymax - ymin) / height。转换脚本一般用 xml.etree 解析三分钟就能写完。2.3 双格式文件组织方式这份数据集的标签分文件夹存放YOLO 格式的 txt 和 VOC 格式的 xml 分别在两个目录下图像在第三个目录。训练时关键是把图像和 txt 放对应路径下类别映射文件按 txt 中的索引顺序写。常见做法是data.yaml里这样指定train: ./images/train val: ./images/val nc: 3 names: [swimming, drowning, emerging]需要补充的是VOC xml 里的name可能是swimming、drowning、emerging这类字符串而 YOLO 用索引所以训练前先跑一遍标签检查脚本确认索引和 names 对应避免类别标签错位导致模型学了个寂寞后面避坑章也会再提到。3. 用这份数据集跑通 YOLOv5 训练目录结构到命令行拿到这份资源第一件事不是急着训练而是把目录整理到 YOLO 仓库预期结构里。YOLOv5 和 YOLOv8 对数据集目录约定略有差异YOLOv5 是images和labels同级YOLOv8 也沿用这套约定所以这里以 YOLOv5 为主线展开。3.1 数据集目录规范化数据集本身已经划分好 train 和 val原始结构大概是这样的drowning_dataset/ ├── images/ │ ├── train/ │ │ ├── img_0700_282.jpg │ │ └── ... │ └── val/ │ ├── img_0700_21.jpg │ └── ... ├── labels_yolo/ │ ├── train/ │ │ ├── img_0700_282.txt │ │ └── ... │ └── val/ │ └── ... └── labels_voc/ ├── train/ │ ├── img_0700_282.xml │ └── ... └── val/ └── ...需要做的是把labels_yolo复制一份或软链为labels目录保持和 images 一样的 train/val 划分。用一条命令可以完成cd drowning_dataset cp -r labels_yolo labels这里复制而不是改名是为了保留 VOC 原文件夹方便后续做数据增强或转化回 VOC 验证。常见的做法是先把 labels 和 images 放到同一父目录下再改data.yaml路径不用把图片和标签混在一个目录里。3.2 data.yaml 配置与训练命令在 YOLOv5 根目录下新建drowning.yamltrain: /absolute/path/to/drowning_dataset/images/train val: /absolute/path/to/drowning_dataset/images/val nc: 3 names: [swimming, drowning, emerging]注意 train 和 val 路径指向的是 images 目录YOLOv5 会自动在同级找 labels 目录。建议使用绝对路径避免相对路径在多级目录下解析出错。接下来直接开始训练python train.py --img 640 --batch 16 --epochs 100 \ --data drowning.yaml --weights yolov5s.pt --name drowning_v5s--img 640是输入分辨率--batch 16视显存调整8GB 显存跑 s 模型建议 batch 不超过 16--epochs 100对于 339 张图来说已经够用模型小、数据量不大100 轮能收敛得很充分。--weights yolov5s.pt用 COCO 预训练权重做迁移学习比从头训练少一半时间还能提点。3.3 类别不均衡与初始训练结果评估训练 100 轮后看runs/train/exp/下的results.csv主要关注mAP0.5和mAP0.5:0.95两个值。如果mAP0.5在 0.7 以上说明基础模型已经可用如果低于 0.5先检查标签是否有误再考虑调参。常见的问题是类别分布不均比如 drowning 样本少。可以用utils/loss.py里的plot_labels功能或者直接统计 txt 文件里每类目标数量cat labels/train/*.txt | awk {print $1} | sort | uniq -c输出如果显示类别 0 有 400 个框类别 1 只有 100 个可以考虑数据增强或增加对应类别权重避免模型偏向多数类。3.4 数据增强对溺水检测的适配YOLOv5 默认开了马赛克增强和随机仿射变换这对溺水场景其实很关键。泳池水面的反光、波纹、水花对检测器来说都是干扰增强等于变相扩充了样本多样性。# hyp.scratch-low.yaml 中对溺水检测比较关键的几项 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 flipud: 0.5 mosaic: 1.0flipud 水平翻转对于水面倒影、游泳姿势有一定帮助mosaic 增强的小目标拼接对远处游泳者检测有益。参数值一般保持默认不需要额外改动除非你发现模型在特定角度下频繁漏检。4. 避坑与排查溺水场景检测翻车的几个高频原因这份资源本身能少踩很多坑但实际操作中环境问题、标签问题才是新手最容易卡住的地方。结合常见实操经验把必踩的坑列出来。4.1 第一个坑标签索引与 names 顺序不对应现象训练时 mAP 全程极低甚至接近 0但 loss 在正常下降。原因txt 标签里类别索引是 1 的开头却对应着names里的第一个类别swimming而实际想表示的是drowning导致模型学反了类别映射。解决先统计全部 txt 文件里出现过的 class 索引值再逐个打开几个 xml 核对name字段。确保names列表顺序跟索引完全对应假设 xml 中显示类别 0 是 swimming但你的 names 第一个是 drowning那就需要交换 names 顺序或者写脚本统一批量改动标签索引。注意改完重新统计类别数量看是否均衡。4.2 第二个坑训练时标签全部报错提示找不到对应文件现象训练命令启动后直接报错image 1/339 ... WARNING: label path not exist。原因YOLOv5 找标签的机制是将 images 路径下的images字段替换为labels。如果数据集目录结构里没有 labels 文件夹或者标签文件命名与对应图像不一致就会全报错。解决严格按第三节的目录结构操作。先把labels_yolo复制为labels再检查每个图像和 txt 的 basename 是否完全一致常见的坑是图片是img_0700_282.jpg标签却写成了img_0700_282.txt多空格这类低级问题用ls对比就能发现。4.3 第三个坑救援人员被识别成溺水者现象验证集上 precision 高但 recall 低漏检多现场误报也多。原因数据集中没有单独标注“救生员/旁人”类别只有游泳、溺水、出水三类。救生员坐在池边或站在水中姿势跟溺水者相似模型被判成溺水是逻辑必然。解决这个值得多说两句。最直接的办法是在训练集里补充包含救生员、泳池边站立的行人、休息区游客的验证图像并新增一个 background 或 passerby 类别或者直接把救生员的标签从训练集中剔除避免干扰。没有额外数据的话就先调低置信度阈值用 NMS 后的框面积和长宽比做筛选过滤锚框再结合帧间运动方向做时序判断。4.4 第四个坑VOC 转 YOLO 时坐标越界现象自己写了脚本从 xml 转 txt训练时 YOLOv5 警告WARNING: invalid bbox检查发现部分框坐标是负数或大于 1。原因xml 里个别 bndbox 超出了图像边界通常是标注时手滑拖出画布或者图像本身有黑边而标注包含黑边范围。归一化后就会出现负值或超 1 的值。解决写转换脚本时加一层 clamp强制边界约束x_center min(max((xmin xmax) / 2 / width, 0.0), 1.0) y_center min(max((ymin ymax) / 2 / height, 0.0), 1.0) box_w min(max((xmax - xmin) / width, 0.0), 1.0) box_h min(max((ymax - ymin) / height, 0.0), 1.0)即便这份数据已经标好也建议跑一遍检查脚本把越界框过滤掉再训练减少意外出错。4.5 第五个坑339 张图做训练模型过拟合现象训练 loss 很低但验证集 mAP 波动大单次跑出的结果不稳定。原因339 张图、三类目标数据量偏少模型容易记住训练集的细节而不是泛化出“溺水”这个概念。解决数据不够增强来凑。用--augment开启测试时增强TTA或者增加训练时的随机 erasing、cutout都能提升鲁棒性。也可以直接跑 YOLOv5 的--cache预加载数据减少 I/O 波动让训练更稳定。必要的时候用 K 折交叉验证评估模型稳定性。5. 模型评估与验证从 mAP 到真实场景数据衡量训练完成只是第一步关键是能不能拿到一份可信的评估报告。这章说清楚验证手法和参数解读。5.1 验证命令与输出指标解读训练完后用自带验证脚本跑一遍python val.py --data drowning.yaml --weights runs/train/drowning_v5s/weights/best.pt --img 640 --task val输出指标中mAP0.5是 IoU 阈值为 0.5 时的平均精度mAP0.5:0.95是 COCO 标准中的严格指标对定位要求更高更能反映模型实用性。溺水检测场景下建议两个指标都看尤其关注mAP0.5:0.95因为水下人体与背景对比度低框的定位不精确会影响后续追踪或报警的准确性。5.2 每类别的 PR 曲线单独分析如果整体 mAP 可以但是某一类很低比如 drowning 只有 0.4说明这一类数据过少或特征不明显。手动把runs/val/exp/PR_curve.png拉出来看per-class 的 PR 曲线会直观暴露哪一类是短板。针对表现差的类别优先补充样本其次调整 loss 的类别权重。5.3 实际推理测试部署预演评估指标是参考真实视频流才是试金石。用训练好的模型跑一段泳池监控视频观察连续帧的稳定性python detect.py --weights runs/train/drowning_v5s/weights/best.pt --source demo.mp4 --conf 0.35 --iou 0.5 --view-img--conf 0.35比默认 0.25 严格一点可以过滤一部分背景误检--iou 0.5是 NMS 的 IoU 阈值对于遮挡严重的溺水场景建议调低到 0.3 防止相邻框被合并吞掉。水花干扰、救生员走动、光照突变这些非理想条件在静态图上看不出来跑视频能筛掉大部分不落地的模型。6. 进阶用难例挖掘迭代提升溺水检测的报警稳定性跑通训练和评估后想要让模型在真实场景中稳定运行最值得投入的是难例挖掘和持续迭代。我自己的习惯是前向推理后把置信度低于 0.5 但确实有溺水行为的样本截取出来人工复核后加入训练集。操作逻辑是先跑一次推理用脚本把所有漏检或置信度偏低的检测结果导出import cv2 import torch from pathlib import Path model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/drowning_v5s/weights/best.pt) video_path Path(demo.mp4) results model(video_path, augmentFalse) results.pandas().xyxy[0].to_csv(detections.csv, indexFalse)接着筛选出 detection 置信度低于 0.3 且没有框的帧统一抽帧。import pandas as pd df pd.read_csv(detections.csv) hard_frames df[df[confidence] 0.3][frame_id].unique() for fid in hard_frames: # 抽帧并另存为 hard_example_{fid}.jpg pass将这些难例图像重新标注格式与原始数据集一致合并到原训练集重新训练repeat 两到三轮模型对复杂水面的误检率通常会有肉眼可见的下降。关键参数是置信度阈值的选择报警灵敏度优先就调低到 0.2误报优先就拉高到 0.5没有绝对值只有业务权衡。利用这份数据集把模型基线、验证流程、难例迭代全部打通之后再遇到新的场景视频问题就不再是“模型能不能识别溺水”而是“当前环境的干扰项有哪些如何快速采集样本补齐”。每次换场地我都要用这套流程走一遍难例挖掘再上报告警希望能帮到同样卡在数据到报警这最后一步的你。本文还有配套的精品资源点击获取