ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

水下垃圾检测数据集:7类目标、3种标注格式与YOLO训练实战

水下垃圾检测数据集:7类目标、3种标注格式与YOLO训练实战 简介面向水下垃圾检测与水域环境智能监测任务该数据集包含水下机器人拍摄的5328张真实水下图像覆盖水下生物、塑料垃圾、金属垃圾、木头垃圾、橡胶垃圾、布料垃圾、捕鱼工具垃圾共7个类别适合目标检测算法的训练、验证与项目落地。压缩包共21313个文件、大小约127.54MB其中jpg图片5328张并同步提供VOC格式xml、YOLO格式txt和JSON格式标注各5300余份三种标签文件可直接对接主流检测框架省去格式转换环节。数据分布均衡、标注边界准确能有效支撑课程设计、竞赛方案或实际工程中的模型调优。目前已有325人学习下载对于需要开展水环境智能监测或水下机器人视觉识别研究的开发者来说是一份质量可靠、开箱即用的数据集资源。1. 水下垃圾检测数据集七类目标、三种标签格式一次配齐做水下机器人或水域环境监测项目时最头疼的往往不是模型选型而是找不到一份标注质量过关的数据。公开的水下数据集大多只有几十张到几百张类别集中在鱼和珊瑚真正针对垃圾目标的少之又少。这份水下垃圾检测数据集一共5328张图片覆盖水下生物、塑料垃圾、金属垃圾、木头垃圾、橡胶垃圾、布料垃圾、捕鱼工具垃圾七个类别每张图都配了VOCxml、YOLOtxt、COCOjson三种格式的标签文件拿来就能直接喂给YOLO系列、Faster R-CNN、SSD这些主流检测算法。不管你是做课程设计、参加水下机器人比赛还是准备上手一个水域智能监测的实际项目这份数据都能省掉标注和格式转换的大量时间。下面我把数据集的格式细节、检查方法、训练配置和踩过的坑逐一拆开讲。2. 三种标注格式的区别与转换VOC、YOLO、COCO 各自适合做什么拿到数据集后先别急着训练第一步是把三种格式的关系理清楚。很多新手在这上面翻车是因为以为三种格式内容完全等价直接拿YOLO格式丢进Faster R-CNN的训练脚本结果报错半天找不到原因。其实三种格式面向的是不同的工具链虽然描述的是同一批标注框但存储结构和坐标体系完全不同。2.1 三种格式的存储结构与坐标体系差异VOC格式本质是XML文件遵循PASCAL VOC的组织方式。每个xml文件对应一张图片根节点是annotation里面包含folder、filename、size图片宽高和通道数、object列表。每个object节点下是name类别名、pose、truncated、difficult以及bndboxxmin、ymin、xmax、ymax。这个格式的特点是便于人读用文本编辑器打开就能看懂框的位置配合LabelImg标注时默认就是这种格式。YOLO格式是纯文本txt文件每行代表一个目标五个数字依次是class_id、x_center、y_center、width、height。这里的坐标全部做了归一化除以图片的宽和高值域在0到1之间。class_id从0开始编号对应类别清单里的索引。这个格式节省存储空间数据加载速度快是Darknet、Ultralytics YOLO系列的标配输入。COCO格式是单个json文件把所有图片和标注集中在一起管理整体结构分为images、annotations、categories三个顶层字段。images里记录每张图片的id、file_name、width、heightannotations里记录每个标注框的image_id、category_id、bboxx、y、w、h左上角坐标加宽高、area、iscrowdcategories里是类别id到名称的映射。COCO格式适合做统一的数据管理detectron2、MMDetection这些框架默认使用它。三种格式的坐标换算关系是核心。VOC的xmin、ymin、xmax、ymax转YOLO时x_center (xmin xmax) / 2 / widthy_center (ymin ymax) / 2 / heightw (xmax - xmin) / widthh (ymax - ymin) / height。COCO的bbox转YOLO时x_center (bbox[0] bbox[2] / 2) / width其余同理。一个常见错误是拿VOC的坐标除以640就当YOLO用图片实际宽度是1920的话所有框的x坐标全偏到左边模型训练出来检测位置整体偏移。2.2 用Python脚本做三种格式的互转实际项目里经常需要在三种格式之间切换比如你用LabelImg标注了一批新图片默认输出VOC格式但训练YOLOv8需要txt格式。写一个统一的转换脚本能解决这类问题。下面这段代码实现VOC转YOLO和COCO转YOLO两个方向。import os import xml.etree.ElementTree as ET import json from pathlib import Path def voc_to_yolo(xml_path, class_names, output_dir): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) xmin float(obj.find(bndbox/xmin).text) ymin float(obj.find(bndbox/ymin).text) xmax float(obj.find(bndbox/xmax).text) ymax float(obj.find(bndbox/ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 坐标边界裁剪防止越界值进入训练 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_name Path(xml_path).stem .txt with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(lines)) def coco_to_yolo(ann_file, img_dir, output_dir): with open(ann_file, r) as f: coco json.load(f) cat_id2idx {} for idx, cat in enumerate(coco[categories]): cat_id2idx[cat[id]] idx img_id2info {} for img in coco[images]: img_id2info[img[id]] img boxes {} for ann in coco[annotations]: if ann.get(iscrowd, 0) 1: continue img_id ann[image_id] boxes.setdefault(img_id, []) boxes[img_id].append(ann) for img_id, anns in boxes.items(): img_info img_id2info[img_id] img_w img_info[width] img_h img_info[height] lines [] for ann in anns: x, y, w, h ann[bbox] # COCO的bbox是左上角坐标加宽高需要换算成中心点坐标 x_center (x w / 2.0) / img_w y_center (y h / 2.0) / img_h w_norm w / img_w h_norm h / img_h lines.append(f{cat_id2idx[ann[category_id]]} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) txt_name Path(img_info[file_name]).stem .txt with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(lines))这段代码两个函数分别处理VOC和COCO的转换。voc_to_yolo里用ElementTree解析xml逐层取节点size节点下的width和height是归一化的分母。object迭代时做类别名到id的映射不在类别清单里的目标直接跳过。坐标计算后加了min/max裁剪防止标注框边缘越界导致训练时出现NaN值。coco_to_yolo里先建立category_id到连续索引的映射再用image_id分组所有标注框最后逐图写入txt。这里需要注意COCO的bbox是[x, y, width, height]x和y是左上角坐标转YOLO中心点坐标时要先加一半宽高。我一般会在转换后抽几张图把txt里的坐标反算回像素值画在原图上人工确认框的位置没有错位这个检查步骤能省下后面排错的很多时间。3. 数据体检与预处理训练前必须完成的四步检查数据从下载到训练中间隔着一道体检工序。很多人图省事直接把图片和标签丢进训练脚本结果Loss曲线异常、mAP忽高忽低最后查来查去发现是数据集本身有问题。这份数据集虽然标注质量可靠但任何数据集在换环境、换设备后都可能出现路径失效、文件损坏、类别不匹配的问题花二十分钟做一次全面体检值得。3.1 检查图片完整性、标签对齐与类别分布第一步检查图片能否正常读取。用OpenCV或PIL把所有图片过一遍能加载且尺寸大于0的才算有效和标签文件一一对应。常见的问题是图片与标签文件数量对不上或多出来没有标签的图片这在训练时会导致FileNotFoundError。import cv2 import os from pathlib import Path from collections import Counter img_dir images/train label_dir labels/train broken_images [] missing_labels [] label_counts Counter() for img_path in Path(img_dir).glob(*.jpg): img cv2.imread(str(img_path)) if img is None: broken_images.append(img_path.name) continue label_path os.path.join(label_dir, img_path.stem .txt) if not os.path.exists(label_path): missing_labels.append(img_path.name) continue with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) 5: label_counts[int(parts[0])] 1 print(f损坏图片: {len(broken_images)}) print(f缺少标签: {len(missing_labels)}) print(f各类别目标数量: {dict(label_counts)})这段脚本输出三个关键信息损坏图片数量、缺少标签的图片数量、各类别目标分布。我一般会把broken_images和missing_labels列表单独保存下来根据具体情况决定是删掉这些样本还是重新标注而不是直接把整个文件夹丢进训练。如果某张图损坏但别的数据够了直接删除是成本最低的方案。类别分布检查尤其重要。这份数据集的七个类别里瓶子、渔网这类目标数量较多而某些金属垃圾样本可能偏少训练时小类别的AP会明显低于大类。如果发现极端不均衡可以针对性补采数据或做类别加权而不是盲目堆训练轮数。3.2 数据集划分按目录而非按文件随机挑训练集、验证集、测试集的划分方式直接影响模型评估的可信度。很多人直接写个脚本把数据随机打乱按比例分配但这样做有一个隐患同一场景的连续帧可能同时出现在训练集和验证集里验证分数虚高。水下机器人拍摄时经常是绕着同一个目标转圈相邻帧之间的相似度极高这种数据泄露会让模型在真实场景里表现明显下滑。我的做法是按目录或按采集批次划分确保同一场景的视频帧不会跨集合。这份数据集的图片文件是散放的文件名带编号但没有采集批次信息这时候可以按文件名哈希取模来做划分让同前缀的图片尽量落在同一个集合里。import os import shutil import hashlib from pathlib import Path img_files sorted(Path(images/all).glob(*.jpg)) train_dir Path(images/train) val_dir Path(images/val) test_dir Path(images/test) train_dir.mkdir(parentsTrue, exist_okTrue) val_dir.mkdir(parentsTrue, exist_okTrue) test_dir.mkdir(parentsTrue, exist_okTrue) for img in img_files: # 基于文件名哈希做划分保证同一前缀的图片进同一个集合 h int(hashlib.md5(img.stem.encode()).hexdigest(), 16) % 100 if h 80: dest train_dir elif h 90: dest val_dir else: dest test_dir shutil.copy(str(img), str(dest / img.name)) label_src Path(labels/all) / (img.stem .txt) label_dst Path(str(dest).replace(images, labels)) label_dst.mkdir(parentsTrue, exist_okTrue) if label_src.exists(): shutil.copy(str(label_src), str(label_dst / label_src.name))用文件名哈希取模做划分的好处是稳定可复现——同一张图不管跑多少次脚本都落在同一个集合里而且同前缀的图片大概率分到同一集合降低不同集合间的数据相似度。80/10/10的比例对于5328张图来说训练集约4260张验证集和测试集各约530张这个规模足够训练一个可靠的检测模型。如果你的场景里同一拍摄轨迹的帧特别多可以把训练集比例压到70给验证集和测试集多留一些。划分完成后还要检查一下verify训练集中的类别分布是否和全量数据集一致哪些类别在验证集中完全没有样本。如果小类别在验证集里缺失模型即使训练得很好验证指标也会虚低。3.3 数据清洗边缘框、过小框和重复样本的处理数据体检的第三步是检查标注质量。虽然这份数据集本身标注精准但从其他渠道获取或自行补充的数据不一定可靠。重点筛查三类问题一类是标注框越界框的坐标超出图片边界一类是标注框过小宽或高只有几个像素对检测任务来说基本是噪声还有一类是重复样本同一张图或极其相似的图在数据集里出现多次。边缘框和重复样本的检查逻辑比较直接过小框的判断需要结合你的输入分辨率。如果你的模型输入是640x640而原图是1920x1080一个标注框在原图上只有10个像素宽缩放后不到4个像素这样的目标基本学不到特征。我一般会把这类标注过滤掉或手动修正而不是直接删图片。import cv2 import os min_size 8 # 原图上的最小像素尺寸 for img_path in os.listdir(images/train): img cv2.imread(os.path.join(images/train, img_path)) h, w img.shape[:2] label_path os.path.join(labels/train, img_path.replace(.jpg, .txt)) valid_lines [] with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, xc, yc, bw, bh map(float, parts) # 还原到像素坐标 x_pix xc * w y_pix yc * h bw_pix bw * w bh_pix bh * h # 过滤越界框和过小框 if x_pix - bw_pix / 2 0 or y_pix - bh_pix / 2 0: continue if x_pix bw_pix / 2 w or y_pix bh_pix / 2 h: continue if bw_pix min_size or bh_pix min_size: continue valid_lines.append(line.strip()) with open(label_path, w) as f: f.write(\n.join(valid_lines))这段清洗脚本把越界框和过小框直接过滤掉。min_size的取值需要根据实际任务调整如果检测的目标本身很小比如远处的小塑料片可以把阈值降到4如果只看近景大目标可以提高到12。过滤后最好重新统计各类别的样本数确认没有类别被过滤得太多。数据清洗的原则是宁缺毋滥一个错误标注对模型的影响比十个缺失样本还严重。4. 用YOLO系列训练水下垃圾检测模型配置、训练与验证数据就绪后进入训练环节。YOLO系列是目前检测任务的首选v8和v11的训练流程高度统一都用Ultralytics框架。这份数据集的txt标签直接适配YOLO格式省去了格式转换的工作。下面从数据集配置、训练参数、验证评估三个环节展开。4.1 配置数据集yaml文件与训练目录结构Ultralytics框架训练前需要准备一个数据集配置文件告诉框架图片路径、标签路径、类别数量和类别名称。# underwater.yaml path: /home/user/underwater_data # 数据集根目录 train: images/train # 训练集图片目录相对path val: images/val # 验证集图片目录 test: images/test # 测试集图片目录可选 nc: 7 # 类别数量 names: 0: underwater_organism 1: plastic 2: metal 3: wood 4: rubber 5: cloth 6: fishing_toolpath字段是数据集根目录的绝对路径train、val、test是相对path的图片目录路径。框架会自动在同级目录下寻找labels文件夹如果你的标签目录叫labels且内部结构和images一致不需要额外配置。nc必须和names列表长度一致names的索引顺序要和txt文件里的class_id一一对应。这里有个容易出错的地方txt文件里class_id为2的类别是金属垃圾yaml里names列表的第二个位置也必须是金属垃圾一旦错位模型训练时不会报错但预测结果的类别标签全部是错乱的。训练目录结构建议保持固定images/train、images/val、labels/train、labels/val。如果标签是VOC或COCO格式需要先用第2章的脚本转成txt再放进来。4.2 训练命令、核心参数与Loss曲线的判读方法yolo train modelyolo11s.pt dataunderwater.yaml epochs100 imgsz640 batch16 device0 patience20这是最简训练命令。model指定预训练权重yolo11s.pt在COCO上预训练过迁移学习可以大幅缩短收敛时间。data指向yaml文件。epochs设为100轮imgsz是输入分辨率640是速度与精度的平衡点如果目标小可以试1280但显存占用会成倍增加。batch16需要根据显卡显存调整显存不够就降到8。device0指定用第一张GPU。patience20是早停参数连续20轮验证集mAP没有提升就自动停止训练。这个参数对时间紧张的情况很有用很多人会把训练脚本挂着跑一夜第二天起来发现其实40轮就收敛了后面全是浪费。patience设小一点还能自动防止过拟合。训练过程中需要关注两个关键指标训练集的box_loss逐渐下降说明模型在学习验证集的mAP50和mAP50-95逐步上升并在某个区间趋于平稳。正常的Loss曲线是前20轮下降明显后面进入缓慢优化阶段。如果训练Loss降到很低但验证Loss不降反升说明过拟合需要增加数据增强、降低模型复杂度或提高早停阈值。如果Loss从头到尾都在震荡不下降大概率是数据问题回到第3章检查标签文件和类别映射。训练结束后框架会在runs/detect/train目录下生成权重文件best.pt和last.pt以及训练过程的指标图表。best.pt是验证集mAP最高的权重实际部署优先用它。4.3 验证与测试用混淆矩阵定位哪类目标容易漏检验证环节不只是看mAP数值更要分析错误类型。Ultralytics框架的训练结果目录里会生成混淆矩阵和各类别的PR曲线这些信息比单一指标更有价值。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.val(dataunderwater.yaml, splittest) print(results.box.map) # mAP50-95 print(results.box.map50) # mAP50 print(results.box.maps) # 各类别mAP按索引排列跑完val后重点看results.box.maps里每个类别的AP值。如果金属垃圾的AP特别低回到数据分布表看这个类别的样本数如果样本数不少但AP低大概率是类间特征相似导致的误判比如某些金属垃圾和木头垃圾在颜色纹理上接近。混淆矩阵里看哪两个类别互相误判最严重针对性补充那两个类别的难例样本。测试集的最佳实践是训练完用best.pt在test目录上跑一次正式的评估这次的结果才是对外汇报的最终指标。很多人只用验证集的数据反复调参最后测试集上表现不错但其实已经过拟合了验证集。把测试集留到最后只用一次数据才是可信的。5. 避坑清单标注、格式、训练三条线上的八个常见问题这一章把我在水下垃圾检测项目里遇到的典型问题集中列出来每一条都是真实踩过的坑按现象、原因、解决三段式记录。主流的检测算法框架虽然各有差异但这些问题在不同框架下都出现过值得仔细对照。5.1 标注框整体偏移但训练不报错现象是Loss曲线能正常下降mAP也能到0.8以上但检测结果里框的位置整体朝某个方向偏移。使用边缘很整齐的方形目标做测试时预测框总有一侧超出目标边界。原因是VOC转YOLO时坐标变换写错最常见的是直接用VOC的xmin、ymin、xmax、ymax做中心点计算时忘记除以图片宽高另一种情况是图片尺寸读取错误用了EXIF信息里的尺寸而不是实际解码后的尺寸。解决思路是先随机挑几张图的txt标签用代码把YOLO归一化坐标还原成像素坐标画在原图上人工检查。如果发现所有框统一偏移优先看转换代码里的除法如果只有部分图片偏移对比那几张图片的实际尺寸和读取的尺寸是否一致。水下图片经常带EXIF旋转信息有些解码库会返回旋转前的宽高务必用cv2.imread读到的shape为准。5.2 验证集mAP很高但实拍视频效果差现象是数据集的验证mAP能到0.85图里每个目标都能框住但部署到机器人上跑实时视频漏检率明显上升。原因是数据划分泄露训练分布和实拍分布差异大。水下机器人拍摄的视频帧高度相关如果划分时随机打散同一批连续帧可能分别落入训练集和验证集模型在验证时相当于做过一次开卷考试而实际部署遇到的场景、光照、水质可能和训练数据差别较大。解决方法是按拍摄批次或视频ID划分数据集这个在数据处理阶段就要做。这份数据集虽然单张图片为主但如果你自己补充了视频帧数据务必按视频切分。预处理阶段可以加入随机换背景、模拟不同水质色调的数据增强提升模型的泛化能力。5.3 class_id 错位导致类别标签全乱现象是训练过程完全没有报错但训练完成后预测结果里类别对应关系完全错误比如布料垃圾被识别成金属垃圾而且这种错误稳定复现。原因是data.yaml和标签txt的类别索引对不上。数据集提供方给的txt是按一定类别顺序生成的你换了yaml里的names顺序但txt文件里的class_id没有跟着改或者在过滤类别时删了某个类但没有重新编号导致索引断层后所有后续类别错位。解决办法是写个脚本统计训练集里出现的所有class_id按它们各自在yaml names里的对应关系做一次全量校验。最稳妥的做法是锁定数据集的类别顺序不改变新增类别放最后不删除已有类的索引。我自己一般会在训练前跑一次数据校验检查txt里所有class_id都落在0到nc-1区间内同时抽查每个class_id对应的类别名和实际目标是否符合。5.4 小目标漏检现象是mAP50挺高mAP50-95偏低实际使用中远处或小的垃圾检测不到。原因是输入分辨率不够或者小目标样本太少。水下场景里垃圾大小差异很大远处漂着的小塑料瓶可能只占画面几十个像素缩放到640输入时连十个像素都不到。解决方法是把imgsz调到1280或更高降低batch大小以适配显存。另一种有效做法是在数据增强里加入随机裁剪放大让模型看到更多局部大图。还可以用SAHI切片推理测试时把大图切成多个有重叠的patch分别检测再合并这个方案对小目标效果显著但推理耗时增加。5.5 显存不足现象是训练启动时报CUDA out of memory或者训练到一半爆显存。原因是batch太大、imgsz太高或者数据加载线程数太多导致CPU内存溢出。有些人一开始就把batch设成64想跑快点结果显卡直接撑不住。解决方法是逐步降低batch直到能正常训练batch16撑不住降到8再不行降到4。同时检查是否有其他进程占用显存用nvidia-smi看一下。如果在服务器上多人共用显卡还可以加显存限制参数。数据加载方面减少workers数量可以降低内存占用但会拖慢训练速度一般设成4到8。5.6 标签文件和图片文件名不一致现象是训练时报错找不到对应的标签文件部分图片在数据加载阶段被跳过训练出来的模型在漏检那些图片里的目标。原因是数据文件在拷贝或划分时丢失了部分匹配关系比如图片文件名是UnderwaterGarbage_1163.jpg标签文件却被改名成了1643.txt之类。解决方法是训练前跑一遍文件名比对脚本找出labels目录下没有对应jpg的txt或者images目录下没有对应txt的jpg把不匹配的文件统一改名或补全。这个脚本几分钟就能跑完能避免训练过程中莫名其妙的报错。5.7 Loss 降到很低但检测框完全不收敛现象是训练Loss一路降到0.05以下但验证mAP始终在低位徘徊检测框要么太大要么太小。原因是回归分支和分类分支失衡或者数据集里存在大量的错误的标注框。Loss低只能说明模型在训练集上拟合得很好不代表它学到了目标特征。如果标注框本身位置不准模型只能被迫记住那些错误位置的噪声。解决方法是先做一次数据可视化抽检把所有图片叠加标注框逐张看确认每个框都落在目标本体上。如果大量框偏移或过小数据集本身质量存疑建议优先清洗数据而不是调模型参数。也可以在训练时调整Loss权重配比把box_loss的权重适当调高。5.8 预处理出的数据增强导致标签失效现象是训练时使用Mosaic增强Loss曲线前几轮异常高且震荡剧烈甚至出现NaN值。原因是原始图片尺寸不统一Mosaic拼接时把不同尺寸的图拉伸合并后有些标注框的坐标在原始尺寸下合法但在拼接后的图上越界导致标签失效。解决方法是训练前先统一所有图片的尺寸或者在数据加载时强制resize到相同尺寸再做增强。框架自带的增强模块一般会处理边界问题但如果你自己写了预处理逻辑需要注意归一化坐标的换算。出现NaN值时可以先把增强选项全部关掉确认数据本身无误后再逐项打开增强功能定位问题。6. 数据增强与针对性验证让模型在水下环境中真正可用训练完成只是第一步模型要应对真实水下场景的多样性还需要在数据增强和验证策略上做功课。水下环境的光照不均匀、水质浑浊、悬浮颗粒多这些都会让训练好的模型在实际部署时性能下滑。本章围绕增强手段和验证方法给出具体操作建议。数据增强可以在训练时做在线增强也可以在训练前扩充数据集两种方式各有适用场景。Ultralytics框架默认开启的增强包括HSV色域扰动、随机翻转、Mosaic拼接等这些对常规场景足够。但水下图像有自己的光线特点适当的针对性增强能明显提升泛化能力。augmentations { hsv_h: 0.02, # 色相扰动适应不同色调的水体 hsv_s: 0.5, # 饱和度扰动模拟浑浊和清澈水质的差异 hsv_v: 0.4, # 明度扰动模拟不同深度的光照变化 degrees: 10.0, # 随机旋转适应机器人的姿态变化 translate: 0.1, # 平移扰动模拟目标在画面中的不同位置 scale: 0.4, # 缩放扰动让模型适应不同距离的目标 fliplr: 0.5, # 水平翻转容器类目标左右镜像不影响语义 mosaic: 0.8, # Mosaic拼接增加小目标样本的多样性 }这些参数分别在训练命令里对应translate、scale、mosaic等参数用Ultralytics的API传入即可。hsv_h不宜设置过大否则垃圾和生物之间的颜色区分会被过度破坏scale设到0.4可以让模型同时看到近距离大目标和远距离小目标的不同尺度特征。水下场景还有个特殊点垂直翻转需要慎用因为水下垃圾有浮沉规律瓶子通常朝上飘鱼钩渔网通常下沉垂直翻转会混淆这些上下语义。训练结束后的针对性验证是整个项目最容易忽略的环节。只看mAP指标会被虚假的安全感欺骗最终评估要基于真实机器人拍摄的视频或图片样本。取一段测试集之外的视频推流停在其中某一帧观察小目标漏检情况、遮挡目标是否误检、形态变化的目标比如缠绕成团的渔网是否还能识别。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(sourcetest_video.mp4, conf0.25, imgsz1280, saveTrue)这段推理代码来自真实项目场景测试视频建议包含多个水质条件、不同距离和光照的片段才能真实反映模型的泛化能力。conf阈值默认0.25对于水下漏检场景偏高可以下调到0.15但要同时接受误检增多需要找到平衡点。imgsz调成1280对小目标友好但真机上推理帧率会下降具体取舍取决于硬件算力。把增强参数和验证流程固定下来后我每次训练都会跑完一遍完整的流程先做数据体检和划分再训练、验证、实拍测试任何一步不过关就回头修正不带着问题进入下一个环节。从那以后我每次训练都强制自己走一遍这个流程即使是几百张图的小规模测试也不例外。希望这份数据集的方法论对你有实质帮助特别是那些正要交付水下检测项目的同行们。本文还有配套的精品资源点击获取
返回列表