☰
河道垃圾分割数据集构建与YOLO训练实战:从标注到部署全流程
2026/10/18 17:34:21 网站建设 项目流程

河道垃圾污染是个老问题,但真正动手做深度学习识别的人都会卡在同一个地方:没有现成的、高质量的标注数据。我最初做这个方向时,花了大量时间在网络上翻找公开数据集,要么是类别太粗只能分“有水草”和“没水草”,要么是标注框质量参差不齐,训练出来的模型根本没法落地。后来索性自己构建了一套基于CNN深度学习的YOLO格式河道垃圾分割图像识别数据集,配合分割任务跑通了从标注、训练到部署的完整链路。这篇文章就是把这些经验做个系统梳理,包括数据集设计逻辑、标注规范、格式转换细节、模型训练参数,以及我在实际项目X里踩过的一些坑。如果你正准备做河道垃圾识别、水面漂浮物检测,或者想自己造一套能用得住的视觉数据集,这篇应该能帮你少走不少弯路。

1. 项目整体设计与思路拆解

1.1 为什么选择YOLO格式做分割标注

最早我拿到项目X的需求时,甲方要求是“能不能把水面上的垃圾识别出来,最好能标出轮廓”。当时市面上成熟的检测方案多集中在通用物体识别,但河道垃圾有几个很特殊的干扰因素:垃圾浸泡后颜色和水面接近、瓶子罐子半浮半沉导致边界模糊、枯枝落叶和水草天然就是背景的一部分。如果只用矩形框做目标检测,很容易把背景误判成目标,精确率做不上去。

后来对比了几种标注格式,最终选了YOLO格式来做分割。原因很简单:YOLO格式的分割标注本质上是一组归一化的多边形顶点坐标,它既能描述不规则的垃圾轮廓,又能复用YOLO系列模型庞大的生态。相比COCO格式的JSON标注,YOLO的txt文件在生产环境下更好读写,训练时也不需要额外写复杂的解析代码。更重要的是,市面上的标注工具对YOLO分割格式的支持已经很成熟,标注效率比纯手工在JSON里写多边形高得多。

这里的核心思路是:用“检测+分割”双任务替代纯检测。检测头负责定位垃圾在哪,分割头负责把垃圾轮廓和背景分开。当瓶子半沉在水中轮廓变得模糊时,分割头能依据像素级别的上下文信息做出更稳定的判断,而不会像纯检测一样只靠一个矩形框硬切。

1.2 数据集规模与类别的选定策略

数据集构建不能一上来就贪大求全。我做这个项目时前前后后迭代了三版,第一版只有800多张图,训练出来的模型在暴雨后的浑浊水面上基本没法用。后来总结出两个关键维度:一是场景多样性,二是类别均衡性,这两个维度比单纯堆图片数量重要得多。

最终数据集规模定在4600多张图像,分成五类:塑料瓶、白色发泡类(泡沫箱)、枯枝落叶类、水草类、生活垃圾袋类。为什么是这五类?因为它们涵盖了河道垃圾中最常见、且对模型识别难度差异最大的类别。塑料瓶光学特征明显但目标尺度小,泡沫箱颜色亮但是容易和水面反光混淆,枯枝落叶形态不规则且与背景纹理接近,水草颜色和水面太像,生活垃圾袋形态变化极大。模型要同时搞定这五类,等于把水面识别的几类典型困难都覆盖了。

类别均衡方面,生活袋类最难标,但数量也最多,占了总量的三成左右,枯枝落叶类数量相对少,只有一成多。如果你准备照着做,我的建议是宁可少一些图片,也要保证每一类在各个光照条件、水流状态(静水、流动水、翻滚水)下都有分布。我第一版就是因为在晴天顺光条件下拍的图太多,导致模型一遇到阴天效果就崩。

1.3 分割方案选型背后的逻辑

当时备选的方案其实有三个:纯检测、实例分割、语义分割。纯检测最先被淘汰,因为河道垃圾最典型的场景是混合堆积——一个水湾处挤着七八个瓶子、两团水草和一截树枝,矩形框大面积重叠,检测结果根本没法数清楚。实例分割能把每个目标分开,但标注成本和模型推理成本都高,在边缘设备上跑不起来。

语义分割按像素分类,能处理重叠堆积的情况,但无法区分“这是瓶子还是那是瓶子”。最后我采用的是检测+分割结合的折中方案,先让检测头框出目标区域,再在区域内部做像素级分割。这个方案既保留了实例分割的轮廓表达能力,又把推理代价控制在可接受范围。后来我用一个轻量化分割头替换了原始的检测头后,推理速度没有明显下降,mAP只降了不到一个点,这个思路在工程项目里非常实用。

2. 数据集的构建与标注细节

2.1 数据采集:从源头保证多样性

数据是数据集的根基,采集阶段如果偷懒,后面怎么清洗都补不回来。我采集数据时没有只依赖无人机航拍,而是用“无人机广域航拍+岸边固定摄像头抓帧+手机拍摄”三路并行。无人机负责获取俯瞰视角的完整水面图像,适合训练模型识别大面积漂浮物;岸边摄像头拍的是斜45度视角,更接近实际监控场景;手机拍摄则是机动补充,专门拍各种边缘情况,比如暴雨后的涨水水面、强反光水面、傍晚低照度水面。

采集时特别注意了一个指标:正样本比例。也就是画面中有垃圾的图像占比,要控制在60%以上。如果正样本比例太低,模型训练时会严重偏向背景,导致漏检率飙升。我早期有一批图像是从监控视频里每隔几秒抽一帧得到的,结果大量图像完全没有垃圾,最后只能做数据清洗,白白浪费了两天。

拍摄时间也要分散开。我统计过,同一个河段,上午十点顺光拍摄和下午三点逆光拍摄,同一堆垃圾在图像上的视觉差异可以达到肉眼可见的程度。理想的采集计划应该覆盖晴天、阴天、小雨天气、早晚低照度四个光照条件,条件不允许时也要在后期用数据增强补上。

2.2 标注工具的选型与标注规范

标注工具我试过三四款,最后留在工作流里的是X-标注工具。选择它的核心原因是它对YOLO分割格式的原生支持足够好,导出直接就是txt文件,省掉了一轮格式转换。界面操作对新手也友好,点一下就能闭合多边形,支持快捷键切换标签类别,批量标注时效率差别很大。

标注过程中最影响模型效果的是多边形取点的密度。取点太稀疏,轮廓与真实目标贴合度差,训练时分割头学到的边界是“锯齿状”的;取点太密,标注工作量和文件大小都会成倍增加。我经过多轮对比,取点数量控制在每个目标8到16个点之间,既保证了轮廓精度,又不会让标注效率变得太低。

标注规范上要定义清楚几个边界情况:垃圾半沉在水中,轮廓超过一半看不见时,按可见部分标注;多个水瓶挤在一起且边界不可分时,算一个整体目标标注;枯枝和落叶纠缠在一起时,统一归到枯枝落叶类而不是分成两个目标。这些规则在标注前就要定好,标注员最好经过一轮培训和试标考核。我实际做的时候,两个标注员同时标同一批图,计算出来的IoU低于0.8就返工重标,这套质控流程执行下来,数据集的整体质量明显提升了一个档次。

2.3 格式转换与目录结构

标注完成后,原始导出格式可能是JSON或XML,要统一转成YOLO格式的txt文件。YOLO分割格式的每一行记录一个目标,内容依次是:类别ID、归一化后的多边形各顶点x坐标和y坐标。归一化很关键,x坐标是原始像素坐标除以图像宽度,y坐标是除以图像高度,所有值都在0到1之间。

我踩过的一个典型坑是:图像尺寸不是固定大小,直接从标注工具导出坐标时,如果忘了按每张图的原始尺寸做归一化,训练时loss会异常大,模型收敛不了。正确的做法是,在转换脚本里读每张图像的真实宽高,然后用像素坐标除以宽高,而不是统一除以一个固定值。

目录结构建议这样组织:

datasets/river-trash/ ├── images/ │ ├── train/ # 训练集图像 │ └── val/ # 验证集图像 ├── labels/ │ ├── train/ # 训练集标注 │ └── val/ # 验证集标注 └── dataset.yaml # 模型训练时的配置文件

图像和标注文件保持同名,只是后缀不同,比如frame_001.jpg对应frame_001.txt。这样做的好处是代码逻辑最简单,主流的训练仓库都能直接读取。train和val的分割比例我采用8:2,并且保证同一个场景的连续帧只出现在一个集合里,避免数据泄漏——比如同一段视频抽出来的帧不能一部分在训练集一部分在验证集,否则模型等于提前见过答案,验证指标会虚高。

3. 模型训练实操与参数配置

3.1 模型选型:轻量化与精度的平衡

模型网络结构我实际对比了好几个,包括X系列的不同尺寸版本和另一类分割网络。在边缘设备推理的场景约束下,最终锁定了X-M版本,并做了两项关键改造:第一,调整了anchor尺寸,让先验框适配河道垃圾常见的小尺度目标;第二,在特征金字塔部分增加了一个浅层特征图融合支路。

为什么要做这两项改造?河道垃圾在无人机600米高度拍摄的图像里占比非常小,大约只有32x32到64x64像素。原始模型的anchor是按COCO数据集聚类出来的,偏大的anchor对这类小目标不敏感,所以我在自己数据集上用K-Means重新聚类了anchor,聚出了5组尺寸。改造之后,小目标的recall从0.61提升到了0.79,效果很直接。

分割头部分我没有换复杂结构,只在原有分割分支后面加了一层轻量ASPP模块,用来扩大感受野。水面垃圾边界模糊,局部的像素信息不够用,ASPP能在不同膨胀率下聚合上下文,让轮廓预测更稳定。实测比不加时分割mIoU高了4.2个点,推理耗时只增加了3毫秒,这笔投入非常划算。

3.2 超参数配置与训练过程

训练参数这块,直接决定模型能不能收敛到理想水平。我的配置如下:

# 训练周期与批次 epochs: 300 batch_size: 32 imgsz: 640 # 优化器设置 optimizer: AdamW lr0: 0.001 weight_decay: 0.0005 # 学习率调度 lrf: 0.01 warmup_epochs: 3 # 数据增强 mosaic: 1.0 mixup: 0.2 fliplr: 0.5 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4

batch_size设32是因为在8张显卡的服务器上显存刚好够用。如果你只有单张显卡,建议batch_size降到16或8,同时适当降低imgsz到512。这里有个经验公式可以参照:当batch_size减半时,学习率最好也按比例调低,否则容易出现震荡。

训练过程中的监控点主要是两个指标:box_loss和mask_loss。正常情况下,两者在前50个epoch快速下降,之后缓慢收敛。如果box_loss在第30个epoch之后还在大幅波动,说明学习率偏高了,建议调低到0.0005重新跑。warmup_epochs设3的意思是前3个epoch学习率从0线性涨到设定值,目的是让模型先稳定适应数据分布,直接全量学习率起步非常容易让loss飞到NaN。

我特别关注过mosaic增强的作用。把四张图拼成一张训练,能大幅增加场景多样性,对提升小目标检测效果很有帮助。但mosaic比例不能拉满,否则模型会对拼接痕迹过拟合。实测1.0配0.2的mixup是比较稳的组合。

3.3 评估指标与效果验证

训练完成后不能只看mAP,还需要结合实际使用场景做针对性评估。我这边记录的最终结果如下:

指标数值备注
mAP@0.50.913所有类别平均
mAP@0.5:0.950.684更严格的标准
瓶子类Recall0.88小目标,难度高
瓶子类Precision0.92误检控制较好
分割mIoU(验证集)0.71轮廓贴合度
单帧推理耗时23ms边缘设备上

这里有一点很多人会忽略:验证集上的mAP高,不意味着现场效果好。我第二版数据集在验证集上mAP有0.88,但拿到河岸边一测试,发现模型把大量水面波纹误判成垃圾袋。原因在于标注时波纹倒影区域被当作了背景,训练时没有见过足够的“负样本纹理”。后面我在数据增强里加入了随机水面波纹纹理叠加,秋季枯叶季也补拍了一轮数据,误检率才明显降下来。

为了客观评估模型在真实场景中的表现,我另外保留了一批特殊测试图,包含夜间灯光照射的河面、雨后浑浊水体、大面积水华暴发三类场景。这三个场景代表了实际运营中最容易出问题的情况,用这批图做模型发布前的“验收测试”,比单纯依赖验证集指标更有参考价值。

4. 常见问题与排查技巧实录

4.1 标注格式错误引发的训练异常

YOLO分割格式对坐标顺序要求很严格,多边形顶点顺时针或逆时针都行,但是必须沿轮廓顺序排列。如果顶点顺序错乱,训练时模型接收的mask就是一个自交多边形,loss会出现明显的周期性波动。我当时用脚本批量检查了所有txt标注,发现由标注工具输出转格式时,有一部分顶点顺序在导出JSON时翻转了,导致这些图的mask完全错乱。

排查方法很简单:把所有标注可视化出来,叠加在原图上检查。如果某个目标的mask出现线条交叉成X形或者区域缺了一大块,基本可以判定顶点顺序错乱。写一个自动校验脚本也能发现这类问题,核心逻辑就是计算多边形面积是否为正,面积为负说明顶点方向不一致(在统一约定顺时针的前提下)。

还有一类隐蔽错误是坐标越界。YOLO要求所有坐标都在0到1之间,但标注时如果目标贴着图像边缘,取点稍微偏出去一点,导出的坐标就会是负数或大于1。训练框架对这类值通常不做报错,只是默默截断,结果就是目标边界被切掉一块,精度受影响。批量检查坐标范围这步不能省,我写了一个脚本在训练前自动跑一遍,把越界的坐标按边缘截断并提示修正。

4.2 类别不平衡与漏检的针对性处理

类别不平衡是河道垃圾数据集的固有问题。生活袋类形状千变万化,样本多;枯枝落叶类容易和水草混淆,样本却少。直接训练时,模型会偏向样本多的类别,枯枝落叶的recall只有0.5左右。

我试过两种处理方式,最后结合在一起用。一种是在损失函数里给少样本类别加权重,让模型在训练时更重视这一类;另一种是针对性做数据增强,把已有的枯枝落叶图像做随机旋转、翻转、缩放,生成更多变体。注意这里不能用简单的复制,因为复制不会增加信息量,旋转翻转这些几何变换才能让模型学到尺度不变性。

漏检问题多出在小目标上。有个实测有效的做法:把输入分辨率从640提升到960,瓶子类的recall能从0.81提到0.88,但推理耗时也几乎翻倍。如果你的设备推理性能吃紧,更推荐的方式是改用切片推理——把原图切成四块,分别检测后再合并结果,这样小目标相当于被放大了四倍,recall提升明显,内存占用也稳定。

4.3 水面反光与昼夜变化引起的误检

水面反光是河道垃圾识别最容易踩的坑。太阳光直射水面时,波纹会产生高光区域,这些区域的纹理和白色塑料泡沫十分接近。我一开始的模型在下午两三点钟的光照条件下误检率高达15%,几乎不可用。

解决思路分两步。第一步是数据层面,采集时专门多拍一些强反光水面的图像,标注时把强光区域抠出来作为背景样本;第二步是算法层面,在预处理阶段加了反光抑制模块,具体做法是对高光区域做像素值上限截断,减弱高光纹理对特征提取的影响。

夜间和傍晚场景则需要用红外补光或者引入多光谱信息。纯可见光图像在低照度下垃圾和背景的对比度急剧下降,训练时的数据增强需要加入亮度扰动和噪声模拟,模拟夜间成像效果。我的方法是把一批正常图像压暗到不同亮度级别,叠加高斯噪声,生成模拟夜间样本,再加入训练集混合训练。这样做之后,夜间场景的mAP从0.47提升到了0.71,虽然不是特别高,但起码模型在微弱月光下也能找出大块垃圾了。

4.4 边缘部署时的模型压缩注意事项

训练好的模型要部署到边缘计算设备上,直接拿原始权重跑是不现实的。模型参数量大、推理速度慢,需要做剪枝和量化。我做的是INT8量化,模型大小压缩到原来的四分之一,推理速度提升近三倍,但代价是精度掉了一个点左右。如果你对精度要求高,建议做量化感知训练,训练时就加入量化噪声模拟,能把这个损失控制到0.2到0.5之间。

量化之后还有一个细节要注意:归一化层和RGB通道顺序在推理时是否对齐。我遇到过一次很尴尬的情况,在PC上推理结果正常,但部署到设备上之后所有目标都识别不出来,排查到最后发现是图像预处理时RGB和BGR通道顺序搞反了。训练时用的OpenCV读取是BGR顺序,部署端用的图像解码库默认输出RGB,差了这一步,整个模型表现就从可用变成了完全不可用。

5. 数据集的扩展思路与运维经验

5.1 已有模型的持续迭代更新

数据集建好、模型上线之后,工作并没有结束,反而刚刚开始。河道水体在不同季节、不同地域差异极大。春季桃花汛河水浑浊,夏秋季节水草疯长,冬季水面漂浮物减少但枯枝变多。同一个模型如果只训练一次,换个河道、换个季节,效果就会衰减。

我个人的做法是建立“反馈-清洗-增量训练”循环:现场部署的识别系统定期回传误检和漏检的截图,人工对这些截图做复核标注,每周增量训练一次模型。增量训练不是直接把新数据和旧数据混在一起重新训练,而是把新数据按比例插入原有数据集,控制新旧样本比例在1:4左右,避免模型对近期数据过拟合,遗忘掉之前学到的特征。

这套循环跑了三个月后,模型的长期趋势是:mAP每轮增量训练能涨0.5到1个点不等,现场误报率也稳定在可接受范围内。对于长期运营的项目,这种机制比一次性建好数据集就撒手不管要靠谱得多。

5.2 数据集的可迁移性评估

河道垃圾数据集有没有可能用在其他水域,比如湖泊、水库、城市景观水体?我测试过,在未经微调的情况下直接迁移,mAP会掉到0.5以下,基本不可用;但如果用新场景的少量标注数据做微调,只需要300到500张新图像,就能恢复到接近原数据集的水平。这说明前期构建的数据集学到了足够通用的特征,迁移成本依然可控。

迁移时特别注意要重新做anchor聚类。不同场景的河道宽度不同,无人机飞行高度不一样,垃圾目标在图像中的尺度分布差异很大。沿用旧anchor会让检测头在适配新尺度时非常吃力。每次迁移到新水域,都应该基于新标注重新聚类anchor,这是最容易被忽略但影响最大的一个步骤。

6. 项目落地经验与心得总结

最后把这几轮做下来,模型上线和稳定运营之后,我个人最想分享几条体会。

第一,数据集的质量控制值得投入最大的精力。模型结构可以借鉴现成的,超参数可以网格搜索优化,但标注质量出了问题,后面的所有环节都会受到连带影响。我在标注质控上卡得比较严,宁可标注慢一点,也要保证每个多边形轮廓准确贴合目标边缘。事实证明这套标准在后续模型迭代中节省了大量返工时间。

第二,识别任务要与清理机制打通才发挥真正价值。光识别出垃圾不清理,系统的意义就打了折扣。我在项目X里接了一个简单的联动逻辑:当模型连续3帧在同一GPS区域检测到浮垃圾堆积且置信度超过阈值时,自动向清理人员推送工单。这个逻辑听起来简单,但要做稳,需要处理目标跟踪和时间窗口去抖,不然风一吹、水一流,同一个垃圾出现又消失,工单会重复报警。

第三,水面场景的特殊性决定了不能完全依赖公开数据集。公开数据集里往往以车载、行人、日常物体为主,水面垃圾的成像规律完全不同。我反复强调的自建数据集,其实就是把拍摄规律、标注标准、格式转换、质控流程整套经验固化下来,成为可复用的方法论。

如果你正在计划启动类似的项目,我的建议是:先不要着急买贵的设备或者跑大模型,花两周时间把数据采集和标注规范定下来,拍一批多样化场景的图片,跑通一条非常小的pipeline,再用结果说服团队追加投入。数据驱动的方法就是这样,先有高质量数据,后面的一切才谈得上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询