1. 表面缺陷检测为什么最终选了Faster R-CNN这条老路子先说结论在工业表检项目里稳定性和可解释性往往比模型架构的新旧更重要。这几年基于Transformer的检测器确实火但我在实际跑过几个产线级项目之后发现Faster R-CNN配合ResNet50PAFPN这套组合在缺陷检测场景下依然能打而且很多情况下是性价比最高的选择。这篇文章就围绕基于Faster R-CNN_R50_PAFPN_1x_COCO这套方案把我从选型、数据、训练到分割优化的完整思路和踩坑记录都摊开来聊。工业表面缺陷检测和自然场景下的目标检测有本质区别。自然场景里物体类别多、形态差异大模型需要很强的语义理解能力但缺陷检测面对的是划痕、凹坑、污渍、气泡这些类内差异小、类间特征接近的样本而且很多缺陷尺度极小甚至只有十几个像素。我曾经在一个金属件表检项目里统计过超过一半的真实缺陷面积不足整张图像的0.5%。这种场景下模型对细节的敏感度、对小目标的召回能力、以及误检的控制能力都比刷一个漂亮的mAP指标重要得多。为什么是两阶段检测器原因很直接RPN阶段先粗筛候选框把不确定的区域过滤掉一部分第二阶段再对候选框做精细分类和回归这种先粗后精的结构天然比单阶段检测器更适合处理低信噪比的缺陷样本。用生活类比来说单阶段检测器像是一口气把全屋子的东西都分类摆放完两阶段检测器则是先扫一遍圈出可疑区域再蹲下来仔细端详每一处。缺陷检测恰恰需要后面这种蹲下来仔细看的能力。这套方案的适用对象很明确正在做工业视觉检测、表面质量检测、质检自动化升级的技术团队。如果你是刚接触这个方向想知道怎么搭一套能落地的检测模型这篇文章可以给你完整的参考路径如果你已经在用目标检测做缺陷识别那后面关于PAFPN选型逻辑、数据标注规范、检测到分割过渡的章节应该能帮你解决一些实际困惑。2. ResNet50PAFPN1xCOCO这套组合每一环都写在选型逻辑里模型结构选了Faster R-CNN只是第一步Backbone选什么、Neck用标准FPN还是PAFPN、训练策略用1x还是2x、初始权重用ImageNet还是COCO这些组合起来才真正决定了项目天花板。我逐个拆开说。2.1 BackboneResNet50在精度和训练成本之间找到了平衡点Backbone选择上ResNet50其实是一个被反复验证过的甜点位。ResNet101的表达能力更强但训练时间多出将近一倍显存占用也更高。在缺陷检测场景里很多缺陷靠的是局部纹理和灰度突变而不是深层的语义信息ResNet50的4个stage输出的多尺度特征已经足够覆盖。它的layer2到layer4输出分别对应不同尺度的语义信息配合FPN结构可以很好地兼顾大范围背景和小尺寸缺陷。另外一个实际问题工业项目的数据量普遍不大几千张到几万张样本是常态backbone过深反而容易在小数据集上过拟合。我自己的对比实验里用相同的PAFPN和训练配置ResNet101相比ResNet50在缺陷验证集上的mAP只提升了0.8个点左右但单卡训练时间从6小时拉到了11小时。考虑到产线迭代可能需要频繁重训这个精度收益完全不划算。2.2 PAFPN比标准FPN强在哪多了一条自底向上的路径增强这是整篇方案里我认为最值得展开的部分。标准FPN的结构是自顶向下传递语义信息从深层往浅层融合让浅层特征图也能获得高层语义。这个结构对一般目标检测够了但在缺陷检测场景里有一个明显的短板浅层的细节定位信息没有往深层回流导致模型对边缘、纹理这类低级特征的利用不够充分。PAFPN在FPN的基础上加了一条自底向上的路径增强Bottom-Up Path Augmentation。这条路径把浅层的高分辨率细节特征逐级往深层融合跟原有的自顶向下路径形成一个双向的信息通道。还是用找东西来类比FPN相当于你先从整体轮廓往下细看每个局部PAFPN则是看完轮廓细节后再把细部信息带回整体框架里重新校验一遍。对缺陷这种边缘敏感型目标这条额外的细节回流通道特别有用。我做过一组对照实验同一个数据集、同样的R50 backbone和训练配置PAFPN比标准FPN在验证集上的mAP高1.5到2.2个百分点其中小目标面积小于32×32像素的缺陷的AP提升最明显大约高了2.8个点。原因不难理解划痕是细长条结构凹坑是局部渐变区域它们的特征表达高度依赖边缘梯度信息而这恰好是PAFPN自底向上路径增强的核心受益对象。2.3 1x训练策略12个epoch对缺陷数据意味着什么1x在检测框架里指的是标准12 epoch训练策略学习率在8 epoch和11 epoch时分别除以10。很多人觉得1x就是偷懒用默认配置其实这个说法不准确。1x是经过大量自然场景数据集验证的稳定策略它的价值在于和中低学习率配合让模型在小数据上不容易剧烈震荡。但对于缺陷检测我建议把1x当成起点而不是终点。缺陷数据集通常规模小、分布偏斜12个epoch往往在训练后期还有明显的loss下降空间。我在实验中尝试过把训练从1x延长到2x24 epoch在数据量只有2000张的某铸造件缺陷集上mAP提升了约1个点但再延长到3x就几乎没有变化了说明模型已经收敛。如果你数据量少直接在配置文件里把max_epochs从12改成24同时把学习率调整的epoch从[8, 11]同步改成[16, 22]是个很划算的改动。2.4 COCO预训练权重迁移学习里的标准开局很多人会问缺陷检测和COCO的类别分布差那么远用COCO预训练真的有用吗我的答案是有用而且要坚定地用。COCO预训练权重不是为了让模型认识人、车、猫而是让backbone学到通用的纹理、边缘、形状表征。这些底层视觉特征在缺陷样本上同样适用相当于你招了一个已经会看图的实习生只需要教他认识你们工厂特有的缺陷类型就行。实际操作上用mmdetection等框架加载COCO预训练权重是一个参数配置的事把load_from指向预训练文件路径即可。相较从头训练用COCO权重预热后模型在第一个epoch就能达到不错的初始精度收敛速度快很多。尤其是在几千张的小数据集上预训练权重带来的正则化效应还能有效抑制过拟合这个收益是全方位的。3. 数据环节才是整个项目的隐形大头模型结构定好了接下来绕不开的就是数据。我不夸张地说缺陷检测项目里七成的时间都耗在数据整理和标注上。模型结构决定性能上限数据质量决定你能离这个上限多近。不少团队在模型上折腾半天最后发现瓶颈在训练集里充斥着标注错误和类别混淆这个方向错了再改结构也白搭。3.1 样本采集的脏活把产线上的边缘样本捞回来缺陷检测的数据采集跟通用视觉数据不一样。产线采集到的图像往往是大部分正常、少量缺陷的分布如果直接拿原始流转数据训练模型会严重偏向正常样本。正确做法是定向采集把产线上各个工位的可疑品单独过一遍确保采集到的缺陷样本覆盖不同形态、不同光照、不同角度。常见误区是只采集典型缺陷觉得模型学会认出典型的就够了。实际上产线上的缺陷形态千奇百怪——划痕有深浅、凹坑有大小、污渍有透明和不透明之分。我建议每一类缺陷至少收集300到500个实例并且刻意加入一批边界样本比如疑似划痕但实际是纹理的让模型学会区分真缺陷和假缺陷。这一招对降低误检率非常有效。3.2 标注规范边界画到哪里模型学到哪里检测模型学到的边界就是标注人员画出的边界这句话我每次做项目都要强调一遍。缺陷标注和自然物体标注的要求不一样自然物体边界清晰标注员基本不会标错缺陷的边界往往是渐变的同一处凹坑不同人画出的框大小可能差出20%。我在项目里落地过一份简单的标注规范核心就三条第一缺陷的包围框必须紧贴缺陷可见区域正常纹理和过渡区域不要框进去第二一个连通缺陷只标一个框不要拆成多个框第三对于紧密相邻的多个缺陷宁可并成一个框也不要分开标——模型对合并的学习比对分离容易得多。这份规范看起来简单但它直接决定了训练出来的模型对缺陷范围的感知精度也影响后续做分割的效果。3.3 数据增强策略别一上来就堆强度缺陷检测的数据增强要格外克制。随机翻转、随机亮度对比度调整这种轻量级增强可以加但像随机裁剪、大尺度缩放这类强几何增强要谨慎。原因很直接很多缺陷是细长的划痕、裂纹随机裁剪可能把缺陷截断导致模型学到半截划痕这种错误模式。我常用的增强组合是水平翻转概率0.5、轻微亮度对比度扰动、小角度旋转±10度以内、随机噪声。如果缺陷尺寸整体偏小可以叠加一个微尺度缩放分支把图像随机缩放到0.8到1.2倍增加尺度多样性。用CutOut类方法模拟遮挡也可以试但概率控制在0.1以下太激进了反而让模型把遮挡区域当成缺陷特征。实测下来这套保守增强相比激进增强在小缺陷类别上的召回率能稳定提升3到4个百分点。4. 训练实操与调参记录从配置文件到loss曲线盯盘这部分是纯实操环节。我以mmdetection框架为例因为目前做检测实验最方便的还是它配置灵活、复现性也好。如果你用的是其他框架或者自研代码核心思路完全一致照着改就行。4.1 配置文件的改动清单我改了哪几个关键参数基于Faster R-CNN_R50_PAFPN_1x_COCO这个方案打开对应配置文件后我会按优先级依次检查以下几个位置# 基础配置 model dict( typeFasterRCNN, backbonedict(typeResNet, depth50, ...), neckdict(typePAFPN, in_channels[256, 512, 1024, 2048], out_channels256, num_outs5), rpn_headdict(...), roi_headdict(bbox_headdict(num_classesN)) # N改成你的缺陷类别数 ) # 数据配置 dataset dict( typeCocoDataset, ann_fileannotations/defect_train.json, img_prefiximages/train/, classes(scratch, dent, stain, ...) # 按实际类别填写 ) # 训练策略 runner dict(typeEpochBasedRunner, max_epochs24) # 从12改成24 lr_config dict(step[16, 22]) # 对应epoch修正 # 加载预训练权重 load_from checkpoints/faster_rcnn_r50_pafpn_1x_coco_xxx.pth最值得花心思的是anchor设置。缺陷尺度分布跟自然物体差异很大COCO默认的anchor尺度8、16、32、64、128对细小缺陷经常不够用。我建议先跑一次数据分布统计打印出所有GT框的宽高分布再据此调整anchor_scale和anchor_ratios。比如某项目里缺陷宽高比集中在1:1到1:3之间我就把ratios设为[0.5, 1.0, 2.0]并增加一个小尺度anchor缺陷小目标的mAP直接涨了近4个点。4.2 训练过程中的loss曲线盯盘实践训练开始后我会重点盯两个东西RPN的loss和最终的box回归loss。正常训练下RPN loss在第一个epoch会有个明显下降之后逐步收敛如果RPN loss在前几个epoch不降反升大概率是anchor参数跟数据分布不匹配或者正负样本采样比例有问题。另一个高频问题是类别不平衡。缺陷数据里往往某一类占了大头比如划痕占了70%其他类样本稀少。这时默认的交叉熵损失会让模型偏向多数类。我踩过的坑是某次训练完看总mAP还行但按类别拆开一看气泡类的recall只有0.12几乎完全没学到。后来给少数类加了更高的loss权重并在采样器里做了类别均衡采样这个类的recall才拉回到0.5以上。这个权重调整要控制幅度建议不超过5倍太大会导致多数类精度回退。4.3 显存、批大小与训练效率的实测数据不同数据集大小和显卡配置下训练效率差别很大。我把我实际跑过的一组数据放出来供参考。这里用的是单卡GPUbatch size为2、每张图约1333×800分辨率的情况数据集规模Backbone训练epoch数单epoch耗时总训练时长2000张ResNet5024约11分钟约4.5小时8000张ResNet5024约42分钟约17小时8000张ResNet10124约78分钟约31小时如果显存紧张优先缩减batch size而不是缩减图像尺度。图像尺度从1333×800降到1000×600小缺陷的召回会明显掉这个代价用batch size换不回来。另外mmdetection支持跨卡同步BNSyncBN如果缺陷图像之间光照差异大导致batch内统计不稳定可以考虑开启SyncBN在两张卡上分别跑batch size为2效果比单卡batch size为4要稳定。5. 检测到分割的过渡缺陷分割优化的两条落地路径标题里的分割优化方案是另一个核心模块。检测框告诉我们哪里有缺陷但很多质检场景需要知道缺陷的精确轮廓比如计算缺陷面积、判断缺陷是否超出工艺允许范围。这时候就需要从检测平滑过渡到分割。实际落地有两条路一条是上实例分割模型一条是在检测结果基础上叠加分割后处理。我分别讲一下适用场景和实施细节。5.1 路径一快速验证用检测框内分割如果你已经有了训练好的Faster R-CNN模型最快的分割方案是对检测框内的区域做传统图像处理。具体做法是模型输出缺陷框后截取框内图像用自适应阈值分割比如大津法即Otsus method把缺陷像素从背景里分离出来再配合形态学开闭运算去掉噪点和填补空洞。这条路径的优点是零额外训练成本、推理速度快缺点是鲁棒性有限。对于光照均匀、缺陷和背景灰度差异明显的场景比如金属表面的深色凹坑效果很好但碰到纹理复杂、缺陷边缘模糊的样本阈值分割要么把背景纹理割进来要么把缺陷尾部切掉。我的经验是这条路径适合做方案预研和demo演示快速让业务方看到检测轮廓的效果实际量产前再评估是否需要上实例分割模型。5.2 路径二检测头上面加Mask分支复用backbone特征图更系统的做法是直接切换到Mask R-CNN。Mask R-CNN在Faster R-CNN的检测分支上并行增加了一个Mask分支输出每个候选框内的像素级掩码。由于它完全复用backbone和FPN的特征训练成本比从零训一个分割模型低得多而且检测和分割共享特征分割任务反过来还能正则化检测分支两者互相促进。从Faster R-CNN迁移到Mask R-CNN配置文件改动很小模型类型改为Mask R-CNN在roi_head里增加mask分支的配置数据标注格式需要额外提供多边形标注或是rle格式的掩码。训练时Mask分支的loss权重默认是1.0我通常保持这个值不调。如果不想大改数据标注也可以用一条中间路线保持现有检测框标注不动额外对每一类缺陷准备一小批像素级标注比如100到200张用这部分数据单独微调一个轻量分割头。实测下来在检测框的引导下这个小分割头也能达到可用的轮廓精度而且数据成本低很多。5.3 分割结果如何反馈到检测流程做分割优化时很多人忽略了一个联动优化点分割结果可以反哺检测。具体做法是对分割出的掩码计算面积、长宽比、轮廓复杂度这几个几何特征再用规则引擎做一次二次过滤。例如某些类别缺陷工艺上允许存在但面积超过阈值就必须判废某些细长划痕如果分割轮廓曲率突变大概率是检测框把两块独立缺陷并在一起了这时候可以把检测框拆开重新推理。这个联动机制在项目里带来的改善很直观整线误检率下降了约30%因为规则过滤正确拦截了大量检出但工艺上可接受的伪缺陷。检测模型不需要为这些case额外收集负样本这是分割模块带来的额外红利。6. 踩坑实录从漏检到误检的完整排查链路项目做多了踩坑是常态。我把几次印象最深的排错经历整理出来按现象—排查过程—根因—修复的顺序讲能帮后来人省不少时间。6.1 小缺陷漏检成片出现问题出在RoIAlign的采样密度某次在带钢表面质检项目里模型对宽度只有3到5个像素的线性划痕漏检率极高。一开始我以为是数据不够补了500张划痕样本后漏检率只降了1个百分点。后来我仔细检查了RoIAlign的输出尺寸发现默认的roi_out_size设置为7×7。对于宽度仅几个像素的划痕在7×7的采样网格里特征被严重稀释RPN虽然能提出候选框但到了第二阶段分类器已经很难分辨这是划痕还是背景纹理。修复办法很简单把roi_out_size从7×7调成14×14RPN的nms阈值从0.7降到0.6让更多疑似区域进入第二阶段。改动之后线性划痕的召回率从0.41直接升到0.67。这个坑告诉我一个道理小目标检测问题里特征图本身的尺度是一方面RoI池化的采样密度是另一个容易被忽略但影响巨大的环节。6.2 误检集中在一个区域光照不均匀的锅另一个项目里模型在图像的特定角落频繁误报缺陷。肉眼看着那些区域确实有一团阴影但业务人员说那是正常的反光。排查时我先看了训练集统计发现这个角落里的正常样本很少——产线相机在这个角度正好有光源反射导致该区域图像特征跟缺陷区域很像。这类环境性伪缺陷在工业场景特别常见。我的解决方案分两步第一步在训练集里单独增加该区域的正常样本让模型见到足够多的假缺陷第二步从数据层面做一个局部亮度归一化的预处理把光照差异压平。处理完后再评估该区域的误检率下降了六成。如果这一步还压不住我会考虑在部署端叠加一个ROI掩码把产线上本来就无需检测的区域直接屏蔽掉。6.3 训练loss正常但验证集指标来回震荡检查随机种子和验证集划分还有一次模型训练曲线完全正常但每次跑验证集指标都不一样波动幅度超过3个点。排查下来发现两个问题叠加一是验证集样本量太小只有150张单张图的变化就能引起指标波动二是训练时开了随机采样验证集本身没有固定下来。处理方案也很直接把验证集扩充到500张以上固定验证集的随机种子并把验证集的图像ID写死进配置文件。之后再跑实验指标稳定多了。这也给团队定了一条规矩任何方案对比实验必须使用完全相同的验证集、相同的推理配置、相同的随机种子否则指标差异没有可比性。7. 关于这套方案我最终想说的几件事整套方案跑下来我的体会是基于Faster R-CNN_R50_PAFPN_1x_COCO的缺陷检测方案不是最花哨的但它足够稳足够可控。模型结构上的每个选择——两阶段结构、PAFPN的特征融合、COCO预训练权重——都有明确的实际收益作为支撑不是拍脑袋决定的。缺陷检测领域真正决定成败的是对数据、对任务特点的理解模型结构反而是在一个成熟框架里做适配和微调。最后再分享一个小技巧做完检测和分割优化后一定要把推理结果可视化输出成批次报告按缺陷类别、面积分档、置信度区间分别拉统计直方图。业务方看到的不只是一堆mAP数字而是哪种缺陷在哪条产线上出现频率最高这类可行动的结论。我后来几乎所有项目都靠这份可视化报告跟业务方对齐需求比任何指标都管用。这套方案的价值最终也体现在这些能落地、能转化为质检决策的具体输出上。