简介面向水下机器人、海洋环境监测与计算机视觉算法研究者这套YOLO海洋水下垃圾检测数据集提供了真实水域场景的高质量图像资源。压缩包共含7667张jpg原图配套7667个xmlVOC格式与7668个txtYOLO格式标注文件整体约174MB标签文件与图像一一对应按两种格式分目录存放便于直接切换训练框架。数据针对金属、木材、塑料、橡胶、布料等多类海洋垃圾目标进行框选标注涵盖不同光照、浑浊度及水底杂物背景场景丰富且贴近实战用户既可直接基于YOLO系列模型训练也可将VOC格式转换后用于其他检测框架。目前已有2080人学习下载适合作为算法课程设计、毕业论文实验或海洋环保竞赛的基准数据集能显著减少水下垃圾图像采集与标注的时间投入将更多精力集中在模型优化与检测精度提升上。1. YOLO水下垃圾检测数据集双格式标注与训练流程解析做水下机器人或近海监测项目时最先卡住进度的往往不是模型结构而是训练数据。水下光照偏色、悬浮颗粒遮挡、目标形变严重一个矿泉水瓶在陆地上是清晰的矩形框到了水下就变得半透明、扭曲颜色和背景几乎融在一起。这份YOLO算法海洋水下垃圾检测数据集的做法比较省事同一批图片同时给出VOC和YOLO两套标注分别保存在独立文件夹里几千张经过labelimg逐一核对过的真实场景图片类别覆盖metal、wood、plastic、rubber、cloth这几类常见垃圾材质。它解决的问题很直接——让你跳过硬啃VOC转YOLO的格式转换直接进入yolov5或yolov8的训练流程也适合需要快速验证检测算法在水下场景表现的研究人员做基准测试。2. 数据集结构与标注格式把VOC和YOLO两套标签一次看清2.1 图片、XML与TXT三种文件的对应关系下载解压后第一件事别急着开训练先把目录结构摸清楚。常见做法是数据按如下方式组织dataset/ ├── images/ # jpg原图真实水下场景 ├── VOC/ │ └── label/ # VOC格式标注每张图对应一个xml ├── YOLO/ │ └── label/ # YOLO格式标注每张图对应一个txt └── classes.txt # 类别清单按id顺序排列注意这只是最常见的一种划分方式不同打包者可能把文件夹命名为Annotations、labels之类但逻辑一致图片、VOC标注、YOLO标注三套文件并列classes.txt写明类别顺序。项目正文里那串obj1602_frame0000087.txt就是YOLO标注文件文件名与图片名完全一致仅后缀不同。这个命名规律在写数据加载脚本时可以直接利用扫描图片目录后做一次后缀替换就能拿到对应标注路径不用再额外维护一张映射表。每张jpg在VOC目录里对应同名xml在YOLO目录里对应同名txt。xml记录物体类别和绝对像素坐标xmin、ymin、xmax、ymaxtxt记录归一化后的类别id和中心点坐标class_id、x_center、y_center、width、height这就是YOLO系框架原生读取的格式。两套格式的差异我用一张表列出来维度VOC格式YOLO格式文件后缀xmltxt坐标记录xmin、ymin、xmax、ymax绝对像素x_center、y_center、width、height归一化值类别记录标签名如metal、wood类别id整数从0开始读取方labelimg可复核xml解析直观YOLO训练脚本原生读取为什么作者要同时保留两套格式因为labelimg在标注时就能同步导出这两种格式顺手把两份都放进了包里。VOC格式的价值在复核效率打开xml能看到类名和坐标范围适合人工抽查YOLO格式的价值在训练效率直接把txt目录填进数据配置就能跑不用每次先转换。对做数据清洗的人来说我建议以VOC为主、YOLO为辅——检查标注对不对、要不要删掉某个框用VOC更直观确认没问题后训练时直接用YOLO那份。还有个容易忽视的检查点三个目录下的文件名集合必须一致。常见做法是写个小脚本对比一下找出缺xml或缺txt的图片因为有些工具在标注为空时会生成空文件有些干脆不生成。这个动作能在训练前暴露数据缺失问题省得后面训练报出“found no labels”才回头找。2.2 五个类别的标签分布与场景特点目标类别是metal、wood、plastic、rubber、cloth覆盖水下最常见的垃圾材质。从实际拆包经验看metal类大多是易拉罐、铁丝、瓶盖wood类多见断木和板材plastic类以瓶子、包装袋、塑料袋碎片为主rubber类是轮胎和橡胶管cloth类主要是渔网、绳索和布料碎片。这五个类别有个共同的训练难点形态差异极大。金属反光强烈塑料半透明且容易变形布料柔软所以框通常很紧橡胶颜色偏深容易淹没在背景里。同一个“plastic”类目下硬塑料瓶和软塑料袋的外观差别可能比塑料和布料的差别还大这也是水下检测mAP上不去的一个重要原因。场景方面数据来自真实水下拍摄而不是合成图因此包含光照偏色、悬浮颗粒、目标部分遮挡等真实干扰。“场景丰富”不是客套话不同水深、不同水质、不同底质沙地、礁石、海草区下同一类物体的外观差异相当明显。我见过一些合成数据集做得再逼真模型迁移到实拍视频上mAP还是掉一大截问题就出在训练分布和真实分布差太远。这份数据在这点上让人放心至少不用担心训练集和测试集之间存在明显的风格鸿沟。需要提醒的是五个类别的实例数大概率不均衡。水下垃圾检测的常见现象是plastic类大量出现rubber和cloth偏少。类别不均衡会直接反映在mAP上——少数类在验证集里可能只有几十个框任何一个误检都能让它的AP掉好几个点。所以训练前先做一次统计心里有数比闷头调参有用得多。2.3 用脚本盘点数据规模与类别平衡训练前我先跑一个统计脚本看每类标注框数量和各图片的标注密度。直接用YOLO格式的txt统计逻辑最简单import os from collections import Counter label_dir YOLO/label file_count 0 box_count 0 cls_counter Counter() for fname in sorted(os.listdir(label_dir)): if not fname.endswith(.txt): continue file_count 1 with open(os.path.join(label_dir, fname), encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 5: continue # 跳过空行或残缺标注避免int()报错 cls_counter[int(parts[0])] 1 box_count 1 print(标注文件数:, file_count) print(总框数:, box_count) print(类别分布:, dict(sorted(cls_counter.items())))这段代码的核心是逐行解析txt用Counter统计每个类别id出现的次数。len(parts) 5 的判断在过滤异常行一个完整目标行必须有类别id、中心点x、中心点y、宽、高五个字段少于五个说明标注文件有问题直接跳过比让程序崩溃好。跑完后如果发现某个类别的数量比最多的那类少一个数量级后续训练就要重点盯它的AP必要时单独补充该类数据或用loss权重平衡。统计的同时我还会顺手算一下每张图的平均框数和框的面积分布。平均框数能反映图片拥挤程度——水下垃圾经常多个目标堆在一起如果大部分图都有三四个以上的框增强策略里就不要把mosaic开得太大否则小目标被裁掉一半的概率很高。3. 把VOC转成YOLO格式转换脚本与四个边界坑3.1 为什么同时保留两套格式以及什么时候必须自己转这份数据集已经备好双格式直接能用。但真实项目里常遇到的情况是队友给了一份纯粹的VOC标注或者你自己在labelimg里标注时只导出了xml。这时候就得自己转。所以转换脚本不是白写的——它是你的后悔药拿到任何VOC格式数据都能用同一套流程处理。转换的原理一句话就能讲清把VOC里的绝对像素框xmin、ymin、xmax、ymax除以图片宽高变成0到1之间的归一化值再转成YOLO需要的中心点和宽高x_center、y_center、width、height。类别名也要映射成整数id。注意YOLO的类别id从0开始和classes.txt里的行号对齐这个细节和后面要讲的坑一直接相关。3.2 一个可复用的批量转换脚本我通常把转换脚本写成这样支持批量处理整个目录import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, class_list): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_list: continue cls_id class_list.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 过滤宽或高小于1像素的异常框 if xmax - xmin 1 or ymax - ymin 1: continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: with open(out_path, w) as f: f.write(\n.join(lines)) class_list [metal, wood, plastic, rubber, cloth] xml_dir VOC/label out_dir converted_labels os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue xml_path os.path.join(xml_dir, fname) out_path os.path.join(out_dir, fname.replace(.xml, .txt)) voc_to_yolo(xml_path, out_path, class_list)逻辑上分三块第一块解析xml拿到图片宽高和每个目标的类别与框坐标第二块做坐标换算并把异常框过滤掉第三块遍历整个目录批量执行。参数说明里值得注意的几点类名在比较前做了strip()防止xml里混入空格或换行符导致匹配不上归一化保留6位小数精度足够训练过滤条件写在换算之前既能避免宽高为负也能避免生成除零错误。3.3 四个边界坑路径、越界、类别id、空文件这个脚本一开始写的时候我也翻过车踩过几个比较典型的坑列出来供参考。坑一类别id从1开始数。有同事的习惯是从1开始编号类别转出来的txt第一列是1、2、3训练时YOLO强制从0开始等于所有类别整体错位一位。现象就是训练能跑、loss能降但预测时把metal的框标成wood。解决方法是每次转换后随机抽几个txt用labelimg的YOLO模式打开核对类别id是否与classes.txt对齐。坑二坐标越界和宽高为零。xml里偶尔会出现xmax小于xmin的反向框或者宽高只有0.5像素的碎框。转换后w或h为负数时YOLO训练会直接报错或者静默地把它当背景忽略导致标注白白丢失。解决方法是像脚本里那样在转换时就把宽高小于1像素的框过滤掉再对归一化结果做一次clip(0, 1)兜底防止某个坐标写成1.05。坑三路径分隔符在Windows和Linux间不一致。在Windows上生成的路径是反斜杠写进data.yaml后在Linux服务器上跑反斜杠会被当成转义字符轻则路径解析失败重则直接找不到图片。解决方法是统一用正斜杠或在代码里用os.path但最后打印一次解析后的路径确认。坑四空标注文件处理。有些工具对没有目标的图片生成一个空txt有些则直接不生成tar。yolov8对空文件会报warning然后跳过但如果你在划分train/val时按文件名匹配空文件会导致图片没有标注训练时该图被当成纯背景数量多的话会明显误导模型。解决方法是统计后手动决定这些空标注图片是删除还是保留我一般只保留有目标的图片参与训练。4. 用YOLOv8训练水下垃圾检测模型环境配置到损失函数观察4.1 环境配置CUDA版本与ultralytics安装yolov8训练自己的数据集环境配置是第一道坎。“yolo v8 anaconda环境配置要求”这个搜索词出现频率很高卡住的人大多栽在同一个地方没先装PyTorch就直接装ultralytics结果装上了CPU版本。我的固定流程是用conda建独立环境、固定Python版本、先装PyTorch再装ultralyticsconda create -n yolo python3.9 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics安装完成后先验证GPU是否可用这一步别跳过python -c import torch; print(torch.cuda.is_available())输出True说明CUDA版PyTorch装好了输出False则多半是CUDA驱动版本与PyTorch的cu118不匹配或者机器上根本没有NVIDIA显卡。这类问题排查起来比较费时间我一般先用nvidia-smi看驱动支持的CUDA版本再选择对应的PyTorch安装源而不是随便装一个版本。ultralytics和onnxruntime、opencv这些依赖包之间偶尔也有版本冲突如果import时报依赖错误优先看报错堆栈里是哪个包不兼容用pip install “包名指定版本”回退即可。4.2 数据集yaml的写法与路径陷阱ultralytics训练前需要把数据组织成它期望的目录形态。常见做法是分成train和val两个子集图片放images目录、标注放labels目录dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml然后写data.yamltrain: dataset/images/train val: dataset/images/val nc: 5 names: [metal, wood, plastic, rubber, cloth]train和val的路径我建议在yaml里写相对路径但要确保从终端启动训练时的工作目录能解析到。更稳妥的做法是写绝对路径但注意整个项目移动后要同步修改yaml。nc和names是绑定的nc必须等于names列表的长度否则训练会直接报错或者类别数对不上names的顺序必须和classes.txt保持一致否则检测出来的类别名就是错位的。划分train/val时保证同一张图片的jpg和txt在对应目录里同名不要把一张图同时放进train和val这会导致验证集虚高。注意labels目录下的txt文件名必须与images目录下的jpg文件名完全一致只差后缀。yolov8按文件名匹配图片和标注多一个空格或少一个下划线都会导致该图变成无标注的纯背景样本。4.3 训练命令与损失函数曲线的观察重点数据准备好后用一条命令启动训练yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16解释一下参数modelyolov8n.pt是官方预训练权重适合在已有视觉特征上微调比从零训练收敛快很多imgsz640是训练输入尺寸水下小目标多的话可以升到1280batch16是批大小显存不够就降到8。训练时终端会打印每一轮的box_loss、cls_loss和dfl_loss很多人只盯着总loss看实际上要分开看box_loss反映定位误差cls_loss反映分类误差。水下场景里cls_loss迟迟不降最常见的原因是难分类别太多——半透明的塑料和布料的边界在低光下本来就模糊如果标注框又松模型根本学不到可区分的特征。mAP曲线也能看到同样的趋势但mAP是最终结果loss是过程信号。我一般会在训练跑到一半时看一眼P曲线和R曲线P高R低说明模型偏保守宁可漏检也不误检R高P低说明模型偏激进框出大量背景。水下垃圾检测的业务场景通常希望R优先——漏掉一个塑料瓶比误检一块石头代价更大。这时候可以在训练结束后调整置信度门限把模型的行为推向目标方向这个技巧在最后一章展开。epoch的设置也是新手容易踩的点。水下数据集几千张图100个epoch通常足够但如果你发现val_loss在50轮以后反而回升说明过拟合了应该减少epoch或加大数据增强。yolov8默认开了mosaic和mixup对水下这种背景复杂的场景很有帮助但如果目标本身很小过强的mixup会把目标混没这种情况建议适当降低增强强度。5. 训练避坑指南标注错位与mAP异常的5条排查记录这一章是我反复在同一个数据集上折腾积累下来的排查记录按现象、原因、解决的顺序写每条都是实际踩过的坑。5.1 现象训练中途报错提示标签文件读取失败原因数据里有空txt或只有一列数据的文件。yolov8读取标注时要求每行至少五个数字遇到空文件会报warning或跳过遇到残缺行可能直接抛错中断。这个情况在labelimg打标后只保存了类别、没画框的文件里很常见也可能是转格式时脚本漏输了坐标列。解决训练前统一做一次清洗。扫描所有txt把行数不足5列的行删掉把空文件单独拎出来计数。我习惯写一个清洗脚本同时把归一化坐标clip到0到1之间防止某个框的坐标写成1.05导致训练时越界报错。5.2 现象训练能跑完但预测时所有类别整体错位一位原因类别id对照表错位。最典型的就是classes.txt里塑料类被拼成platstic——这个拼写错误在真实数据里出现的频率比想象中高很多或者VOC标注里类名一会儿是wood一会儿是Wood大小写不统一。如果转换脚本里的class_list按标准拼写写那拼错的类别就会被静默跳过导致某些图缺标注。更隐蔽的情况是class_list顺序和训练yaml里的names顺序不一致txt里的id对应上另一个类。解决统一拼写和顺序。用脚本扫描所有xml里的name标签打印出所有出现的类别名再用统计图核对五类各自的框数。改完后重新生成txt并在训练前打印一次txt第一行的类别id和names对照确认id 0对应metal、id 1对应wood这样的映射没有错位。5.3 现象mAP0.5不低但实拍水下视频里漏检一大片原因验证集分布和实际使用场景分布不一致。训练时mAP是在同一批分布的数据上算的如果验证集里目标都比较大、遮挡少分数自然好看实际水下视频里光照不同、目标小、很多目标半埋在沙里模型没见过这种分布自然检测不到。另一层原因是置信度门限设得太高默认0.25在清晰场景下问题不大但在水下低对比度场景里许多正确框的置信度只有0.2左右被直接滤掉了。解决先别动网络结构和训练参数把置信度门限降到0.1或0.05再看检测结果。如果降门限后大量目标浮现说明模型本身学到了特征只是输出分数偏低如果降了也没反应说明训练分布确实缺这类样本需要补充数据。这一步能帮你快速区分是模型不会还是门限卡掉。5.4 现象小目标完全检测不到大目标框得不错原因输入尺寸太小或下采样过深。imgsz640时一个直径只有10像素的小塑料片经过多次下采样特征图上只剩不到1个像素检测头根本找不到它。水下垃圾里有大量这样的碎片目标这也是很多人用默认配置训练水下数据时小目标AP接近于0的原因。解决把imgsz提到1280训练和预测保持一致显存不够就降batch。另一个方案是启用yolov8的P2检测层或用带更强小目标特征的模型做辅助。实测在相同数据集上imgsz从640提到1280小目标AP通常能提升5个点以上代价是训练时间翻倍。5.5 现象loss曲线震荡不收敛mAP在某个值附近反复横跳原因学习率太高加batch太小或者标注里混入了不少错误框。yolov8默认的学习率对多数场景是稳的但水下数据标注质量参差如果某张图里10个框中有3个框标偏了模型就会在正确和错误的梯度方向上来回拉扯。训练日志里box_loss忽高忽低val mAP跟着一起抖就是典型的被脏数据带偏的信号。解决先用小学习率比如0.001跑一遍确认loss能平缓下降再逐步调回去。同时抽检loss最高的那批图片按loss从大到小排序打印图片路径人工看一眼是不是标注问题。这个按loss找脏数据的方法比全量复核高效得多我后来对每一份新数据集都会跑一遍。6. 验证技巧置信度门限调整与可视化检测6.1 用conf-thres把漏检和误检调到业务可接受的状态训练完的模型默认置信度门限是0.25对水下垃圾场景通常不合适。推理时显式指定门限yolo detect predict modelruns/detect/train/weights/best.pt \ sourcetest_images/ imgsz640 conf0.1 iou0.5conf0.1表示只保留置信度高于0.1的框iou0.5是NMS去重时的IoU阈值。水下低对比度场景里把conf从0.25降到0.1常能让召回率显著回升代价是误检增多。我一般先跑一遍0.25再跑0.1对比两批结果里的目标数量就能估算出模型的置信度分布区间。比如0.25检出80个框、0.1检出140个框说明有近一半的正确框落在0.1到0.25之间那业务上就该用0.1附近的门限。6.2 可视化输出与置信度分布二次校验光调门限还不够我会用一个脚本统计验证集所有预测框的置信度分布而不是只看几张效果图。分布图能直接反映模型是犹豫型还是自信型如果大部分正确框的置信度都集中在0.8以上说明模型学得很好如果集中在0.3附近说明训练数据里类别混淆严重光调门限只能缓解一时根本上还是要补充难例。从那以后我每次拿到新的水下数据集都会强制走一遍“统计类别分布、清洗标注、转换核对、小学习率试跑、按loss抽检脏数据、推理时对比两个门限”这个流程整套下来基本没再出过岔子。这份数据集本身就带好了VOC和YOLO两套标注拿到手先按第2章的脚本盘一遍再走第4章的流程训练最磨人的预处理阶段可以省掉大半。希望这些能帮到你。本文还有配套的精品资源点击获取
