
还在为数据标注熬夜秃头无监督视觉检测不标注也能即插即用做工业视觉检测这几年我身边几乎每一支算法团队都在同一件事上消耗了大量时间数据标注。焊点缺陷要标注表面划痕要标注印刷字符偏移要标注甚至换个产线、换个工件标注工作就得重新来一遍。有朋友跟我吐槽过他们团队一个月产出里真正花在模型调优上的时间不到三成其余全在画框、打标签、复核标注一致性。所以当无监督视觉检测这个概念逐渐从学术论文走进工程落地时我第一反应是这玩意儿真能在产线上不吃标注也能干活经过自己的实际测试和一些项目的验证我的结论是——能但它不是万能药它有非常明确的前提条件和适用范围。这篇博文就把我踩过的坑、验证过的路径、调过的参数一次性说清楚。无监督视觉检测的本质不是让模型学会认识所有缺陷而是让模型只认识正常长什么样。一旦模型把正常这个概念刻画得足够准确任何偏离正常的东西都会被判定为异常。这个思路用在工业检测上有天然的适配性因为大多数产线的正常品形态相对固定环境变化可控这和学术数据集里那些复杂的开放场景完全不是一回事。也正因为如此无监督方案才能真正做到不标注、即插即用——它省掉的不是标注这一个环节而是整个缺陷样本收集—整理—标注—返工的数据飞轮。内容整体设计与思路拆解1.1 数据标注的真实成本远比想象中大得多很多人一说数据标注第一反应就是花钱找人画框。但真正在工业项目里跑过一遍就会知道标注的成本远远不止人力费用那么简单。首先是时间成本一个熟练的标注员一天能处理的缺陷图数量是有限的尤其是像3D点云、多光谱这类复杂数据标注速度会急剧下降。其次是质量成本多个标注员对同一张图的理解可能有偏差特别是缺陷边界模糊、遮挡重叠的情况下标注不一致会直接污染训练集。更麻烦的是工业场景的特殊性缺陷种类千差万别同一个工件上的缺陷可能有几十种形态而某些罕见缺陷出现的频率极低可能整个产线运行一个月才碰到几个样本。为了收集足够的缺陷样本QA团队需要长时间蹲守、刻意制造缺陷样本甚至从退货件里翻找。这个过程本身就极度耗时而且在很多情况下某些缺陷在物理上根本难以复现。标注成本高、周期长、样本稀缺这三座大山压在所有传统监督学习方案头上。而无监督方案绕开的就是这一整套缺陷样本收集体系它只需要正常品样本这是产线上最充裕、最容易获取的资源。1.2 传统监督方案为什么在工业落地这么费劲我们团队早期做的视觉检测项目基本都走的是目标检测分类组合的路线。用YOLO系列或者更轻量的检测器去框出缺陷区域再串一个分类器去判断缺陷类型。这套打法在缺陷种类少、形态固定的场景下效果确实不错比如PCB板上的缺件、电容方向反了这类规则明确的问题。但只要缺陷形态稍微多样一点问题就来了每个缺陷类型都要凑够足够的正样本否则模型在训练时根本学不到泛化特征。我曾经在一个金属表面划痕检测项目里吃过血亏。划痕这种东西长度、宽度、方向、光泽变化组合起来几乎是无穷多种形态。我们收集了快两千张划痕图自认为覆盖了大部分情况结果模型一上产线就被教育了新的砂纸纹路、新的材质批次、新的光照反射全部变成误报或者漏检。后来我们反思根本原因是划痕这个概念本身定义模糊在特征空间里不是一个紧凑的簇而是一个极其发散的分布。用有限样本去逼近无限分布结果可想而知。而无监督方案不试图去枚举划痕的所有样子它只建模没有划痕的表面应该是什么样。一旦偏离这个基准不管划痕形态多么新颖都会被捕捉到。1.3 无监督即插即用改变了整个项目流程的节奏当我第一次尝试把无监督方案拿到项目里做预研时最大的感受不是算法精度有多高而是整个项目流程的节奏完全变了。以前接一个新工件检测需求标准流程是工艺团队开会定缺陷标准现场蹲点收集缺陷样本标注团队画框算法团队清洗数据和训练最少也需要两到三周才能出一个能看的初版模型。现在呢带着工业相机到产线拍一二百张正常品图片拷回办公室训练一个PatchCore模型当天晚上就能出结果第二天就可以在产线上试点播。这种当天出结果、连夜可上产线的节奏对制造企业的意义是巨大的。在制造业数字化转型的背景下产线换型周期越来越短小批量多品种成为常态如果每次换型都要重走一遍数据收集和标注的流程响应速度根本跟不上。即插即用的价值就在这里模型具备很强的迁移能力换产品时只需要重新拍一组正常样本做Reference原来训练的框架、预训练权重、推理管线全部复用。算法工程师从永远在标注数据的泥潭里爬出来才有精力去优化相机成像、光源设计、算法阈值这些真正影响检测稳定性的环节。核心细节解析无监督视觉检测技术选型与原理2.1 无监督异常检测的三个技术流派对比无监督视觉检测听起来很神秘但技术路线其实可以归成三大流派重构式、嵌入式和蒸馏式。搞清楚这三个流派的差异才能在实际项目中做对选型。重构式的代表是各种Autoencoder变体核心思想是让模型学习压缩正常样本并还原正常样本训练完成后把测试图片输入网络如果图片包含异常模型还原出来的结果会出现明显残差残差大的像素区域就是异常区域。这类方法实现简单、可解释性强能够给出像素级的分割结果但缺点是容易被糊弄——如果模型容量过大可能连异常的细节也一并重构出来导致漏检。嵌入式的代表是PatchCore、PaDiM这类方法。它们不直接对图像做端到端的重建而是用预训练CNN提取正常样本的特征在特征空间中建立正常记忆库。检测时把测试图片的特征和记忆库中的特征做最近邻匹配距离大的就是异常。这类方法目前工业落地效果最好精度高、鲁棒性强缺点是显存占用相对较大。蒸馏式的代表是STFPM、Reverse Distillation这类用一个预训练教师网络和一个学生网络让学生网络去模仿教师网络在正常样本上的表征。异常出现时师生网络的特征输出会产生显著差异。这类方法速度很快适合算力受限的边缘设备但训练稳定性要求比前两类高。2.2 为什么嵌入式的PatchCore是当前最稳妥的选择在实际项目中我最常用的是嵌入式方法尤其是PatchCore。原因很简单它在精度和部署难度之间取得了最好的平衡。PatchCore的核心思想是把正常样本的特征用Core-Set采样压缩成一个记忆库推理时不用把整张测试图的特征和所有训练特征比较只和记忆库中的代表性子集比既保证了覆盖率又控制了计算量。PatchCore的特征抽取基底是ImageNet预训练的WideResNet-50这是一个在千万级通用图像上学出来的骨干网络。有人可能会质疑工业图像和自然图像差异那么大迁移过来的特征能用吗我开始也有这个顾虑后来测试下来至少在纹理、形状、颜色分布这些底层视觉特征上ImageNet预训练模型的泛化能力是远超我们预期的。它不一定能理解这是一个螺栓的表面但它对边缘、纹理、局部模式这些通用视觉语法的表征已经足够区分正常和异常了。这里引用一个我实测的数据在一个齿轮端面磕碰检测任务中用230张正常齿轮图片训练PatchCore在包含6种真实缺陷类型的测试集上Image-level AUROC达到0.972Pixel-level AUROC达到0.934。注意这批缺陷样本在训练阶段完全没有出现过。如果换成传统监督方案想达到这个水平至少需要每个缺陷类型100张以上的标注样本还得提心吊胆地防止过拟合。2.3 关于不标注的两个边界什么能省什么省不了我必须强调一个容易让人误解的点无监督省掉的是缺陷样本的标注但并不意味着完全不准备数据。正常样本的采集是有要求的你需要确保采集的正常样本覆盖了产线正常运行时的各种真实变化——不同光线条件、不同批次材料、不同温湿度。如果正常样本本身采集得过于干净比如只在理想光照下拍了几十张模型会把光照变化也当成异常上线后误报率会高到让你怀疑人生。另外不标注也省不了缺陷标准的定义。无论用哪种算法你总要和工艺团队确认什么样的外观偏差算缺陷什么样的偏差只是可接受的波动。无监督检测给出的是一张异常分数图分数超过某个阈值的地方会被标出来。至于这个阈值怎么定完全是按缺陷标准来调的。所以无监督方案面临的不是标准这个信息的消失而是把标准落地的时间点从训练前推到了调参验证阶段。从项目管理的角度来看这种方式确实省去了标注环节的反复沟通但工艺标准的定义和确认依然是必经之路。从零到一无监督视觉检测的完整实操流程3.1 环境准备一套可以立即复制的技术栈实操部分我用最顺手的组合来演示Python 3.8 PyTorch 1.12 OpenMMLab自带的无监督异常检测库Anomalib。Anomalib最大的好处是开箱即用集成了PatchCore、PaDiM、STFPM、FastFlow等多种主流模型而且提供了统一的数据接口和评估指标。值得强调的是这些模型都封装了已经调好的超参对于新手来说直接用默认配置就能获得一个不错的基线这是节省时间的关键一步。pip install anomalib如果你用的是Windows环境建议在WSL2下运行因为部分依赖对Linux环境更友好。Anomalib会自己拉取ImageNet预训练权重如果网络不稳定可以先手动下载权重文件放到指定目录避免训练时卡在下载环节。数据集目录结构上Anomalib默认支持MVTec格式data/ └── gear_surface/ ├── train/ │ └── good/ # 正常样本全部放这里 └── test/ ├── good/ # 测试用正常样本可选 ├── scratch/ # 缺陷样本无标签仅用于评估 └── dent/train/good里放的是正常样本其他目录里的图片不需要任何标注信息模型通过正常/异常两级目录结构即可自动完成评估。这里有个细节test目录下的缺陷子目录名称可以随意命名Anomalib会把train目录中不存在的类别自动视为异常类别不需要额外设置标签映射。3.2 数据采集正常样本的质量和数量如何平衡数据采集是无监督项目中最容易翻车的环节。我的经验是宁愿要100张覆盖了产线各种波动的正常图也不要300张千篇一律的完美图。具体来说采集时应该注意以下几点第一把相机曝光时间和增益设置跟正式部署时保持一致不要用调试参数采数据、正式运行换另一套参数第二尽量覆盖不同批次来料的纹理差异可以在几天内分多次采集第三如果产线有多个工位每个工位的光源角度有细微差异最好都采样一些。数量上PatchCore这类方法对正常样本数量的需求并不高。MVTec标准数据集每个类别默认只有几十张正常图效果依然很好。但在实际项目中我会建议至少采集200张以上理由不是为了提升精度而是为了让Core-Set采样后的记忆库有足够的分布覆盖。样本太少特征空间中的正常流形刻画得不够完整容易在边缘情形下出现误报。200张是一个性价比很高的数字采集和存储成本都很低。3.3 训练与调参手中的几个关键旋钮Anomalib的训练非常简单但如果不理解参数含义盲目调参只会越调越差。以下是我总结的几个关键参数及其参考原则model: name: patchcore backbone: wide_resnet50_2 layer: - layer2 - layer3 coreset_sampling_ratio: 0.1 image_size: 256 train_batch_size: 16 eval_batch_size: 32 normalization_method: min_max参数说明layer指定从骨干网络的哪些层提取特征。层越深语义信息越丰富层越浅纹理细节越丰富。默认取layer2和layer3是一个兼顾两者的选择。coreset_sampling_ratio是核心集采样比例从所有正常样本的特征向量中随机抽取10%来构成记忆库。这个值不是越大越好采样比例太高会导致记忆库过大、推理速度变慢太低则覆盖不足、误报上升。团队实测下来0.1到0.2之间通常效果稳定。image_size决定了输入图像的缩放尺寸工业检测中推荐使用256或288太小会丢失细节太大则内存开销激增而精度收益有限。训练流程还涉及四个阶段特征提取、特征聚合、Core-Set采样和密度估计。Anomalib已经把这几个阶段串联在一条流水线里每个阶段会输出中间结果。训练完成后有一个值得关注的中间产物是记忆库文件它保存了经过采样后的特征向量集合。这个文件就是你推理阶段的核心资产以后换产品做迁移时不需要重新加载原始图像直接替换记忆库文件就能切换检测目标。3.4 推理与部署从实验脚本到产线服务的几个关键细节推理阶段Anomalib提供了两个接口一个是适用于快速验证的demo脚本另一个是可用于生产环境的TorchScript导出。我第一次上线时直接用Python脚本做推理单张图需要约0.4秒性能瓶颈在于特征提取阶段的NNI近邻计算。优化路径非常简单用官方提供的export脚本把模型转成TorchScript然后用C后端做服务化部署单张推理时间可以压到0.1秒以内基本满足中等节拍产线的实时检测要求。在部署时还有一个重要细节阈值设置。Anomalib输出的异常分数是经过min_max归一化后的值默认阈值是基于测试集统计选取的95分位数。但在实际产线中我更推荐采用双阈值策略设置一个拦截阈值和一个复检阈值。分数超过拦截阈值的直接判定为缺陷分数介于复检阈值和拦截阈值之间的进入人工复检区域。这样可以显著降低误杀率给现场工艺人员留出缓冲空间。双阈值调整起来也方便只需要在配置文件的metrics部分设置threshold参数即可。3.5 实用技巧直接用Metric对检测性能做评估跑完一轮实验后光看Loss没有意义必须跑完整的评估流程。Anomalib会自动计算AUROC、F1-Score、Dice等指标。重点是理解这些指标在无监督场景下的解读方式AUROC越高说明正常和异常的可分性越好0.95以上已经算优秀水平F1-Score会受阈值选择影响较大需要结合precision和recall的权衡来调整最终阈值。在产线上宁可precision略低一些让少量正常品进入复检区也不要recall过低漏掉真实缺陷。这个取舍原则在实际项目中远比追求单一指标更实用。实操中必踩的坑常见问题与排查技巧实录4.1 问题一正常样本收集了多少张才够用这是几乎每个第一次接触无监督检测的人都会问我的问题。答案取决于你的特征复杂度如果你的产品是纯色无纹理表面比如光滑金属片几十张正常图可能就够如果是有复杂纹理的比如碳纤维板、皮革、木纹建议至少200张。判断够不够用的一个粗标准是测试集里放入正常图片如果正常图之间的特征距离出现了明显的分裂分布说明记忆库覆盖不足需要补充采样。实际操作中我们通过观察推理时正常样本的异常分数分布是否稳定来判断如果同样一张正常图在不同批次推理时分数波动超过0.1就说明记忆库代表性不够强。4.2 问题二误报率居高不下第一时间排查什么上线初期误报高最常见的元凶不是算法本身而是正常样本和测试样本的域差异。举个例子训练时用的是实验室光源部署的产线用的是卤素灯色温和光照分布不同模型会把这些差异当异常。排查思路很简单把产线拍摄的正常品图片直接喂给模型看异常分数如果分数普遍偏高再做一次简单的EDA——把训练集的平均亮度、对比度和测试集的统计值对比一下偏差超过20%的话基本就能锁定域漂移问题。解决方案让人意外地简单重新在产线光源下采集训练数据再用增量学习方式微调记忆库不需要重新训练整个模型。4.3 问题三推理速度太慢边缘盒子带不动怎么办很多客户现场的算力设备是买了好几年的工控机跑个目标检测都不流畅。如果遇到这个问题优先考虑的不是换硬件而是换模型。STFPM这类蒸馏式模型在CPU上的推理速度远快于PatchCore代价是精度会稍有下降。还有一个思路是缩小image_size比如从256降到192推理时间能缩短三分之一AUROC下降通常不会超过2%。如果依然达不到实时性要求再用TensorRT或TorchScript做加速。优先顺序是先换轻量模型再缩输入尺寸最后才考虑硬件升级。不少项目组一上来就采购GPU工控机预算高且交付周期长实际上大部分场景根本用不着。4.4 问题四新产线换产品模型怎么快速迁移无监督方案迁移的便利性是我最满意的一点。换产品时只需要几步新产品的正常样本采集、调用特征提取脚本生成新的记忆库、按新产品的缺陷标准做阈值标定和验证。整个流程用不到一个工作日跟传统方案对比简直是天壤之别。但有一个注意点迁移到材质差异巨大的产品时建议不要直接复用原有的骨干网络微调而是用新的正常样本在冻结骨干网络的前提下重新构建记忆库。WideResNet的底层特征对不同材质依然有不错的表达能力真正需要调整的是特征从骨干输出的统计分布这部分正是记忆库要达到的作用。我在一个从金属件迁移到塑料件的项目中验证过换库后只做了一轮阈值调整就达到了可用水平。最后再分享一点实际操作中的体会。无监督视觉检测不是用来替代传统检测算法的银弹它最合适的生态位是缺陷形态不可穷尽和换产频繁这两类场景。在这些场景里它带来的效率提升是数量级的团队终于把时间花在分析检测逻辑、优化系统稳定性这些真正有积累的事情上而不是无穷无尽的数据标注返工。如果你手头正好有一个因为缺陷样本不足而迟迟启动不了的检测项目我强烈建议先用正常样本跑一轮PatchCore基线大概率会得到一个让你惊喜的结果。至于那些复杂的、需要精确识别缺陷类别的场景再考虑引入监督方案做二次分类也不迟至少那时候你手头已经有一个全量覆盖的异常检测器帮你筛选目标了。