2026/10/21 11:59:15

道路缺陷分割实操记录:从数据集整备到部署的完整链路

道路缺陷分割实操记录:从数据集整备到部署的完整链路 道路缺陷分割项目实操记录从数据集整备到部署的完整链路拿到这套道路缺陷分割系统源码的时候我的第一反应不是翻模型结构而是先去找数据集和训练脚本。在这个方向上断断续续折腾了一两年我太清楚路面裂缝、坑槽这类病害用普通目标检测框起来有多别扭裂缝细长蜿蜒一个矩形框根本框不完坑槽边缘参差不齐框一裁就丢掉了真实形状。这也是YOLOv8-seg这类实例分割模型在道路检测场景里越来越吃香的原因——它不但告诉你哪里有病损还输出精确到像素的缺陷轮廓后面算面积、量宽度、估工程量都非常顺手。我手上这套改进版在原生YOLOv8-seg基础上集成了EfficientRepBiPAN、AFPN-P345等几十种改进点自带清洗好的数据集和部署教程支持一键完成训练闭环。这篇文章不打算复述README我会按自己实际跑通的顺序把道路缺陷分割的关键环节拆开讲数据怎么整备、模型哪里能改、改进点怎么组合、训练参数怎么定、部署会踩哪些坑最后落到如何输出一份真正能给养护部门用的缺陷清单。不管你是刚接触实例分割的新手还是想往这个方向发论文的进阶玩家都应该能从这里找到能直接抄作业的内容。1. 道路缺陷分割的题眼检测框解决不了的事mask可以先说清楚一个问题路面病害检测为什么非得上分割道路缺陷其实是一类形状差异极大的目标。横向裂缝、纵向裂缝是长条状的长度可以从几十厘米到几米宽度却只有几个像素到十几像素长宽比可能超过一百比一。龟裂网状裂缝则是成片出现的细碎裂纹边界交叉重叠。坑槽虽然相对块状但边缘是破碎的、不规则的甚至会被阴影遮挡一部分。如果用检测框去描述这些目标你会面对两难框大一点裂缝和旁边干扰全包进来了框小一点裂缝又断成了一截一截一条完整的纵向裂缝会被识别成十几个小框。后续无论按框面积统计病害规模还是按框位置生成维修方案误差都很大。分割输出的是二值mask也就是每个缺陷在图像中的精确轮廓。有了这个轮廓你可以直接计算缺陷的像素面积、等效宽度、主轴方向、覆盖路面的百分比。裂缝的平均宽度、坑槽的开口面积这些指标对公路养护来说就是工程量估算的直接依据。目标检测给不了这些语义分割虽然能给轮廓但分不清实例一条路上的三处坑槽会被算成一片。所以道路缺陷检测的最优选就是实例分割既能区分单独病害又能给出精确轮廓。难点也集中在这几点上类不平衡极其严重。一张路面图上正常路面面积占99%以上裂缝只有几百个像素模型很容易把裂缝当成背景忽略掉。裂缝本身的语义信息弱。它就是几条暗色像素序列和沥青纹理、水渍、油渍、轮胎印、接缝都有相似外观不能用有没有一个显眼的物体来简单判别。光照和天气影响大。逆光、阴影、积水反光、雨后潮湿沥青会让裂缝特征完全反转同一个裂缝在不同光照下像素值差异巨大。标注成本高。裂缝边缘模糊不同标注者标注同一张图轮廓可能偏差好几个像素这直接决定了训练数据的质量上限。正因为这样做道路缺陷分割真正的功夫不在模型而在数据和训练策略。YOLOv8-seg作为基础模型的好处就是工程性单阶段、速度够快、部署方便而且官方预训练权重可以直接迁移在小规模道路数据集上也不会像两阶段方法那样容易崩。后面几节我会顺着数据、结构、改进、训练、部署这条链路逐个讲实际操作的细节。2. 数据集整备拿到标注文件之后先别急着训练很多刚入手这套源码的人第一件事就是把数据集丢进训练脚本然后对着低mAP发愁。我建议停一下先把标注文件全部检查一遍。道路缺陷分割的数据集构建是整个项目的质量地基这一步省了后面所有改进都是白搭。2.1 数据来源和类别设计数据源我一般分三部分一部分是公开的裂缝分割数据网上有不少路面病害标注图多半是水泥或沥青路面特写一部分是自己用车载相机或手机拍摄的真实路面还有一部分是早期模型迭代过程中人工修正并重新标注的样本。第三部分别忽略——前几版模型预测错得离谱的图反而最有训练价值。类别设计上最常用的是四类横向裂缝、纵向裂缝、网状裂缝龟裂、坑槽。再想细一点可以加修补区域和松散两类但注意修补区域本身不算病害它是过去维修留下的痕迹加这一类主要是为了告诉模型这里有修补块别去预测裂缝相当于一个负样本类别。类别数不是越多越好每多一类标注难度和训练复杂度都会上升前期建议先跑通四类。2.2 标注格式转换JSON怎么变成YOLO的txt公开数据集通常给你的是JSON标注多边形坐标而YOLOv8-seg训练需要的是每张图对应一个txt文件每行是一类缺陷的多边形归一化坐标。格式如下class_id x1 y1 x2 y2 x3 y3 ...所有坐标除以原图宽度或高度归一化到0到1之间。这个转换看起来简单实际非常容易出错最常见的坑是有人只归一化了x没有归一化y或者在遍历顶点时把坐标顺序写反导致mask变成自交叉多边形。转换的核心思路是读JSON - 取每张图的宽高 - 取标注里的多边形顶点 - 逐点除以宽高 - 按类写入txt。下面是一个我常用的转换逻辑骨架依赖JSON解析和图像尺寸读取通用做法import json def convert_labelme_to_yolo(json_path, out_txt_path): with open(json_path, r, encodingutf-8) as f: data json.load(f) h data[imageHeight] w data[imageWidth] lines [] for shape in data[shapes]: cls_name shape[label] class_id class_name_to_id[cls_name] # 必须从0开始 points shape[points] if len(points) 3: continue # 少于3个点的多边形是无效mask norm_points [] for px, py in points: norm_points.append(f{px / w:.6f} {py / h:.6f}) line str(class_id) .join(norm_points) lines.append(line) with open(out_txt_path, w) as f: f.write(\n.join(lines))转换完成后必须做一轮可视化验证。方法是把txt里的多边形重新画回图上叠加原图逐张看。这一步能过滤掉大量转换错误我自己的经验是第一次转3000张图大概有30-50张会出问题多数是边角点坐标超过1.0或者小于0。对于越界的点可以直接裁剪或丢弃但前提是这个缺陷不要被整个丢掉。2.3 数据清洗和防泄漏数据集整备阶段我习惯按这个顺序过一遍剔除模糊图、严重欠曝或过曝图。这类图即使是人也难以标注模型更学不会。剔除分辨率过低图比如拉伸到很小尺寸的缩略图。检查同一个场景的多帧连续图是否同时混入训练集和验证集。车载视频是逐帧拍的同一漏水缝可能连续出现20帧如果随机划分训练集和验证集会出现高度相似帧mAP虚高到离谱一换真实场景就掉精度。解决方案是按路段切分同一路段的所有帧只能整体进训练集或整体进验证集。过滤无效标注。空txt文件直接删掉如果删掉后某个类别一张都不剩要回头检查是不是标注软件漏标了。数据增强方面道路缺陷场景有几个独特原则。翻转和旋转是安全的因为路面病害不具有方向语义横向裂缝转90度就变成纵向裂缝类别定义本来就和朝向相关训练时多转反而让模型更鲁棒。亮度对比度抖动可以用但要保守我一般把亮度扰动控制在正负15%以内太大容易把裂缝和阴影混在一起。颜色抖动要非常克制沥青路面的颜色是一个很有用的先验把路面变成黄色或红色纯属自找麻烦。Mosaic增强在这个场景里特别好用尤其是把不同光照下的路面块拼在一起能显著提升模型对不同天气的适应能力。还有一个容易被忽略的操作是最后几个epoch关闭Mosaic因为Mosaic产生的合成图像与真实分布略有偏差全阶段开着会让最终精度变差。对于坑槽这类大目标少、小目标多的类别可以用过采样策略统计每个类别的标注实例数坑槽实例少就多采样含坑槽的图。也可以用最朴实的方法——复制粘贴把坑槽mask区域的像素粘贴到正常路面上同时生成对应的分割标注。这样做出来的合成样本对提升坑槽类的召回率非常明显因为坑槽的视觉特征本质上是路面局部缺失贴到任意平整路面上都不违和。3. YOLOv8-seg结构拆解改模型之前要认清三个位置这一节是给想动手改模型的人看的。不做改进、直接用原版训练部署的朋友可以跳过但如果你打算把YOLOv8-seg上的各种改进点用起来建议先把结构脉络摸清楚否则改到哪里出了问题都不知道。3.1 Backbone、Neck、Head各管什么事从整体看YOLOv8-seg由三块组成Backbone负责提取图像特征。输入图像经过一系列卷积和下采样不断把空间分辨率降低、通道数升高。这一步输出的是几个不同尺度的特征图浅层特征保留细节但语义弱深层特征语义强但空间位置模糊。Neck负责多尺度特征融合。它把backbone的多个层输出组合起来让浅层细节信息和深层语义信息能互相传递。原版用的是一种自顶向下再自底向上的路径聚合结构目标是让每一层特征都既能看到细节又能理解语义。Head负责最终的预测。YOLOv8-seg的head分三个分支分类分支输出每个候选区域的类别概率回归分支输出边界框分割分支输出mask。分割分支的特殊之处是它不是直接对每个像素做稠密分类而是先生成一组低分辨率的原型mask再用一个掩码系数组合出目标mask。这个设计大幅减少了计算量代价是mask边缘精度有上限。明白了这个流程你在选改进点时思路就清晰了想让模型更精准地判断这是裂缝改backbone或注意力想让模型不把长裂缝漏掉改neck和特征融合想让小目标边缘更清晰就要在mask分支和多尺度细节上想办法。3.2 道路缺陷场景下原版结构的三个短板原版YOLOv8-seg在COCO这种通用数据集上表现不错但搬到道路缺陷上我有三个直观的体感问题细小裂缝很容易在深层特征中消失。裂缝宽度只有几个像素经过多次下采样后对应的特征响应已经弱到接近噪声neck就算融合了浅层特征信噪比也很低。mask边缘精度不够。原型mask机制输出的是低分辨率mask再上采样对边界模糊的裂缝反而有利不容易过拟合标注噪声但对形状锐利的坑槽边缘来说会出现明显的锯齿和贴边不准。长裂缝被切碎的问题。一条裂缝可能跨越大半个画面如果训练时下采样或者切片导致目标不完整模型倾向于把裂缝识别成多段独立目标。这个不只是结构问题和数据尺度也有关系后面会讲。3.3 改进点该往哪里发力我把道路缺陷场景下常见的改进点按作用位置整理成了一张表方便对照。这里特别说一下一个改进点不是只能作用于一个位置比如某些注意力机制可以用在backbone也可以用在neck但核心逻辑是一样的——它想增强哪部分信息的表达。改进位置典型做法对应问题输入侧多尺度训练、更优的预处理、拼接大图裂缝太小、目标不完整Backbone换更强或更轻量的主干、加入注意力语义信息不足、计算量过大NeckEfficientRepBiPAN、AFPN、BiFPN、加权融合多尺度特征融合不充分Head动态检测头、解耦头优化、mask分支优化分类/回归/分割冲突LossWIoU、SIoU、MPDIoU等边界框损失边界框回归不精准训练策略冻结训练、EMA、衰减Mosaic、蒸馏收敛慢、泛化差我在多次实验里的体会是当你的数据集只有几千张图时上来就换backbone往往不如改neck收益稳定。原因是backbone换了之后预训练权重的分布变了需要在你的小数据集上重新适应很容易训练不稳定而neck改动是在同一个backbone之上的组合方式调整可以保留绝大部分预训练能力改动风险小。真实工程项目里稳定上分比花哨重要得多。4. 改进点组合逻辑EfficientRepBiPAN、AFPN-P345和几十种改进的正确打开方式现在聊最有争议的部分一个仓库里塞了五十几种改进点是不是全堆上效果最好我直接说结论不是。堆改进点的边际收益是递减的而且负向相互干扰很常见。我自己踩过一个坑在baseline上加完轻量化backbone、双注意力、新loss、动态head之后mAP反而掉了0.8个点。原因是每个模块都在争夺有限的特征表达能力最终学出来的表征变得碎片化。4.1 EfficientRepBiPAN和AFPN-P345分别解决什么问题EfficientRepBiPAN这个组合词拆开看是两层意思EfficientRep表示一种高效重参数化结构BiPAN表示双向特征融合的网络。它的核心思想是用重参数化卷积来构造可融合的特征模块训练时是一个多分支结构推理时可以等价折叠成单分支速度无损。放在neck里它既增强了特征融合的路径多样性又不会让推理变慢太多属于稳赚型改进适合作为系统默认配置。AFPN是渐进式特征金字塔P345指的是取3、4、5三个尺度的特征层参与渐进融合。AFPN和传统自顶向下融合的思路不同它不是一步到位把最深特征直接传给最浅层而是让特征从浅到深逐级逐步融合每一级只做局部的信息融合最后再统一汇总。这种设计对道路场景特别受用因为裂缝和坑槽的尺度跨度极大渐进融合可以保留中间尺度的信息避免深层特征越级传递时把小目标细节冲掉。从我跑通的经验看这两个改进点是可以叠加的一个偏特征融合路径一个偏多尺度渐进策略作用并不完全重叠。但要注意叠加之后训练时间会明显变长调参的耐心得跟上。4.2 三套实用的改进组合模板根据项目诉求不同我把常见改进点组合成三套方案。你可以直接照抄配置再微调第一套是精度优先型适合离线分析或者对实时性不敏感的巡检场景。以原版为baselineneck换成AFPN-P345在backbone倒数第二层插入一个轻量注意力loss换成带聚焦权重的边界框损失训练时开启多尺度。这套方案我实测的收益是mAP50提升2到3个点左右代价是推理耗时增加约20%。第二套是速度优先型适合边缘设备部署。backbone换成轻量化主干neck用EfficientRepBiPAN的轻量配置剪掉一部分冗余通道再配合蒸馏训练用一个重模型当老师轻模型当学生。这套方案的收益是推理速度提升50%以上精度只掉1到1.5个点在嵌入式环境下远比原版好用。第三套是论文型适合要发文章的。思路是不要堆点而是要围绕一个核心创新点展开。比如你主推渐进式特征融合就把AFPN-P345作为核心模块配套做消融baseline、只加AFPN、AFPN注意力、AFPN新loss每级汇报指标增量。审稿人看的是清晰的故事线不是你有多少种改进。五十几种改进点在这个场景下反而是弹药库你只需要挑互相支撑的2到3个组建论文实验矩阵。4.3 消融实验的正确做法无论哪套方案消融实验都要记录完整。我给自己的纸面模板固定这几列模型配置、参数量、浮点运算量、mAP50、mAP50-95、mask精确度、单帧推理耗时、显存占用。这八个指标缺一不可在论文或项目汇报里都有用。做法是每加一个改进点就完整训练一次不要为了省时间在同一个模型里一次加三个点。虽然训练耗时折磨人但这是唯一能定位哪些改进带来收益、哪些改进是负优化的办法。还有一个很多人忽视的细节消融实验的随机性。我用同一套配置跑过两次mAP波动有0.3到0.5个点如果你一个改进点的增量只有0.2个点那完全在噪声范围内。新手最好每个配置跑两次取平均或者至少固定随机种子再对比否则你会拿着一个假阳性结论去发论文复现阶段翻车是迟早的事。5. 一键训练闭环从数据集划分到拿到可用权重的工程细节一键训练听起来简单背后其实是把数据集划分、配置读取、训练、验证、导出整个流程串成一个脚本。核心理念是哪怕是完全没接触过项目的新人双击训练入口也能跑通。下面说几个我在工程化过程中觉得最关键的细节。5.1 数据划分不能按数组下标切一键脚本的第一步是划分数据集。网上很多demo就是按文件名序号从头到80%做训练、剩下20%做验证这在道路视频数据上是灾难级的错误。前面说过同一路段连续帧高度相似按序号切很容易把同一段路一分两半验证集指标虚高。正确做法是按路段ID分文件名里前缀相同的图视为同一段整段分配。脚本里我会先提取每个样本的场景ID字段按场景ID分组再以组为单位划分。目录结构也建议固定避免路径填错。一个简洁的布局如下dataset/ images/ train/xxxx.jpg val/xxxx.jpg labels/ train/xxxx.txt val/xxxx.txt road_defect.yamlyaml里写类别数和类别名训练脚本自动读取。注意YOLO格式要求类别编号从0开始连续编号类别名不要带中文和空格否则脚本解析会出现各种玄学错误。5.2 超参数怎么定在道路缺陷这个场景下我最常用的初始参数组合是这样的图像尺寸默认640。如果你的显卡显存够而且裂缝占比小、细节要求高可以试试960甚至1280。高分辨率对细裂缝的召回率提升很明显代价是训练时间和显存翻倍。建议先在640上把结构调通最后再用高分辨率精调。训练轮数我建议给300轮配合早停。道路缺陷不是特别复杂的数据集模型通常在150到200轮内收敛早停设置20轮左右可以避免无效等待。批量大小显存允许的情况下尽量大至少8以上太小会导致mAP波动明显。优化器AdamW在这个任务上通常比SGD收敛更快、更稳对学习率没有那么敏感新手建议直接用AdamW。学习率从0.001起步配合余弦退火调度。权重衰减设置在0.0005附近太大容易欠拟合。一个只说一遍的经验训练过程中不要只看loss曲线。loss下降得漂亮可能只是拟合了训练集要定期在验证集上做一次评估把验证集预测结果可视化保存出来直接看图。我会把每个epoch的PR曲线、mAP曲线都输出到日志目录每10个epoch保存一批带mask叠加的验证图。你很快就会发现loss在降但长裂缝被截断的问题依然存在、阴影区域的误检依然在跳——这些只能通过看图才能定位。5.3 预训练与断点续训从零训练YOLOv8-seg在道路数据上是很蠢的做法务必加载COCO预训练权重。但全量加载也有个细节前10到15个epoch可以将backbone冻结只训练neck和head让模型先把道路缺陷长什么样的注意力集中到语义和空间层上之后再解冻所有层微调。冻结阶段的学习率可以调低一些防止预训练特征被大步长更新直接冲坏。断点续训是工程标配。训练脚本里每个epoch保存一次权重只保留最近两个epoch最优epoch避免磁盘爆炸。写训练脚本的时候续训的加载逻辑一定要先看权重里有没有优化器状态只加载模型权重的话学习率调度会乱掉后半程训练基本报废。我把这个坑踩过一次后来每次续训都会在日志里打印loaded optimizer state来确认。训练完成后不要着急做最终评估。把最优权重复制到单独的runs/exp_best目录用测试集不是验证集跑一次最终评估记录报告。我习惯同时导出PR曲线图、混淆矩阵和几张典型的可视化预测图这三个东西是项目汇报时最有说服力的材料。6. 部署链路从模型文件到能用的路面缺陷清单训练完模型只是把一个能识别缺陷的算法做出来了。真要拿去巡检用还要走完部署和落地的最后一段路这里面的坑不比训练少。6.1 导出和推理加速YOLOv8-seg训练产物默认是框架格式的权重文件部署第一步是导出为通用的ONNX格式。导出时要注意两个参数一个是动态尺寸因为推理时输入图像尺寸不固定建议enable动态batch和动态高宽另一个是mask分支的导出完整性有些精简操作会把mask分支丢掉导出后模型只剩检测框没有mask等于白做分割。导出的ONNX可以直接跑CPU推理但速度一般。要上边缘设备或需要实时处理就得用推理引擎优化并转成半精度。我的经验是对裂缝这类细长目标半精度转换要格外谨慎。有几次转完半精度之后坑槽的mask边缘出现了断裂和锯齿肉眼可见的退化。如果出现这种情况优先检查是不是预处理归一化参数被量化弄乱了其次再退回全精度。全精度慢一点但输出稳定比上线后效果拉胯强得多。6.2 部署形态和后处理流程部署形态取决于使用场景。离线巡检可以直接用PC或边缘板卡处理静态图集在线巡检则需要一个API服务接收前端上传或巡检车实时回传的视频帧返回检测结果。API服务这块我建议做成标准的请求-响应格式输入一张图输出检测到的每个缺陷的类别、置信度、mask多边形坐标和统计指标。这里有一个很值得做的后处理环节mask原始输出是像素级二值图业务方一般不关心mask图他们关心这处病害有多严重。所以后处理脚本会把mask转成多边形坐标然后计算缺陷像素面积再按图像空间分辨率换算成实际面积需要提前标定每像素对应多少毫米裂缝平均宽度用骨架化算法提取中线后计算宽度分布坑槽开口尺寸等效直径或外接矩形长宽缺陷空间位置图像坐标映射到实际路面坐标配合采集设备定位。这些数据最终汇总成一个路面缺陷清单按路段ID归档能直接进入养护工单系统。到了这一步算法才真正变成了可用的工具而不只是一堆跑分指标。6.3 部署后依然要盯的两个问题第一个问题是模型漂移。路面材料多种多样有沥青路、水泥路、碎石路不同路况下缺陷外观差异很大。你在训练集里见得多的是沥青路裂缝到了水泥路上就可能疯狂漏检。部署后一定要留一个反馈通道现场巡检发现模型漏检的图片定期收集回来重训练迭代。这个反馈循环比任何模型改进都重要。第二个问题是输出不稳定。同一处坑槽车载相机换个角度拍模型可能一会儿识别成坑槽一会儿识别成阴影。针对这种情况我建议在业务层做多帧融合同一路段的多帧检测结果做投票和几何一致性校验只有超过一定置信度和帧数的缺陷才进入最终清单。这虽然会过滤掉部分真阳性但大幅度减少了误报养护部门那边的好感度直线上升。最后再分享一点个人体会这套系统做到后面我最大的感触是道路缺陷分割的瓶颈早就不是在模型结构上而是在数据质量和工程闭环上。数据集整理得好不好直接决定模型上限部署后能不能持续收集真实反馈决定系统会不会越用越准。如果你手头也有一份标注不规范的数据别急着上各种改进点先把标注的可见错误清完、把视频切分逻辑改对你可能会惊喜地发现mAP已经涨了一截。另一个特别想说的是半自动标注模型训练完首轮后用它在未标注图片上预测mask人工只需要修正错误部分。对两万张路面图来说全手工标注可能要三周半自动标注一周就能做完而且标注一致性更高。这也是我后续扩充数据集一定会走的路。