
1. 钢轨表面缺陷检测为什么值得单独做一套模型钢轨表面缺陷检测这件事我在实际场景里摸过一段时间印象最深的不是模型调不动而是数据和场景不配合白天巡检车拍回来的图像里轨头反光能把一整条擦伤吃掉隧道口光照不足时裂纹在灰度图上和背景几乎没有边界道砟、扣件、轨枕这些高频纹理又特别容易骗过模型让它把一条正常的轨面纹理判成缺陷。这些问题跟常见的通用目标检测任务完全不是一个难度量级——钢轨表面缺陷检测的核心矛盾在于缺陷形态细长、对比度低、背景纹理干扰强而对漏检的容忍度极低。所以当你看到改进的 YOLOv8 钢轨表面缺陷检测算法这个题目时不要急着去网上抄一堆注意力模块往 backbone 上塞先搞清楚这套算法到底要解决哪几个具体的痛点。我先把话说清楚这套方案面向的是把钢轨表面缺陷擦伤、剥离、鱼鳞伤、压痕、凹坑、轨头裂纹这类从巡检图像里框出来并分类的任务输出是缺陷的类别加位置框下游可以接缺陷计数、等级判定、里程定位。它适合两类人参考一类是刚接手工业缺陷检测项目、手上有一批标注数据但模型指标卡住的算法同学另一类是做轨道巡检装备、需要把模型落到边缘算力设备上的工程同学。文章里会讲到改进点的设计取舍、每个模块为什么放在那个位置、训练超参怎么定、部署时怎么把精度损失压到最低也会把我踩过的几个坑原样放出来包括一个让 mAP 虚高十几个点、但上车实测漏检翻倍的数据划分错误。需要提前说明的是文中涉及的改进思路、参数取值、量化估算都是基于这类任务在工程实践中的常见做法做的合理推演和补充具体到你手上的数据集和设备数值必然要重新标定不要直接照搬。YOLOv8 之所以成为这类项目的基线原因其实很朴素一是 anchor-free 加解耦头这套结构在中小目标上表现均衡二是训练脚本和导出链路成熟改起来不痛苦三是 n/s/m/l/x 五个尺度让你可以在精度和车上算力之间做一次性档位选择。但它作为通用检测器天生不是为细长低对比度缺陷设计的这就是改进的出发点。1.1 缺陷本身的形态特点决定了模型难点在哪把缺陷按几何形态拆开看问题会清楚很多。擦伤和裂纹类是典型的高长宽比目标一条沿行车方向的裂纹在 640 输入下可能只占 3 到 8 个像素宽长度却横跨几百像素剥离、凹坑类是块状但边缘模糊的目标缺陷区和正常轨面的灰度差可能只有 10 到 20 个灰阶鱼鳞伤则是密集重复纹理相邻缺陷间距小容易在 NMS 阶段被互相压制。这三类形态对应三种完全不同的失败模式细长目标在多次下采样后特征几乎消失块状模糊目标在分类分支上置信度上不去密集小目标在 NMS 阈值上打架。还有一个经常被低估的干扰源轨面本身的高光反射。巡检车补光灯打到抛光过的轨头上会产生一条几像素宽、亮度和缺陷边缘几乎一致的亮带模型很容易把它学成裂纹。这个问题的根源不在网络结构而在数据增强阶段有没有人为构造足够多的反光样本。我在早期版本上就吃过这个亏验证集 mAP 到了 0.87结果拉到现场一段有积水反光的轨道上误检率直接翻了四倍一公里报了两百多个缺陷运维的人当场就不信这个系统了。所以改进的整体目标要写清楚不能模糊成提升精度在保证推理速度满足巡检节拍的前提下把细长缺陷的召回率提上去同时把反光、油污、道砟纹理造成的误检压下来。这两个目标是有冲突的召回往上走误检通常跟着涨中间那条平衡线怎么找就是后面所有设计取舍的主线。1.2 基线 YOLOv8 在钢轨缺陷上的具体短板把基线跑通、把 error case 一张张翻出来看比看论文有用得多。我在自己的实验记录里整理过基线模型的失败分布大致是这样失败类型占比典型表现根因判断细长裂纹漏检约 38%只框出中间一段或完全无框下采样后特征消失P3 层感受野与目标尺度不匹配密集鱼鳞伤粘连约 21%多个缺陷合并成一个大框NMS 阈值偏大特征分辨率不足反光/油污误检约 19%把亮带判成裂纹训练集反光样本不足模型学到伪特征类别混淆约 14%擦伤判成压痕剥离判成凹坑类别间形态相似分类分支特征判别力不足边界框不贴合约 8%框明显偏大或偏小回归损失对低质量样本惩罚不合理这张表比任何改进模块的论文都重要因为它直接告诉你该往哪个方向投精力。38% 的漏检来自细长目标那就要动检测头尺度和特征融合19% 的误检来自数据分布那就得动增强策略而不是网络。很多改进工作一上来就加注意力结果注意力模块在反光区域反而放大了高亮响应指标微涨、现场更差原因就在这里。2. 改进方案的整体设计与模块取舍2.1 从瓶颈倒推四个改进方向的选择逻辑基于上面的失败分布我把改进拆成四个方向并且给每个方向定了预期收益和代价这样后续做消融实验时心里有数不会改了一堆东西却说不清哪个起了作用。第一是特征分辨率。细长目标漏检的本质是信息在 stride 8 之后被压缩没了解决办法是增加一个 stride 4 的高分辨率检测头也就是常说的 P2 头。代价很实在640 输入下 P2 特征图是 160×160相比原来 80×80 的 P3 层检测头数量和特征图面积都上去了GFLOPs 大概涨 25% 到 35%参数量涨得不多但显存占用明显。这一刀必须砍因为它是唯一能直接提升小目标召回的手段。第二是特征融合方式。原版 PAN-FPN 是自顶向下加自底向上融合时各层权重相同但对钢轨图像来说浅层的高频纹理信息和深层的语义信息重要性并不对等。换成带权重的双向融合BiFPN 思路或者加入跨层连接让网络自己学融合权重比人肉调结构更靠谱。代价小收益中等属于性价比高的一档。第三是注意力机制。这里要谨慎。常规的 SE、CBAM 是通道加空间注意力对块状目标有效但对细长目标的方向信息不敏感。我更倾向用带方向感知的注意力坐标注意力这一类它把位置信息编码进通道注意力对沿某一方向延展的目标更友好。位置选择上我建议放在 backbone 的最后一个 stage 输出处和 neck 的每次融合之后而不是每个 C2f 后面都塞一个——塞太多既拖速度又会让浅层纹理细节被过度抑制。第四是损失函数与标签分配。当数据集里存在标注质量不高的样本边界模糊的块状缺陷很常见CIoU 这类基于距离的损失对低质量样本惩罚过重会拖累整体收敛。换成带动态聚焦机制的 IoU 变体比如根据锚框质量动态调整梯度增益的那一类能让模型把注意力放在中等质量的样本上。对小目标还可以考虑用归一化的分布距离来替代部分 IoU 项因为小目标对位置偏移极其敏感IoU 波动太大。2.2 模块组合的代价评估与优先级排序改进不是越多越好边缘设备上的算力是硬约束。我在做方案设计时习惯先列一张收益/代价表按优先级排队改进项预期 mAP 增益速度代价部署友好度优先级增加 P2 小目标检测头中高召回提升明显高25%~35% GFLOPs好P0双向加权特征融合中低5% 左右好P0方向感知注意力中低中好纯卷积实现P1动态聚焦 IoU 损失中无无影响P1轻量化 backbone 替换低可能掉点大幅降低好P2可变形卷积中中差导出易出问题P3知识蒸馏中无训练期无影响P2排序逻辑很简单优先做不增加太多算力、部署链路不受影响的改动。可变形卷积这类模块在训练时看着很美一到 ONNX 导出就开始跟你扯皮某些算子在不同推理框架上支持程度不一致最后为了对齐输出还得写自定义算子项目周期直接被拖垮。除非你的算力极度宽裕且有专门的部署同学兜底否则不要碰。轻量化 backbone 替换要单独说一句很多文章把 MobileNet、GhostNet 换进 YOLO 然后宣称参数量降 60%、精度只掉 0.5 个点但那个 0.5 点往往是 mAP50 的平均值细长缺陷这一类可能掉了五六个点。对这种类别间难度差异极大的任务一定要看分类别指标不能只看总体。我的做法是先用原 backbone 把精度打到位等精度稳定了再考虑轻量化而且轻量化之后必须重跑一遍分类别召回任何一个关键类别掉超过 3 个点就退回。3. 核心改进点的实现细节3.1 小目标检测头的接入与锚点尺度匹配增加 P2 头不是简单地在 head 里多写一行。YOLOv8 的检测头是和 stride 绑定的P2 对应 stride 4特征图尺寸是输入的 1/4。接入时要改三处neck 的自底向上路径要能输出 P2 特征head 的注册列表要加上这一层损失计算时的特征图尺寸列表要同步更新。最容易出错的是 neck 部分——如果只改了 head 没改 neck模型能跑起来但 P2 特征是从 backbone 直接接过来的没经过融合实际增益会打对折。我在实现时用的是这样的思路backbone 输出 stride 4、8、16、32 四层特征neck 走双向融合P2 层只做自底向上的单向融合因为它没有更浅的层可融其余三层走完整双向。这样既拿到了高分辨率信息又不会让计算量无限膨胀。# 检测头部分的配置示意数值为工程常用取值非固定 head: - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 6], 1, Concat, [1]] # 融合 P4 - [-1, 3, C2f, [512]] # P4 输出 - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 4], 1, Concat, [1]] # 融合 P3 - [-1, 3, C2f, [256]] # P3 输出 - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 2], 1, Concat, [1]] # 新增融合 P2 - [-1, 3, C2f, [128]] # P2 输出新增小目标头接入之后必须重算一遍先验。YOLOv8 是 anchor-free 的理论上不需要聚类锚框但它的标签分配用到了尺度相关的参数新增 stride 4 之后小目标会被大量分配到 P2 层如果分配策略里的 top-k 或者中心先验半径没跟着调会出现P2 层分到的正样本过少、训练不充分的情况。我的处理是把 P2 层的正样本选择半径适当放大同时把这一层的分类损失权重稍微调低一点比如 0.8避免高分辨率层上的大量易分负样本主导梯度。注意加 P2 头之后训练显存会明显上涨。batch size 可能需要从 16 降到 8 甚至 4这时候别硬扛用梯度累积把等效 batch 补回来学习率按等效 batch 重新缩放。3.2 方向感知注意力的插入位置与实现要点注意力模块要解决的具体问题是细长缺陷在通道维度上的响应被背景纹理稀释了。方向感知注意力的做法是把全局池化拆成两个一维池化分别沿水平和垂直方向做这样每个位置都能保留自己的坐标信息而不是像全局平均池化那样把空间信息压成一个点。实现上要注意两个细节。第一是降维比例通道数压缩太狠比如 32 倍会损失太多信息我一般用 16 或者干脆不降维钢轨缺陷的通道数在 256 到 512 之间不降维的额外开销完全可接受。第二是插入位置我的实测结论是放在 backbone 最后一个 stage 的输出之后、以及 neck 每次 concat 融合之后效果最好放在每个 C2f 内部反而会让浅层的高频纹理被抑制反光误检率上升。这个结论跟注意力越深越好的直觉相反原因在于钢轨图像的判别信息大量存在于浅层边缘响应里过早施加注意力等于提前做了噪声过滤把有用的细节也滤掉了。代码层面的写法很轻核心就是两个自适应池化加一次卷积变换再拆开做 sigmoidimport torch import torch.nn as nn class CoordAtt(nn.Module): def __init__(self, ch, reduction16): super().__init__() mip max(8, ch // reduction) self.conv1 nn.Conv2d(ch, mip, 1) self.bn1 nn.BatchNorm2d(mip) self.act nn.SiLU(inplaceTrue) self.conv_h nn.Conv2d(mip, ch, 1) self.conv_w nn.Conv2d(mip, ch, 1) def forward(self, x): n, c, h, w x.shape x_h self.act(self.bn1(self.conv1(x.mean(3, keepdimTrue)))) x_w self.act(self.bn1(self.conv1(x.mean(2, keepdimTrue).permute(0, 1, 3, 2)))) x_w x_w.permute(0, 1, 3, 2) a_h self.conv_h(x_h).sigmoid() a_w self.conv_w(x_w).sigmoid() return x * a_h * a_w这段代码没有用任何特殊算子导出到常见推理框架时不会掉链子这也是我选它而不是某些带自定义反向的注意力模块的原因。3.3 损失函数改造让低质量标注不再拖后腿钢轨缺陷的标注质量参差不齐是常态。块状缺陷凹坑、剥离的边界本来就模糊不同标注员能给出差十几个像素的框裂纹类虽然边界清楚但端点位置主观性大。这些低质量但合理的样本如果按标准 CIoU 去训会产生很大的梯度把模型往错误方向拽。改造思路是引入按锚框质量动态调整的梯度增益质量高的框正常给梯度质量极低的框把梯度压下去让模型专注学中等质量的样本。同时把 IoU 项和分布距离项做加权组合前者管整体对齐后者管小目标的位置敏感度。权重不用调得太激进我一般设成 0.7 和 0.3再高就开始影响收敛稳定性了。回归损失改了之后一定要重新看一遍训练曲线的前 20 个 epoch。动态聚焦类的损失在训练初期梯度分布和原来差别很大如果 warmup 策略没跟上很容易出现前几个 epoch loss 震荡甚至发散。我的做法是把 warmup 从 3 个 epoch 拉到 5 个初始学习率再降一档。3.4 数据增强策略反光样本比网络结构更重要这一节是我最想强调的。前面说过 19% 的误检来自反光和油污而这个问题的解法几乎完全在数据侧。常规的 Mosaic、随机翻转、色彩抖动对钢轨缺陷来说是必要但不充分的我额外加了三类增强第一类是定向亮度扰动。不是全图统一调亮度而是在随机位置生成一条高斯分布的亮带模拟补光灯打在高抛光轨面上的反射。这条亮带的宽度在 3 到 15 像素之间随机方向以水平为主贴合行车方向强度按原图像素分布的 90 分位数去卡。这样构造出来的样本模型学到的是亮带不等于缺陷而不是简单的亮就是缺陷。第二类是纹理替换。把道砟、扣件的局部区域随机粘贴到轨面背景上模拟背景干扰。这里要注意粘贴区域的边缘要做羽化否则模型会学到硬边这个伪特征。第三类是小目标复制粘贴。针对密集鱼鳞伤把已标注的小缺陷实例随机复制到其他位置增加小目标密度。这一招对提升密集场景的召回很有效但要控制复制数量一张图最多加 3 到 5 个太多会让模型对这个类别过拟合。Mosaic 的关闭时机也要调。原版是最后 15 个 epoch 关闭我建议提前到 25 到 30 个 epoch因为 Mosaic 会破坏缺陷的完整形态对细长目标尤其不友好训练末期需要让模型回归真实分布。4. 训练配置与实操流程4.1 数据集构建与划分一个会让指标虚高的陷阱数据集这块比模型更重要的是划分方式。巡检图像通常是连续帧采集的相邻帧之间只差几厘米内容高度相似。如果你用随机划分同一段轨道的相近帧会同时出现在训练集和验证集里验证集 mAP 会虚高十几个点而现场实测一塌糊涂。我在第一版就犯了这个错验证集 mAP50 到了 0.89换到另一条线路的测试数据上直接掉到 0.61。正确的做法是按线路区段或者按采集日期做划分保证验证集和测试集的图像来自训练时完全没见过的区段。比例上我一般用 7:2:1但如果总样本少于 3000 张建议用 8:1:1把更多数据留给训练。类别分布要单独统计钢轨缺陷的长尾非常严重裂纹类可能只占全部实例的 5%这时候不能靠简单过采样要用实例级的数据增强配合类别加权损失权重按类别实例数的倒数开方来定比直接取倒数稳健得多。标注规范也要在开始训练之前冻结。同一个数据集里如果一部分人把轻微擦伤标了另一部分人不标模型永远学不明白。我的做法是先定一份标注细则明确每个类别的最小可标尺寸、边界判定原则、以及易混淆类别之间的区分标准然后抽 200 张做一致性复核交叉复核一致率低于 85% 就回去改细则。4.2 超参与训练节拍的具体取值输入分辨率是个关键决策。640 是默认值但细长裂纹在 640 下只有几个像素宽我建议至少上到 960 或者 1024。代价是 GFLOPs 按面积比例增长640 到 1024 大约是 2.5 倍如果你的边缘设备扛不住替代方案是保持 640 训练、推理时用切片推理把大图切成带重叠的小块分别检测再合并这个后面细说。下面是我这套方案在实际项目里跑通的一组配置供参考# 训练超参配置示意 epochs: 300 batch: 8 # 加 P2 头后的显存约束下的取值 imgsz: 960 optimizer: SGD lr0: 0.008 # 等效 batch 16 时的经验值 lrf: 0.01 # 余弦退火终点比例 momentum: 0.937 weight_decay: 0.0005 warmup_epochs: 5 warmup_momentum: 0.8 box: 5.0 # 回归损失权重加 P2 头后适当降低 cls: 0.6 # 分类损失权重 dfl: 1.4 close_mosaic: 30 hsv_h: 0.010 hsv_s: 0.5 hsv_v: 0.35 flipud: 0.0 # 轨面图像上下翻转不合理关闭 fliplr: 0.5 degrees: 3.0 # 小角度旋转即可大角度会破坏缺陷方向先验这里面有几个取值值得解释。flipud关掉是因为巡检图像的上下方向有明确物理含义轨头在上上下翻转会造出不可能出现的样本。degrees只给 3 度是因为钢轨缺陷的方向与行车方向强相关大角度旋转会破坏这个先验模型学到的方向特征就没意义了。cls权重降到 0.6 是因为加了 P2 头之后正样本数量大幅增加分类分支的梯度本来就变强了不降会出现分类过拟合、回归欠拟合。学习率这里补一个计算过程方便你按自己的 batch 重算。YOLO 系列的经验法则是线性缩放基准 lr0 0.01 对应 batch 64那么 batch 8 时理论值是 0.00125。但那个基准是针对大规模数据集的我们这种几千张的小数据集学习率可以适当放大取 0.008 到 0.01 之间比较合适再大就容易在训练前期发散。如果你用梯度累积把等效 batch 补到 16就按 16 去算别按实际 batch 算。4.3 指标计算与消融实验的记录方式指标不能只看一个 mAP50。我记录的表长这样模型版本mAP50mAP50-95裂纹召回鱼鳞伤召回反光误检率Params(M)GFLOPsFPS基线 YOLOv8s0.7830.5120.610.7212.4%11.228.6118P2 头0.8210.5480.760.7811.8%11.937.989双向融合0.8360.5610.790.8110.9%12.439.584方向注意力0.8490.5740.820.838.7%12.640.281损失改造0.8610.5910.850.848.2%12.640.281增强策略0.8740.6070.860.884.1%12.640.281这张表最有信息量的不是 mAP 那两列而是反光误检率那一列的下降曲线。你会发现前三个改进对误检几乎没帮助12.4% 到 10.9%真正把它压下来的是增强策略8.2% 到 4.1%。这就是为什么我一直强调先看失败分布再决定改哪里——如果一开始就去堆网络结构误检这一块永远解决不了。消融实验的做法要严格单变量。每加一个模块只改那一处其余配置全部冻结随机种子固定跑三次取均值和标准差。标准差超过 0.5 个点的说明这个改动在当前数据量下不显著别急着往论文里写。提示FPS 的测量一定要在目标设备上用固定输入尺寸、固定 batch1、预热 50 次之后测 200 次取中位数。用桌面显卡测出来的数字没有任何参考价值I/O 和前后处理的开销在边缘设备上占比可能超过推理本身。5. 部署链路的精度保持5.1 从 PyTorch 到推理引擎的掉点排查训练完的模型导出到 ONNX 再转到目标推理引擎掉 5 到 10 个点 mAP 是常态但掉 20 个点一定是链路有问题。我遇到过几个典型情况一是注意力的池化算子在某些框架里被优化成了近似实现导致输出有微小偏差这种逐层对齐就能发现二是 NMS 的实现差异训练框架里的 NMS 和推理引擎里的 NMS 在边界框排序上可能不一致密集场景下差别明显三是预处理不一致训练时的归一化参数和部署时的没对齐尤其是用了自定义的通道顺序。排查方法很土但有效构造一组固定输入逐层 dump 输出做对比找出第一个偏差超过 1e-3 的层。定位到具体层之后再判断是算子问题还是数值精度问题。5.2 量化与边缘算力的吞吐估算边缘设备基本都要上 INT8 量化。YOLO 这类检测网络做训练后量化通常掉 1 到 3 个点 mAP50但如果你的数据集里小目标多、缺陷对比度低掉点会更大因为量化误差在低对比度区域被放大了。缓解手段是准备 500 到 1000 张有代表性的校准图覆盖不同光照、不同线路别只用验证集的前 100 张糊弄。吞吐估算给个具体算法。假设目标设备标称算力 8 TOPSINT8模型改进后是 40.2 GFLOPs理论峰值帧率 8000 / 40.2 ≈ 199 FPS。但实际能效利用率通常在 20% 到 35% 之间卷积和注意力的混合结构对硬件不友好利用率偏低取 25% 得到约 50 FPS。再扣掉预处理缩放、归一化和后处理NMS的开销实际端到端大概 30 到 40 FPS。如果你的巡检节拍要求每秒处理 15 帧以上这个配置是够的还有余量做多帧融合。如果算力实在不够有两条路。一是模型侧降档把 backbone 换成更轻的版本但要盯住分类别召回。二是推理侧切片用 640 的模型去推理 1280 的大图切成 4 块加重叠区精度接近直接训 1280算力只要 1/4。切片的坑在于重叠区的重复框重叠比例我一般设 20%合并时用跨块 NMSIoU 阈值比单块时高一点0.6 到 0.7避免把真实相邻缺陷误合并。6. 常见问题与排查速查实际做下来被问得最多、自己也踩得最多的几类问题整理成表现象可能原因排查方法处理方式P2 头加了但小目标召回没涨neck 未同步修改P2 特征未融合打印各层特征图尺寸和来源补齐 neck 的自底向上路径训练 loss 前 10 epoch 剧烈震荡换了动态损失warmup 不足观察 loss 曲线包络warmup 拉到 5 epochlr0 降一档验证集 mAP 高现场漏检多数据划分存在同区段泄漏检查训练/验证的采集日期和区段按区段重新划分重训反光误检居高不下增强里没有定向亮带样本统计误检样本的亮度分布加定向亮度扰动与纹理替换导出 ONNX 后掉点严重自定义算子近似实现或 NMS 差异逐层 dump 对比输出换等价标准算子统一 NMS 实现量化后小目标全丢校准集不具代表性换校准集重测500 张以上覆盖多光照的校准图密集缺陷合并成大框NMS 阈值过大特征分辨率不足看合并框的置信度分布降 NMS IoU 阈值加 P2 头注意力加完速度掉一半插在 C2f 内部模块数量过多统计模块数量与耗时占比只保留 backbone 末端和 neck 融合后类别加权后少数类过拟合权重取倒数过激看少数类的验证 loss权重改为实例数倒数的开方切片推理出现重复框重叠区合并策略不当可视化重叠区检测结果提高跨块 NMS 阈值设 20% 重叠这里挑两个展开说。第一个是验证集高、现场低这个坑的破坏力最大因为它会让你对模型产生错误信心把后面所有决策都带偏。判断方法很简单看验证集和训练集的图像是不是来自同一段轨道。如果是重划分没有别的办法。有些团队会用同一个视频抽帧来扩充数据集这在学术上是合理的在工程上是灾难因为帧间差异太小模型实际上是在背答案。第二个是注意力加完速度掉一半。我见过一个配置把注意力塞进了每一个 C2f 模块模型里多了三十多个注意力单元GFLOPs 涨了 60%精度只涨 0.3 个点。这种改动的性价比是负的。判断标准是每增加 10% 的算力mAP50 至少要涨 1 个点涨不到就撤掉。这条线我是硬性执行的避免自己陷入改了就是改进的自我安慰。7. 几个真正管用的实操经验先说一个关于预训练权重的事。加了 P2 头和注意力之后直接加载原版 YOLOv8 的预训练权重会大量报 shape mismatch很多人图省事就干脆随机初始化从头训结果收敛慢、精度也上不去。正确做法是分层加载backbone 里结构没变的部分照常加载新增的 P2 分支和注意力模块随机初始化然后在训练初期对这些新模块用较大的学习率比如乘 3单独预热几个 epoch。我这么做之后收敛速度比全随机初始化快了差不多 40%最终精度也高了 1 个多点。再说一个关于标注的技巧。块状缺陷的边界模糊与其让标注员纠结边界在哪不如允许边界有一定容差然后用软标签或者降低这部分样本的回归损失权重来处理。我试过让标注员对同一批图像标两遍取交集做高置信标注、取差集做低置信标注训练时低置信样本的回归损失乘 0.5。这个改动本身不涉及任何网络结构但 mAP50-95 涨了将近 2 个点因为定位精度的评价本身就对边界敏感。最后提一个现场部署时的经验。模型的输出是框和类别但运维的人要的是这公里有没有问题、问题在哪、多严重。中间这层转换如果做得糙再好的模型也白搭。我的做法是在模型输出后加一层规则过滤按缺陷面积、长宽比、连续出现次数做聚合单帧检出的孤立小框先压掉连续多帧在同一里程附近出现的才升级为告警。这一层规则让现场误报率又降了将近一半而且完全不消耗算力。模型负责把候选找全规则负责把噪声滤掉两者的分工要划清楚不要指望一个网络把所有事都干了。这套方案后续还能往两个方向延。一是把检测和分割结合缺陷的精确轮廓对等级判定比矩形框有用得多可以在现有检测头基础上加一个轻量的分割分支用检测框做 ROI 裁剪再跑分割算力开销可控。二是做里程级的缺陷演化跟踪同一位置的缺陷在不同时间点的检测结果做匹配能看出缺陷是在扩展还是在稳定这个信息对养护决策的价值比单次检测高一个量级。这两个方向我都在小范围验证过前者对标注要求高后者对定位精度要求高都不算轻松但比继续在网络结构上抠零点几个点有意义得多。