资讯动态

工业质检中的视觉异常检测:无监督范式与PatchCore实战

发布时间:2026/9/24 20:37:18 来源:尧图企业网站定制
1. 视觉异常检测到底在工业质检里扮演什么角色我在工厂产线做视觉项目前后加起来有七八年最早那会儿大家嘴里的“视觉检测”基本等同于模板匹配加形态学后来慢慢过渡到目标检测再到现在越来越多项目开始提“异常检测”这个词。很多人第一次听到“视觉异常检测”会以为就是目标检测换了个说法其实两者在工业质检场景里的定位差别很大。目标检测解决的是“我知道要找什么你帮我在图里框出来”而异常检测解决的是“我根本不知道会出什么毛病你帮我判断这张图跟正常品有没有偏差”。这个区别直接决定了算法选型、数据准备、部署方式乃至整个项目能不能落地。工业质检里最头疼的从来不是那些已知的、反复出现的缺陷而是那些偶发的、新出现的、连工艺工程师都说不清楚成因的异常。比如注塑件表面突然出现的一小片流痕、PCB板某个焊盘边缘的轻微发暗、金属冲压件上一条几乎看不见的微裂纹。这些缺陷往往没有稳定样本你让产线攒三个月也攒不出五十张能拿来训练的图。传统监督式目标检测在这种场景下基本抓瞎因为它的前提是你得有大量标注好的正负样本。而视觉异常检测的核心思路是只拿正常样本训练让模型学会“正常长什么样”推理时但凡偏离这个分布的就判为异常。这个范式上的转变才是它在工业质检领域真正有前景的根本原因。适合关注这个方向的人其实挺广的。如果你是在工厂里做自动化集成的工程师正在被产线新缺陷搞得焦头烂额那这套东西值得你花时间研究。如果你是算法工程师做过YOLO系列目标检测但发现标注成本压不下来异常检测能帮你打开另一条路。哪怕你是刚入门深度学习的学生只要接触过CNN基础、跑通过简单的图像分类也能顺着无监督这条线摸进来。我下面会从整体设计思路、核心细节、实操落地到问题排查把我在实际项目里踩过的坑和总结出来的方法尽量讲透。2. 内容整体设计与思路拆解2.1 为什么工业质检需要无监督范式先算一笔账。一条中等规模的产线一天产出大概两万到五万件产品。缺陷率按千分之三算一天也就六十到一百五十件不良品。这里面还分好几种缺陷类型摊到每一种可能就十几二十件。你要训练一个监督式目标检测模型每种缺陷至少需要几百张标注样本才能让模型稳定收敛这意味着你得攒好几个月的产线数据。更麻烦的是等你攒够了数据、标完了、训完了产线工艺可能已经调整了缺陷形态又变了。这个循环在工业场景里几乎是无解的。无监督异常检测绕开了这个死结。它只需要正常样本而正常样本在产线上要多少有多少。你随便抽一天的生产数据里面百分之九十九以上都是良品直接拿来用就行。模型学的是正常品的特征分布推理时算异常分数。这个思路在学术上叫分布外检测或者一类分类在工业上就是“只教它什么是好的坏的自然就露馅了”。我做过一个连接器外观检测的项目客户换了三家供应商每家的来料表面纹理都有细微差异监督模型每次换料都要重新标数据重训。后来改用异常检测方案把三家供应商的良品混在一起训练模型自动学到了“良品的共同特征”换料后只要补几十张新供应商的良品图做微调就行标注工作量降了九成以上。2.2 异常检测与目标检测在产线上的分工逻辑不是说目标检测在工业质检里就没用了。恰恰相反在实际产线部署中这两者往往是配合使用的。我的典型方案是先用目标检测把产品主体从背景里抠出来做ROI对齐和姿态校正然后在对齐后的产品区域上跑异常检测。这样做的好处是异常检测模型不用去学背景变化、光照波动、产品位置偏移这些跟缺陷无关的东西它的注意力全部集中在产品表面本身。举个例子一个手机中框的外观检测工位相机拍到的图里中框可能偏左也可能偏右角度还有正负三度的波动。如果直接把整图丢给异常检测模型它会把位置偏移也当成异常误报率会高得没法看。所以前面必须有一个目标检测或者关键点检测模块把中框的四个角点找出来做透视变换把它摆正到标准姿态。这个预处理步骤的精度直接决定了后面异常检测的上限。我一般要求对齐后的产品边缘位置偏差控制在两个像素以内角度偏差控制在零点五度以内否则异常检测模型学到的正常分布会被姿态噪声污染。2.3 技术路线选型从重构到嵌入再到流模型目前工业界主流的异常检测方法大致分三条路线。第一条是基于重构的代表方法是自编码器和GAN思路是让模型学会把正常样本压缩再还原异常样本还原误差大。第二条是基于嵌入的代表方法是PaDiM和PatchCore用预训练CNN提取特征在特征空间里建正常样本的分布模型推理时算特征距离。第三条是基于归一化流的代表方法是FastFlow和CS-Flow直接对正常样本的特征分布建模算精确的似然。我在实际项目里用得最多的是PatchCore这条线。原因很实际它对预训练权重的依赖没那么强用ImageNet上训过的ResNet或者WideResNet就能work不需要在工业数据上重新预训练。而且它的推理速度可控通过coreset采样可以把特征库压到很小在工控机上跑单张图几十毫秒没问题。重构类方法的问题是容易把异常也重构出来尤其是那些跟正常区域纹理接近的微小缺陷自编码器经常直接忽略掉。流模型方法精度高但训练不稳定对超参敏感产线工程师维护起来门槛偏高。所以我的建议是如果你刚开始做工业异常检测从PatchCore入手把整个流程跑通再根据实际效果决定要不要换更复杂的方案。3. 核心细节解析与实操要点3.1 正常样本的采集与清洗策略异常检测模型的上限在数据而数据里最关键的是正常样本的质量。我见过太多项目失败在“以为正常的就是正常的”这个假设上。产线上你随手抽一批图里面一定混着肉眼难辨的轻微不良品比如极浅的划痕、微弱的色差、边缘的毛刺。这些样本如果混进训练集模型就会把它们当成正常的一部分推理时遇到类似缺陷就不报警了。我的做法是分两步清洗。第一步是人工粗筛让产线质检员用放大镜过一遍把有明显瑕疵的挑出来。第二步是用一个初步训练的模型做自检把异常分数排在前百分之五的样本调出来人工复核。这个迭代过程一般跑两到三轮能把正常样本集的纯度提到比较高的水平。另外正常样本要覆盖产线可能出现的各种正常波动比如不同批次原料的色差、不同班次的光照变化、产品在视野内不同位置的成像差异。我一般要求正常样本至少覆盖连续三天的生产数据每天抽五百到一千张总数控制在两千到五千张之间。太少模型学不充分太多训练时间线性增长但效果提升有限。3.2 图像预处理与ROI对齐的关键参数预处理环节我重点讲ROI对齐。前面说了要用目标检测或者关键点检测把产品摆正这里面的参数设置很讲究。以手机中框为例我用YOLOv8做中框检测置信度阈值设零点五NMS的IoU阈值设零点四五。检测到中框后取最小外接矩形然后根据中框的长宽比判断姿态是否正常如果长宽比偏离标准值超过百分之十就判为姿态异常直接报警不进入后续检测。透视变换的目标尺寸我一般设成标准产品尺寸的整数倍比如中框实际尺寸是一百五十毫米乘七十五毫米相机分辨率是每毫米十个像素那变换后的图就是一千五百乘七百五十。这个尺寸要保证产品的最小缺陷特征至少占三乘三个像素否则异常检测模型也看不出来。光照归一化我用的是CLAHE对比度限制设二点零网格大小设八乘八。这个参数在金属表面检测里比较稳能压住反光带来的局部过曝又不会把微小缺陷的对比度也压没了。3.3 特征提取网络的选择与微调尺度PatchCore默认用WideResNet50在ImageNet上的预训练权重取第二层和第三层的特征图做拼接。我在工业数据上对比过ResNet50、WideResNet50和EfficientNet-B5在金属表面缺陷数据集上WideResNet50的AUROC比ResNet50高大概两个点EfficientNet-B5跟WideResNet50差不多但推理慢了三成。所以如果没有特殊需求WideResNet50是性价比比较高的选择。关于要不要在工业数据上微调预训练网络我的经验是如果你的正常样本跟ImageNet的自然图像差异特别大比如纺织品的纹理、金属的拉丝表面那用工业数据做几个epoch的自监督预训练会有帮助。具体做法是用正常样本做SimCLR或者MoCo的自监督训练让网络先适应工业图像的底层纹理特征然后再接PatchCore。这个步骤能让AUROC提升三到五个点但会增加大概一天的训练时间。如果项目周期紧直接用ImageNet权重也能跑出可用的结果后面再迭代优化。3.4 异常分数计算与阈值设定的实操方法PatchCore的异常分数是每个patch特征到最近邻正常特征的欧氏距离取最大值作为图像级异常分数。这里有个细节直接取最大值对噪声很敏感一个孤立的异常点就能把整张图的分数拉高。我一般用top百分之一的patch分数的均值作为图像级分数这样既保留了局部异常的敏感性又抑制了单点噪声。阈值设定不能拍脑袋要用验证集来定。我通常留出两百张正常图和五十张已知缺陷图做验证画正常样本和缺陷样本的分数分布直方图找两个分布的交叠区域取交叠区间的中点作为初始阈值。然后根据产线对漏报和误报的容忍度做微调如果漏报代价高就把阈值往低调如果误报导致停线频繁就把阈值往高调。4. 实操过程与核心环节实现4.1 环境搭建与依赖版本锁定工业现场部署最怕环境问题。我现在的标准做法是用Docker把整个推理环境封起来基础镜像用nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04Python锁3.10PyTorch锁2.0.1torchvision锁0.15.2。这几个版本组合我在至少五个项目里用过稳定性没问题。PatchCore的实现我用的是anomalib这个库版本锁0.6.0它把PatchCore、PaDiM、FastFlow都封装好了省得自己从头写。pip install anomalib0.6.0 pip install opencv-python4.8.0.74 pip install scikit-learn1.3.0工控机如果是NVIDIA的卡记得装对应版本的显卡驱动我一般用525系列的驱动跟CUDA 11.8配合没问题。如果产线只有CPU工控机PatchCore也能跑但单张推理时间会从几十毫秒涨到几百毫秒要看产线节拍能不能接受。我做过一个低速产线节拍是两秒一件CPU推理完全够用。4.2 训练流程与coreset采样参数PatchCore的训练其实分两步第一步用预训练网络提取所有正常样本的特征第二步对特征做coreset采样选出一个有代表性的子集作为正常特征库。coreset的采样比例我一般设百分之十也就是从所有patch特征里选百分之十作为代表。这个比例在保证精度的同时能把特征库压到原来的十分之一推理时的最近邻搜索快很多。采样比例降到百分之五以下精度会明显下降升到百分之二十以上推理时间翻倍但精度提升不到一个点所以百分之十是个比较甜的平衡点。from anomalib.models import Patchcore from anomalib.data import Folder datamodule Folder( nameconnector, root./data/connector, normal_dirtrain/good, abnormal_dirtest/defect, taskclassification, image_size(256, 256), train_batch_size32, eval_batch_size32, ) model Patchcore( backbonewide_resnet50_2, layers[layer2, layer3], coreset_sampling_ratio0.1, num_neighbors9, )num_neighbors设九是我实测下来比较稳的值。设太小对噪声敏感设太大异常定位会变模糊。image_size我一般设成二百五十六乘二百五十六如果缺陷特别小可以提到五百一十二但推理时间会翻四倍左右。4.3 推理部署与产线集成要点推理部署这块我重点讲跟产线的对接。异常检测模型输出的是异常分数和异常热力图产线PLC需要的是一个OK或者NG的信号。中间需要一个阈值判断模块我一般用Python写一个轻量的推理服务用FastAPI暴露HTTP接口产线MES系统通过HTTP调用。接口收到图片后先做ROI对齐再跑异常检测分数超过阈值就返回NG并附带热力图否则返回OK。import cv2 import numpy as np from fastapi import FastAPI, UploadFile app FastAPI() app.post(/inspect) async def inspect(file: UploadFile): img cv2.imdecode(np.frombuffer(await file.read(), np.uint8), cv2.IMREAD_COLOR) aligned align_roi(img) score, heatmap model.predict(aligned) if score THRESHOLD: return {result: NG, score: float(score)} return {result: OK, score: float(score)}热力图我建议保留并推送到产线看板这样质检员能看到模型关注的是哪个区域方便复核和调试。我见过一个项目因为没加热力图模型误报时质检员完全不知道模型在看什么排查效率极低。4.4 模型迭代与产线反馈闭环模型上线不是终点。产线每天都会产生新的数据其中既有新的正常波动也有新的缺陷类型。我一般设一个每周一次的迭代周期把过去一周产线判为NG但人工复核为OK的样本挑出来这些是误报把它们加入正常样本集重新训练把人工复核为NG但模型判为OK的样本挑出来这些是漏报分析它们的特征如果是一种新的缺陷类型就单独建一个缺陷类用监督方法补一个专门的检测器。这个闭环跑起来之后模型的误报率一般能在两个月内降到千分之五以下。5. 常见问题与排查技巧实录5.1 误报率居高不下的排查思路误报是异常检测在产线落地最大的拦路虎。我遇到过的误报原因大概分四类。第一类是ROI对齐不准产品位置或角度有偏差异常检测模型把偏移当成了异常。排查方法是把误报样本的ROI对齐结果可视化出来看产品边缘是否跟标准姿态对齐。第二类是光照波动比如产线旁边的窗户有阳光直射进来或者车间的灯管老化导致色温变化。排查方法是看误报是否集中在特定时间段如果是就查光照。第三类是正常样本覆盖不足比如某个批次的产品表面纹理跟训练集里的不一样。排查方法是把误报样本的异常热力图调出来看模型关注的是哪个区域如果关注的是产品本身的纹理区域那大概率是正常样本没覆盖到。第四类是阈值设得太低这个最简单调高阈值就行但要小心漏报率上升。5.2 微小缺陷漏检的补偿方案异常检测对微小缺陷的敏感度取决于特征图的分辨率。如果缺陷在特征图上只占一个像素那模型很难把它跟噪声区分开。我的补偿方案有两个。第一个是提高输入分辨率从二百五十六提到五百一十二甚至一千零二十四但推理时间会线性增长。第二个是在异常检测之前加一个专门的小目标检测模块用YOLOv8的P2层特征做微小缺陷的初筛把可疑区域裁出来单独送进异常检测模型做精细判断。这个方案我在一个连接器 pin 脚检测项目里用过漏检率从百分之三降到了千分之二。5.3 产线换型时的快速适配方法产线换型是工业质检的常态。一个工位今天检A产品明天可能就换B产品了。异常检测模型换型时不需要重新标注缺陷样本只需要采集新产品的正常样本重新训练特征库就行。我的标准流程是换型前采集新产品正常样本五百张用已有的预训练网络提取特征重新做coreset采样生成新的特征库。整个过程在工控机上跑大概十五分钟。如果新产品跟老产品外观差异很大比如从金属件换成塑料件那可能需要重新做自监督预训练时间会拉长到几个小时。我一般建议产线换型前预留半天的模型适配时间。5.4 常见问题速查表问题现象可能原因排查方法解决措施误报集中在特定位置ROI对齐偏差可视化对齐结果调整对齐参数或重训关键点检测误报集中在特定时段光照波动对比不同时段图像加装遮光罩或固定光源微小缺陷漏检特征图分辨率不足检查缺陷在特征图上的尺寸提高输入分辨率或加小目标检测换型后精度骤降正常样本分布变化对比新旧产品特征分布重新采集正常样本训练推理速度不达标特征库过大检查coreset采样比例降低采样比例或换更轻的backbone异常热力图定位不准num_neighbors设置不当调整num_neighbors观察热力图一般设7到11之间5.5 实操心得与避坑建议最后分享几个我在项目里踩过的坑。第一个坑是不要用产线实时数据直接训练一定要用经过清洗的历史数据。产线实时数据里混着各种异常直接训进去模型就废了。第二个坑是阈值不要一次定死上线后前两周每天调一次根据实际误报漏报情况微调。第三个坑是异常检测模型对图像质量很敏感相机镜头要定期清洁光源要定期检查亮度衰减这些维护工作不做模型精度会慢慢漂移。第四个坑是不要指望一个模型解决所有缺陷类型异常检测擅长的是发现未知异常对于已知的、高频的缺陷类型还是应该用监督式目标检测专门处理两者配合才是产线的最优解。我个人在实际操作中的体会是视觉异常检测在工业质检领域的前景不在于它能不能完全替代目标检测而在于它填补了监督方法覆盖不到的那块空白。那些偶发的、新出现的、没有稳定样本的缺陷才是产线真正头疼的问题也是异常检测真正能发挥价值的地方。把ROI对齐做扎实把正常样本洗干净把阈值调到位这套方案在大多数工业场景里都能跑出可用的效果。后续如果产线有新的缺陷类型出现也不用慌把它当成一个新的异常类补几十张样本进去重新训练特征库就行迭代成本比监督方法低得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑