十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MemSeg半监督工业缺陷检测:仅用正常样本实现像素级异常分割

MemSeg半监督工业缺陷检测:仅用正常样本实现像素级异常分割 工业图像表面缺陷检测这个方向行业内卷了很久。传统机器视觉靠规则和特征工程换个产品、换个光照条件就得重新调参后来上了深度学习效果是好但工业现场最大的拦路虎不是模型而是标注数据。正样本好找缺陷样本千金难求——流水线上跑一天都未必能碰上一块真正有问题的料。这种情况下半监督方案就成了刚需。MemSeg 就是冲着这个痛点来的。这个方法的亮点在于只靠正常样本训练就能学会把异常区域精确地分割出来而且在 MVTec AD 这类主流工业缺陷数据集上跑出了当时的最优成绩。再加上它对内存和算力的要求比较友好不像很多大模型动辄一张 A100普通工控机加一张消费级显卡就能跑起来。这篇文章我想认真拆一下 MemSeg 的思路和落地要点结合我在实际部署中遇到的坑和调整经验来写希望能给正在做工业质检、仪表读数和表面缺陷检测的朋友一些参考。1. 缺陷检测到底难在哪为什么大家都转向半监督1.1 传统视觉方案和监督学习的现实困境先说传统机器视觉。很多工厂产线上跑的其实还是老一套打光、拍照、阈值分割、连通域分析、找特征。这套东西在场景极其固定的情况下能跑比如黑色橡胶圈上的白色杂质背光一打阈值一卡检测稳定率也很能打。但缺陷检测的难点在于“缺陷”这个东西的形态几乎是无限的——划痕有深浅、有方向污渍有通透、有纹理局部反光造成的假阳性更是防不胜防。一旦产品迭代、材料批次变了整套视觉方案往往要推倒重来。深度学习方案其实也有它的尴尬。以分割网络为例要训练一个像样的缺陷分割模型往往需要几千张甚至上万张逐像素标注的图像。工业缺陷样本出现频率低很多场景良率在 98% 以上想采集足够多的缺陷样本本身就不现实。我曾经帮一个做金属外壳的客户做项目对方提供了三个月产线攒下来的图片里面真正的缺陷图像只有一百来张而且集中在三种类型。这种数据量训一个二分类都费劲更不用说逐像素分割了。1.2 半监督的核心逻辑只用正常样本也能把异常找出来半监督缺陷检测的思路本质上换了一个角度不学“缺陷长什么样”而是学“正常长什么样”。训练阶段喂进去的全部是正常样本模型记住的是正常特征分布推理阶段一旦输入的图像任意区域偏离了正常分布模型就有足够的依据判断它是异常。这个思路放在工业场景里非常实用。正常样本在产线上简直取之不尽随便架个相机拍几百张、几千张都行完全不需要标注。这直接绕开了“缺陷样本少”的核心痛点。我接触过的几个落地项目里最短的只花了两天采集正常样本再花半天训练模型的实用性就已经超过了之前调了三周的传统视觉方案。MemSeg 属于半监督里更彻底的一种——它做的不是简单的重构误差或者特征距离计算而是通过结合记忆库和分割网络直接输出像素级异常图。这样一来你不仅能判断“有没有问题”还能看到“问题在哪个区域、大概是什么形状”这对后续的缺陷分类和工艺改进有着非常直接的参考价值。2. MemSeg 凭什么做到 SOTA核心设计拆解2.1 记忆库机制用正常特征构建一个“标准答案库”MemSeg 整个框架的核心之一是特征记忆库。你可以把它理解成一本“正常样本字典”——训练时模型用预训练的骨干网络提取所有正常图像的多尺度特征然后把这些特征存储起来。推理时输入图像的特征会到这个字典里做检索和匹配找到最接近的正常特征做对比差异大的区域就是异常区域。我在实际读论文和复现时的理解是记忆库的设计有一个很聪明的点它不是对整幅图做全局匹配而是考虑了位置匹配和相似度聚合。这避免了“这里看起来像正常”和“应该跟哪个正常区域对比”这两个问题之间的冲突。打个比方就像你拿着一块拼图去找参考图不是随便找一个最像的色块就完事而是既看颜色、也看位置、还看周边纹理是否一致综合判断之后才能下定论。记忆库的规模也需要根据实际数据量调整。原论文在 MVTec AD 上的默认配置是每个类别用一个记忆库大约存储数万到十几万个特征向量。实际部署时我建议先按图像的复杂程度来定纹理简单的产品类型记忆库可以缩小到原配置的 1/4速度会明显提升精度几乎不受影响。2.2 潜在异常样本生成让网络学会“识别异常”而不是“比较特征”如果只用记忆库做距离度量出来的结果往往是一张噪声很大的距离图很难直接当分割结果用。MemSeg 的做法是加了一个分割网络通过一种叫“潜在异常样本生成”的策略来训练它。具体机制是在训练阶段把正常样本切块随机遮盖一部分区域或者从别的正常样本里截取一块纹理不一致的图块拼上去模拟出“伪缺陷”样本。这些伪缺陷样本虽然粗糙但能教会分割网络一个关键能力把特征空间里的差异转化为空间位置上的精确分割。我在复现这个部分时踩过一个坑伪缺陷样本如果生成得太随机、变化太剧烈网络会学会“看到拼缝就报警”导致推理时对正常图像的边缘和纹理过渡区域产生大量误检。正确做法是要控制伪造缺陷的尺度范围和透明度尽量模拟真实缺陷的形状而不是简单粗暴地切块拼接。2.3 多尺度特征融合与分割头的精妙配合工业缺陷的尺寸跨度极大。细划痕在原始图像上可能只占几十个像素而产生的异常特征在深层的响应很弱大面积的污渍区域则相反浅层特征嘈杂、深层特征明显。MemSeg 的处理思路是引入多尺度特征融合把骨干网络不同阶段输出的特征做对齐和拼接保证浅层的细节信息和深层的语义信息都能到达分割头。分割头的设计上MemSeg 采用了比较经典的编解码结构并引入了注意力机制来增强对异常区域的响应。这个设计在实际效果上的直接体现是缺陷边缘的连续性明显变好了不像很多像素级分类方法那样东一块西一块的碎片化。如果你之前用过基于 Patch 的异常检测方法对比下来会很明显——MemSeg 输出的缺陷掩膜更像是“人画出来的”而不是“机器猜出来的”。2.4 损失函数设计的门道MemSeg 在损失函数上采用了多任务组合的方式同时优化特征匹配的一致性和分割结果的准确性。印象比较深的是它把正常样本也纳入分割损失的计算——换句话说正常图经过网络后分割头应该输出一张全零图。这个强迫网络“不要把正常当异常”的约束对降低误检率帮助很大。我自己的调参经验是分割损失和特征匹配损失之间的权重比例需要根据场景微调。默认 1:1 的配比我试过在 MVTec 上效果不错但换成工厂里光照不稳定的实时采集图像时误检明显增加后来把分割损失权重提到 1.3 左右效果才稳定下来。3. 从论文到产线工业仪表图像上的落地要点3.1 五类常见工业仪表图像的特点与难点很多仪表类产品的质检比如压力表、电流表、电压表、温度表、流量计看起来只是“拍一张表盘照片”实际上难度一点都不低。这些表除了外壳划痕、玻璃面板污渍和裂纹等常规缺陷外表盘还有非常密集的数字、刻度线和指针这些元素在传统算法眼里就是一堆高对比度的干扰信息。我在实际项目中遇到过不少有意思的案例。比如压力表表盘上的玻璃反光会形成一条贯穿刻度的亮带单一阈值分割几乎没法处理电流表刻度区域如果存在细小的油污人眼很难捕捉但模型能把区域标出来温度表在温差环境里容易产生内部水雾这种全局性异常一些基于重构的模型反而容易漏检因为水雾在整幅图上都有体现并不存在“局部突变”。流量计表面的轻微划痕则很容易与表盘本身丝印的纹路混淆。这正是 MemSeg 这类方法比较占优的地方。它学习的是正常表盘的完整特征分布不管是局部异物还是全局纹理变化只要偏离了正常范围的分布都能在特征层面体现出来。这与传统只在局部找异常的思路有本质区别。3.2 数据采集与预处理建议采集阶段有一个容易忽略的点正常样本也要足够“多样化”。很多项目初期只拍了一批光线均匀、表盘朝正前方的图片结果推理阶段稍微偏转一点角度或者补光灯衰减了一点误报率就暴涨。回头想问题其实出在“正常”这个定义太狭窄了。我的建议是打光方案定了之后先连续采一个班次甚至一整天的图像覆盖不同光照、不同摆放角度下的正常表盘然后做去重和筛选挑 500-1000 张作为训练集。推理阶段同时保留灰度图和彩色图两条通道对于表盘这种颜色编码信息丰富的场景比如电压表的红区警告区颜色信息对检测特定类型缺陷很有帮助。预处理方面建议将输入尺寸统一到 256 或 512。表盘这种纹理密集的目标我建议用 512虽然训练时间长了约 30%但小缺陷的召回率提升非常明显。3.3 训练和推理参数调优我的推荐配置MemSeg 默认的骨干网络是预训练的 ResNet这个选择挺务实工业场景下游特征比较通用ResNet 预训练权重带来了很好的起点。在实际复现中我习惯用更大的输入尺寸配合较大的 batch size以稳定 BatchNorm 的统计量。工业图像往往光照分布和自然图像差异较大BatchNorm 如果统计量不稳定很容易导致推理时特征分布漂移引起大面积误检。具体的调参经验可以参考我这里整理的一组默认配置参数建议值备注输入尺寸512表盘类纹理密集场景推荐骨干网络ResNet-50精度和速度的均衡选择训练轮次150-200正常样本数据量小防过拟合优化器SGD Momentum比 Adam 更稳泛化更好初始学习率0.01配合 warmup 和余弦衰减记忆库特征数量5万-10万按产品类型单独建库分割损失权重1.0-1.5光照不稳定场景调高另外要强调的是每个表盘类别压力表、电流表等建议独立建记忆库。不同仪表的表盘纹理差异太大混在一起会让记忆库变得“博而不精”匹配出的正常特征不够有参考性。我做过对比混合建库比独立建库的 AUROC 平均掉了 2 到 3 个百分点有些类别甚至掉了 5 个点。3.4 部署硬件与实时性考量产线部署和论文跑实验完全是两码事。论文里可以四张 V100 慢慢测产线只关心两件事一条皮带线上每秒过几个件、你检测一个件要多久。MemSeg 的检测过程包括推理、特征提取、记忆匹配和后处理分割整条链路在 GPU 上可以流水线化实际测试下来单张 2080Ti 上处理 512 分辨率图像大约需要 70ms 到 120ms完全能覆盖大多数每秒一个的产线节拍。如果你的节拍要求更高比如每秒五六个件可以考虑两个优化方向一是把骨干网络换成 ResNet-18 或者 MobileNet 这类轻量结构精度会损失一点但速度能翻倍二是把输入尺寸降到 256对表盘这种大面积目标检测影响不算大但对微小划痕的召回会有明显影响。工程就是取舍关键要想清楚哪些缺陷是必须抓的哪些是可以容忍漏掉的。4. 完整实操从零搭建一个 MemSeg 缺陷检测流程4.1 环境选型与依赖安装动手之前先把环境理清楚。我建议用 PyTorch 框架MemSeg 相关的参考实现大部分也都是基于 PyTorch 的。CUDA 版本和 PyTorch 版本要匹配这块如果出了问题前面的各种部署都会很被动。基本依赖如下pip install torch torchvision pip install opencv-python pip install scikit-learn pip install scipy pip install matplotlib代码组织上我习惯把项目拆成这样几部分config 目录存放不同数据集的参数文件dataset 目录做数据加载和伪缺陷生成models 目录放骨干网络和分割头memory 模块单独管理记忆库的构建、存储和匹配逻辑utils 目录放指标计算和可视化工具。4.2 记忆库构建与训练流程训练流程分成两个阶段。第一个阶段用正常样本建立记忆库我用一个示例来展示核心逻辑def build_memory_bank(model, dataloader, cfg): model.eval() features_list [] with torch.no_grad(): for imgs, _ in dataloader: imgs imgs.cuda() feats model.extract_features(imgs) # 返回多尺度特征 feats [torch.nn.functional.normalize(f, dim1) for f in feats] # 可选下采样减少特征数量控制记忆库规模 for f in feats: f torch.nn.functional.adaptive_avg_pool2d(f, (32, 32)) features_list.append(f.flatten(1)) memory_bank torch.cat(features_list, dim0) torch.save(memory_bank, cfg.memory_path)第二个阶段是训练分割网络。每个 batch 里正常样本经过增强或者伪造生成“异常样本”喂给分割头计算分割损失和特征匹配损失的联合损失。训练过程中需要定期用验证集做评估工业场景我一般用 AUROC 配合 F1 最大阈值来选模型。只盯着 AUROC 容易掉进一个陷阱它只衡量排序质量不衡量实际分割效果有些模型 AUROC 很高但输出的掩膜很碎、不连续没法直接用于后续的剔除和分类。所以我还额外关注像素级 IoU 和分割边的连续性。4.3 推理与可视化结果推理部分的核心流程是输入图像经骨干网络提取特征到记忆库中检索匹配得到异常响应图再把响应图送入分割头最终输出像素级异常掩膜。def inference(model, image, memory_bank): model.eval() with torch.no_grad(): feats model.extract_features(image.unsqueeze(0)) feats [torch.nn.functional.normalize(f, dim1) for f in feats] anomaly_map compute_anomaly_map(feats, memory_bank) mask model.seg_head(anomaly_map) return mask有一个小细节值得注意记忆匹配计算的是余弦距离距离越大说明越异常但实际输出的响应图往往有大量孤立噪点。我的做法是先做一个中值滤波再做一个小的开运算能去掉绝大多数噪点分割效果会好看很多。4.4 模型导出与产线对接如果只是在 PC 端部署直接保存权重文件就够了。如果要上边缘盒子或者用 TensorRT 加速需要把模型导出成 ONNX 再做转换。有一个容易踩的坑PyTorch 里如果在特征提取时用到了自适应池化导出 ONNX 时某些版本的算子支持不完整建议把自适应池化替换成固定大小的平均池化再导出。接口层面建议封装一个标准的检测类对外暴露predict(image) - (mask, score)方法。工业现场通常用 SDK 或者 HTTP 服务调用封装层保持简单稳定比花里胡哨更重要。5. 常见问题和排查经验实录5.1 高频问题速查表现象可能原因排查与解决训练损失不下降学习率过大或数据预处理错误降低学习率至 0.001检查图像归一化是否和预训练一致正常图像误检严重记忆库太小或分割损失权重过低扩充记忆库调高分割损失权重至 1.2-1.5小缺陷漏检输入尺寸过小或浅层特征利用不足提高输入尺寸到 512检查多尺度特征是否完整拼接推理时显存溢出Batch size 设置过大或特征维度过高降低 Batch size压缩记忆库特征维度ONNX 导出报错自适应池化算子不兼容替换为固定池化方式再导出5.2 显存优化与提速的三种有效手段第一招是给记忆库特征降维。原始高维特征直接存储一个类别可能有上万个特征显存开销很大。我试过用主成分分析把维度从 1024 降到 256检索速度提升接近三倍AUROC 只掉了 0.3 到 0.5 个百分点。在工业场景里这个代价完全能接受。第二招是缩小记忆库的规模而不是特征维度。观察特征分布的聚类情况用 K-Means 做聚簇每个簇只保留质心附近最有代表性的特征。我做过实验记忆库规模压缩到原来的 1/5效果几乎不变检索速度快了一截。第三招是推理时关闭梯度计算并把模型切到 eval 模式这一步很多人会写但真正做到位的不多。另外把图像预处理改成在 GPU 上用 NVTensor 完成也能省一点 CPU 和 GPU 之间的拷贝开销。产线场景里吃 CPU 的还是其他业务逻辑能省一点是一点。5.3 我在实际项目中总结的几点心得先说数据。无论算法多先进数据质量始终是决定项目成败的第一因素。我经历过一个项目算法侧折腾了一个月误检率始终压不下来。后来排查发现采集相机的自动增益没有关同一块正常物料的成像亮度在不同时间段差别很大。把相机参数锁死、打光调稳之后误检率直接下降了一个数量级。所以做工业视觉项目第一个星期的时间应该耗在成像方案上而不是调模型。再说评估指标。别只看论文里报的 AUROC产线只看两个数漏检率和误检率。漏检意味着缺陷品流出误检意味着好品被多剔两个都直接算钱。建议在项目启动阶段就设定好这两个指标的目标值并且用固定的验证集持续回归。很多项目做到后面精度上不去不是模型不行而是目标模糊。最后说模型维护。产线物料批次、光源衰减、环境温湿度变化都会导致模型性能缓慢退化。建议部署时设计一个简易的监测机制定期采集一些正常样本跑一遍推理看看平均异常分数的变化趋势。一旦发现基准值缓慢抬高就该考虑补充正常样本、微调记忆库或者重新训练分割头了。这种“体检式”的维护策略能帮你在出大事之前就发现问题。我在多次复现和落地 MemSeg 类方案的过程中最大的感受是这个方法把“半监督缺陷检测”从论文里拉到了工程可用的层面。它不依赖海量缺陷标注对算力的要求适中分割结果精确而且结构透明、便于调试。如果你正在为工业图像表面的缺陷检测发愁手头有一批容易获取的正常样本MemSeg 这条路值得认真试一把。
返回列表