多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

工业缺陷检测小样本YOLO与异常检测融合及漏检控制实战

工业缺陷检测小样本YOLO与异常检测融合及漏检控制实战 1. 工业缺陷检测的底层逻辑与方案选型1.1 为什么工业缺陷检测和常规目标检测完全不是一回事做过COCO数据集上YOLO训练的人第一次进工厂产线做缺陷检测大概率会被现实打脸。常规目标检测面对的是人、车、猫、狗这类语义明确、特征丰富、样本量巨大的目标而工业缺陷检测面对的是划痕、脏污、气泡、缺角、色差这类形态模糊、边界不清、样本极少的目标。一张手机玻璃盖板的划痕可能只有几个像素宽长度方向却有几百个像素标注框拉出来是一个极端长宽比的矩形YOLO默认的anchor根本匹配不上。更麻烦的是工业场景里“正常样本”是海量的“缺陷样本”是稀缺的。一条产线跑一天可能出几万件良品缺陷件只有几十件甚至几件。你拿几十张缺陷图去训YOLO模型要么直接过拟合到那几十张图的背景纹理上要么把所有稍微不一样的正常品都判成缺陷漏检和过杀两头堵。所以工业缺陷检测的核心矛盾不是“模型够不够强”而是“样本够不够用”和“漏检能不能压住”。这两个问题贯穿整个项目周期从数据采集、标注策略、模型选型、训练技巧到后处理阈值每一步都在跟这两个矛盾做斗争。1.2 小样本条件下YOLO和异常检测怎么选先给一个我实际项目里反复验证过的判断标准场景条件推荐方案理由缺陷样本≥200张/类缺陷形态相对固定YOLO系列直接训练有监督信号足够YOLO的定位和分类能力能充分发挥缺陷样本50~200张/类形态有变化YOLO强数据增强预训练权重需要靠增强和迁移学习撑住泛化缺陷样本50张/类缺陷形态多变异常检测PatchCore/PaDiMYOLO兜底异常检测只学正常样本分布不依赖缺陷样本数量缺陷样本10张/类且无法采集更多异常检测为主YOLO仅做已知缺陷的补充有监督方案基本不可行只能靠无监督异常检测这个表不是拍脑袋来的。我做过一个连接器端子缺陷检测的项目缺陷类型有歪针、缺针、缩针三种每种能收集到的缺陷样本不到30张。一开始硬上YOLOv8mAP在验证集上看着还行一到产线就崩漏检率超过15%。后来改成PatchCore做异常检测只用了300张正常样本训练漏检率直接降到3%以下。但PatchCore的问题是过杀率高正常品里稍微有点反光差异就报警所以又加了一个轻量YOLO做二次确认专门识别已知的三种缺陷形态把过杀压下去。注意异常检测不是万能的。它只能告诉你“这张图和正常样本不一样”但没法告诉你“这是什么缺陷”。如果产线要求分类缺陷类型异常检测只能做初筛最终分类还得靠有监督模型。1.3 漏检控制的本质是什么漏检控制的本质是在召回率和精确率之间找一个产线能接受的平衡点。很多人一上来就调置信度阈值把阈值从0.5降到0.1召回率确实上去了但过杀率也爆炸了。产线工人一天要复判几百张误报图很快就没人信这个系统了。正确的做法是分层控制第一层数据层面。确保训练集里包含了产线上可能出现的各种缺陷形态包括边缘案例。比如划痕要包含横向、纵向、斜向、弧形脏污要包含点状、片状、条状。数据层面漏了模型层面怎么调都补不回来。第二层模型层面。用更大的输入分辨率、更密集的anchor、更强的特征融合来提升小缺陷的召回。YOLOv8的P2层检测头对微小缺陷很关键但很多人为了速度把P2砍了漏检就上来了。第三层后处理层面。用NMS的IoU阈值、置信度阈值、以及自定义的形态学过滤来精细控制。比如划痕检测可以加一个长宽比过滤把明显不是划痕的检测框去掉这样就能在保持召回的同时压低过杀。这三层是乘法关系任何一层没做好最终漏检率都下不来。2. 小样本训练的核心细节与实操要点2.1 数据采集与标注的坑小样本场景下数据质量比数量重要十倍。我见过太多项目标注员把划痕的框拉得比实际宽一倍模型学出来的边界全是糊的。工业缺陷标注有几个铁律框要贴紧缺陷边缘但不要切掉缺陷本身。对于划痕这种细长缺陷框的宽度可以比划痕宽2~3个像素但长度方向必须完整覆盖。同类缺陷的标注标准要统一。比如“脏污”和“色差”的界限必须在标注规范里写清楚否则模型学出来的分类边界是乱的。难例要单独标记。产线上那些模棱两可的样本比如“疑似划痕但可能是反光”要单独放一个文件夹训练时可以作为难例挖掘的候选。采集环节有个容易被忽略的点光照一致性。工业相机的光源如果老化或者角度偏了正常品的成像都会变异常检测模型直接误报。所以采集数据时一定要记录光源状态最好每批次采集都拍一张标准色卡做参考。2.2 数据增强在小样本下的正确打开方式小样本训练离不开数据增强但工业缺陷检测的增强和自然图像完全不一样。翻转、旋转、裁剪这些常规操作在工业场景里可能把缺陷的物理意义破坏掉。比如PCB板上的缺陷你旋转90度走线方向就变了模型学到的特征和实际产线对不上。我常用的增强策略是分层的几何增强谨慎使用水平/垂直翻转仅适用于缺陷形态无方向性的场景比如点状脏污小角度旋转±10度以内适用于划痕、缺角等但角度大了会引入不真实的形态随机缩放0.8~1.2倍模拟不同距离下的成像变化这个比较安全像素级增强重点使用亮度/对比度扰动模拟光源波动幅度控制在±15%以内高斯噪声模拟传感器噪声标准差控制在5以内高斯模糊模拟对焦偏差核大小3~5局部对比度增强用CLAHE模拟不同光照条件下的纹理变化Mosaic和MixUp小样本下要调参 YOLOv8默认的Mosaic增强在COCO上很有效但在工业缺陷检测里4张图拼在一起会引入大量无关背景小样本下反而干扰学习。我的经验是把Mosaic的概率从1.0降到0.3~0.5MixUp的概率降到0.1~0.2。如果缺陷本身很小Mosaic拼图后缺陷更小直接关掉Mosaic改用Copy-Paste增强把缺陷区域抠出来随机贴到正常样本上。这个对小样本特别有效能把缺陷样本的有效数量翻好几倍。实操心得Copy-Paste增强时粘贴的位置要避开正常样本本身的纹理边缘否则会引入不自然的边界。我一般用泊松融合或者简单的alpha混合来过渡边缘效果比硬贴好很多。2.3 迁移学习与冻结策略小样本训练必须用预训练权重这个没有争议。但怎么用预训练权重很多人没搞对。YOLOv8在COCO上预训练的权重backbone学的是自然图像的底层特征边缘、纹理、颜色这些对工业缺陷检测是有用的。但neck和head学的是COCO的类别语义对缺陷检测基本没用。我的做法是第一阶段冻结backbone只训neck和head。学习率设大一点0.01训50~100个epoch。这一步是让模型快速适应缺陷检测的任务形式。第二阶段解冻backbone的后几层整体微调。学习率降到0.001训100~200个epoch。这一步是让底层特征也适应工业图像的纹理分布。第三阶段如果样本极少50张只解冻backbone的最后两个stage。再往前的层冻结防止过拟合。冻结策略的核心逻辑是越底层的特征越通用越顶层的特征越任务相关。小样本下任务相关的层需要重新学但通用层最好保持稳定。2.4 损失函数的调整YOLOv8默认的损失是CIoU Loss DFL BCE分类损失。在工业缺陷检测里这个组合有几个问题CIoU对小缺陷不敏感。小缺陷的IoU波动很大CIoU的梯度不稳定。可以换成NWDNormalized Wasserstein Distance或者EIoU对小目标的定位更友好。DFL对边界模糊的缺陷不友好。工业缺陷的边界往往是渐变的DFL学出来的分布很分散。可以降低DFL的权重或者改用更简单的L1损失。分类损失在不平衡样本下会偏向多数类。正常样本远多于缺陷样本BCE会倾向于把所有东西判成正常。可以用Focal Loss或者带类别权重的BCE给缺陷类更高的权重。我实际项目里常用的配置是CIoU换成NWDDFL权重从1.0降到0.5分类损失用Focal Lossgamma2.0alpha0.25。这个组合在小样本缺陷检测里比默认配置的mAP能高3~5个点。3. 漏检控制的完整实操流程3.1 从数据到模型的全链路漏检排查漏检不是某一个环节的问题而是全链路累积的结果。我一般按下面的顺序排查第一步看训练集里有没有漏标。用训练好的模型在训练集上跑一遍把模型检测到但标注里没有的框找出来人工复核。如果确实是漏标补上重新训。这一步能解决20%左右的漏检。第二步看验证集的漏检分布。把漏检的图按缺陷类型、尺寸、位置分类。如果漏检集中在某一种缺陷上说明这一类样本不够或者增强不够。如果漏检集中在小尺寸缺陷上说明输入分辨率不够或者P2层没开。第三步看模型的置信度分布。漏检的缺陷模型的置信度是多少如果置信度在0.1~0.3之间说明模型其实检测到了只是阈值卡掉了。这时候可以适当降阈值但要用后处理过滤来控制过杀。如果置信度低于0.05说明模型根本没学到这个缺陷得回去补数据。第四步看NMS有没有误杀。两个缺陷靠得很近时NMS可能会把其中一个框去掉。这时候要调NMS的IoU阈值或者改用Soft-NMS。3.2 输入分辨率与P2检测头的取舍工业缺陷往往很小输入分辨率直接决定漏检率。YOLOv8默认输入640对于小于16x16像素的缺陷特征图到P3层就只剩2x2了根本检测不到。解决办法有两个提高输入分辨率到1280或1536。代价是推理速度下降显存占用增加。T4上跑YOLOv8x 1280分辨率FP16大概能到15~20 FPS如果产线节拍要求25 FPS就得换更小的模型或者用TensorRT加速。开启P2检测头。YOLOv8的P2层特征图是160x160输入640时对微小缺陷的召回提升很明显。但P2层的计算量也不小推理速度大概降20~30%。我的经验是如果缺陷尺寸大于20x20像素640输入P3就够了如果缺陷在10~20像素之间上1280输入如果缺陷小于10像素必须开P2同时输入至少960。注意开P2后anchor的尺寸要重新聚类。用k-means在训练集上重新算一遍anchor否则默认anchor和你的缺陷尺寸不匹配召回反而会降。3.3 后处理阈值的精细调优后处理是漏检控制的最后一道防线。YOLO的输出经过NMS后剩下的框还要经过置信度阈值过滤。这个阈值怎么定直接决定漏检率和过杀率。我的做法是画一条P-R曲线然后根据产线的要求选点。如果产线要求漏检率1%那就选召回率99%对应的阈值接受过杀率可能到10~20%。如果产线要求过杀率5%那就选精确率95%对应的阈值接受漏检率可能到3~5%。但实际产线往往要求“漏检率1%且过杀率5%”这时候单靠一个全局阈值是不够的。需要做分层阈值按缺陷类型设不同阈值。比如划痕的阈值可以低一点0.15因为划痕漏检后果严重脏污的阈值可以高一点0.3因为脏污过杀影响小。按缺陷尺寸设不同阈值。小缺陷的阈值低一点大缺陷的阈值高一点。按图像区域设不同阈值。比如产品边缘区域的阈值低一点中心区域高一点。这个分层阈值表需要在实际产线上跑一段时间收集误报和漏报的案例逐步调整。我一般会做一个简单的网格搜索在验证集上找最优的分层阈值组合。3.4 TensorRT加速与多路视频流支持工业产线往往要求实时检测T4显卡是常见的配置。YOLOv8 640分辨率在T4上用TensorRT FP16加速大概能到100~150 FPS。如果产线有4路1080p25的视频流每路需要25 FPS总共100 FPST4刚好能撑住。但如果输入分辨率提到1280FPS直接降到30~40只能支持1~2路。多路视频流的支持不只是模型推理速度的问题还有视频解码、预处理、后处理的流水线设计。我常用的方案是解码用NVDEC硬件解码把RTSP流解成GPU上的纹理避免CPU解码成为瓶颈。预处理用CUDA kernel做resize和归一化不要用OpenCV的CPU版本。推理TensorRT引擎batch size根据路数调整。4路的话batch size设4一次推理处理4帧。后处理NMS在GPU上做用TensorRT的EfficientNMS插件比CPU NMS快10倍以上。这套流水线下来T4上4路1080p25的YOLOv8 640检测是稳的。如果路数更多要么降分辨率要么换更强的卡。4. 常见问题与排查技巧实录4.1 模型在验证集上表现好但产线上漏检严重这是最常见的问题原因通常是验证集和产线的数据分布不一致。验证集是从训练集里随机划分的和训练集同分布但产线上的光照、产品批次、相机状态都可能和训练时不一样。解决办法验证集要单独采集最好在不同时间段、不同光照条件下采集。在产线上跑一段时间把漏检的图收集起来加入训练集重新训。用在线学习或者增量学习让模型持续适应产线变化。4.2 小缺陷漏检大缺陷正常小缺陷漏检的原因通常是特征图分辨率不够。YOLO的P3层 stride是8输入640时特征图是80x80一个10x10的缺陷在P3上只有1.25x1.25信息损失严重。排查步骤确认输入分辨率是否足够。缺陷小于20像素时640输入基本没戏。确认P2层是否开启。P2的stride是4特征图160x160对小缺陷更友好。确认anchor尺寸是否匹配。用k-means重新聚类anchor让anchor的尺寸覆盖你的缺陷尺寸分布。确认数据增强有没有把小缺陷进一步缩小。随机缩放增强的缩小比例不要低于0.8。4.3 过杀率高正常品被误判过杀率高通常是模型对正常样本的多样性学习不够。正常样本虽然多但形态变化也大如果训练集里的正常样本不够多样模型会把没见过的正常形态判成缺陷。解决办法增加正常样本的多样性。不同批次、不同光照、不同角度的正常品都要采集。用异常检测做初筛YOLO做二次确认。异常检测只学正常分布对正常样本的多样性更敏感。后处理加形态学过滤。比如缺陷面积小于某个阈值就忽略缺陷长宽比不符合先验就忽略。4.4 常见问题速查表问题现象可能原因排查方法解决方案验证集mAP高产线漏检多数据分布不一致对比验证集和产线图的亮度、对比度分布重新采集验证集加入产线漏检图小缺陷漏检分辨率不够/P2未开看漏检缺陷的像素尺寸提高输入分辨率开启P2重聚类anchor过杀率高正常样本多样性不够看过杀图的正常品是否和训练集差异大增加正常样本加异常检测初筛训练loss不降学习率太大/标注有问题看loss曲线和标注可视化降学习率检查标注质量推理速度慢模型太大/后处理在CPU用profiler看各阶段耗时换小模型TensorRT加速GPU后处理多路视频流卡顿解码/预处理瓶颈看GPU利用率和CPU利用率NVDEC解码CUDA预处理batch推理4.5 几个容易被忽略的细节相机标定和畸变校正。工业相机镜头有畸变尤其是广角镜头。畸变不校正缺陷的位置和形状都会偏模型学到的特征和实际不符。我一般用棋盘格做一次标定把畸变系数存下来推理时先做畸变校正。光源稳定性。LED光源用久了会衰减色温和亮度都会变。异常检测模型对光源变化特别敏感光源衰减10%就可能引发大量误报。建议每季度用色卡校准一次光源或者用自适应阈值来补偿光源变化。产品定位。产线上的产品位置可能有偏移如果模型是在固定位置上训练的产品一偏就漏检。解决办法是在检测前加一个定位步骤用模板匹配或者关键点检测把产品对齐到标准位置再做缺陷检测。模型版本管理。产线模型不是训一次就完事的每次更新都要记录训练数据、超参数、评估结果。我见过太多项目模型更新后效果变差但没人知道上一版是怎么训的。建议用DVC或者MLflow做版本管理至少要把每次训练的配置文件和权重存档。5. 异常检测与YOLO的融合方案5.1 什么时候该用异常检测兜底异常检测的核心优势是只学正常样本不需要缺陷样本。这在缺陷样本极少或者缺陷形态完全未知的场景下是唯一可行的方案。但异常检测的缺点是无法分类缺陷类型而且过杀率通常比有监督模型高。我一般在这几种情况下用异常检测兜底新产品导入还没有收集到足够的缺陷样本。缺陷形态多变有监督模型覆盖不全。产线要求极低的漏检率愿意接受一定的过杀率。5.2 PatchCore的实操要点PatchCore是目前工业异常检测里最实用的算法之一核心思想是用正常样本的特征构建一个记忆库推理时计算测试样本特征到记忆库的距离距离大的判为异常。实操要点特征提取用预训练的WideResNet50取中间层的特征图不要用最后一层最后一层太语义化对纹理异常不敏感。记忆库的采样率。正常样本的特征太多全部存下来内存扛不住。用k-center greedy采样采样率设1%~10%根据正常样本数量调整。距离度量用余弦距离比欧氏距离更稳定。阈值设定。用正常样本的验证集算距离分布取99%分位数作为阈值。这个阈值需要根据产线的过杀率要求调整。PatchCore在MVTec AD数据集上的AUROC能到99%以上但实际产线上因为光照变化和产品差异AUROC通常降到95%左右。所以阈值要留足余量。5.3 YOLO异常检测的级联架构级联架构的逻辑是异常检测做初筛把所有可疑区域找出来YOLO做二次确认把已知缺陷类型识别出来最后用规则引擎做融合决策。具体流程异常检测模型对整图推理输出异常热力图。对异常热力图做阈值分割得到可疑区域。把可疑区域裁剪出来送YOLO做分类和定位。如果YOLO检测到已知缺陷且置信度阈值判为缺陷。如果YOLO没检测到但异常检测的异常分数很高判为未知缺陷报警人工复判。如果异常检测分数低YOLO也没检测到判为正常。这个架构的漏检率取决于异常检测的召回率过杀率取决于YOLO的精确率。实际项目里漏检率能压到0.5%以下过杀率控制在3~5%。实操心得级联架构的推理速度是瓶颈。异常检测和YOLO串行跑速度直接减半。我的优化方法是把异常检测和YOLO共享backbone用同一个特征提取网络异常检测用中间层特征YOLO用顶层特征。这样推理速度只降20%左右。6. 从训练到部署的完整工具链6.1 训练框架的选择YOLOv8用Ultralytics框架训练最方便但工业项目往往需要自定义损失、自定义数据增强、自定义评估指标。Ultralytics的代码结构比较封闭改起来麻烦。我一般用MMYOLO或者自己基于PyTorch重写训练脚本。MMYOLO的好处是模块化做得好损失函数、数据增强、backbone都可以灵活替换。但MMYOLO的文档和社区支持不如Ultralytics。如果团队里没有熟悉MMCV的人上手成本比较高。我的建议是快速原型用Ultralytics正式项目用MMYOLO或者自研。自研的话YOLOv8的官方代码结构清晰改起来也不难。6.2 模型导出与TensorRT加速YOLOv8导出ONNX再转TensorRT是标准流程。几个关键点导出ONNX时用dynamic axes支持动态batch和动态输入尺寸。TensorRT的FP16精度。工业缺陷检测对精度敏感FP16一般够用但如果漏检率要求极高可以用FP32或者INT8校准。EfficientNMS插件。TensorRT自带的NMS插件比CPU NMS快很多但需要把NMS的配置写进ONNX里。batch size的选择。根据路数和显存来定T4上YOLOv8s 640 FP16batch size8大概占4GB显存。导出脚本示例from ultralytics import YOLO model YOLO(yolov8s.pt) model.export( formatonnx, imgsz(640, 640), batch8, dynamicTrue, simplifyTrue, opset12, halfTrue )TensorRT转换用trtexectrtexec --onnxyolov8s.onnx \ --saveEngineyolov8s_fp16.engine \ --fp16 \ --minShapesimages:1x3x640x640 \ --optShapesimages:8x3x640x640 \ --maxShapesimages:16x3x640x6406.3 部署架构与监控产线部署不是把模型跑起来就完事了还需要考虑服务化。用Triton Inference Server或者FastAPI把模型封装成服务方便多路视频流调用。监控。记录每帧的推理耗时、检测结果、置信度分布。如果发现置信度分布偏移说明产线数据分布变了需要重新训练。报警。漏检和过杀都要记录定期人工复核。漏检的图要加入训练集过杀的图要分析原因。回滚机制。新模型上线前先在影子模式下跑一段时间和旧模型对比确认效果不降再切换。我实际项目里用PrometheusGrafana做监控每路视频流的FPS、延迟、检测数量都实时展示。一旦FPS掉到阈值以下或者检测数量异常波动自动发报警。7. 一些踩过的坑和最后的经验第一个坑是过度依赖公开数据集。MVTec AD和NEU-DET这些公开数据集上的SOTA模型直接搬到产线上大概率翻车。公开数据集的成像条件太理想了产线上的光照、震动、产品差异都是公开数据集没有的。我的建议是公开数据集只用来验证算法可行性正式项目必须用产线数据重新训练。第二个坑是忽略标注质量。小样本下标注错误的影响被放大一个错标可能让模型学偏。我现在的做法是标注完必须做交叉复核两个人独立标同一批图IoU低于0.7的框拿出来讨论。虽然费时间但比模型训废了再回头查便宜得多。第三个坑是阈值调优过拟合验证集。在验证集上把阈值调到最优产线上可能完全不是那么回事。我的做法是留一个独立的测试集阈值在验证集上调在测试集上确认。测试集和验证集要来自不同的时间段确保分布有差异。第四个坑是忽略推理速度的瓶颈。训练时只关注mAP部署时发现FPS不够。工业产线的节拍是硬约束25 FPS就是25 FPS模型再准跑不到这个速度也没用。所以训练阶段就要把推理速度作为约束条件选模型时先看FPS能不能达标再看mAP。最后分享一个实用技巧用少量缺陷样本做快速验证。新产品导入时先手工标注20~30张缺陷图用YOLOv8n训一个快速模型看看能不能检测到。如果能说明缺陷特征明显后续补数据扩模型就行。如果不能说明缺陷太细微或者形态太多变直接上异常检测方案别在有监督上浪费时间。这个快速验证流程能省掉很多无效的模型调优时间。
返回列表