SuperSimpleNet 异常检测模型实战指南:anomalib 中的统一监督表面缺陷检测实现
【免费下载链接】anomalibAn anomaly detection library comprising state-of-the-art algorithms and features such as experiment management, hyper-parameter optimization, and edge inference.项目地址: https://gitcode.com/GitHub_Trending/an/anomalib
SuperSimpleNet 是集成在 anomalib 中的一种判别式(discriminative)表面缺陷/异常检测模型,源自 ICPR 2024 论文SuperSimpleNet: Unifying Unsupervised and Supervised Learning for Fast and Reliable Surface Defect Detection,并由 JIMS 2025 论文No Label Left Behind扩展出全监督范式。本文以 模型官方 README 为主线,结合 PyTorch 实现、Lightning 模块、异常生成器、损失函数 与单元测试,完整讲解其架构原理、核心参数、训练命令、注意事项与 MVTecAD 基准结果,帮助你在 anomalib 中快速上手并理解其底层工作方式。
SuperSimpleNet 架构图,展示特征提取、特征适配、合成异常生成与分割-检测模块的完整流程
模型概述:从 SimpleNet 演化而来的四组件结构
SuperSimpleNet 是一个"简单但强"(simple yet strong)的判别式缺陷检测模型,由 SimpleNet 架构演化而来。根据 README 的描述,它由四个核心组件构成:
- 带上采样的特征提取器(Feature Extractor with Upscaling):使用类 ResNet 骨干网络提取多尺度特征,上采样后做平均池化以聚合相邻上下文;
- 特征适配器(Feature Adaptor):对提取的特征进行(可选的)适配,使其更适合异常检测任务;
- 特征级合成异常生成模块(Feature-level Synthetic Anomaly Generation Module):仅在训练阶段生效,通过二进制 Perlin 噪声掩码界定区域并注入高斯噪声,在特征层面合成异常;
- 分割-检测模块(Segmentation-Detection Module):同时输出逐像素异常图(anomaly map)和图像级异常分数(anomaly score)。
在推理阶段,合成异常生成被跳过,模型直接预测异常图与异常分数,并将预测的异常图上采样回输入图像尺寸,再经高斯滤波平滑。模型类型为Segmentation(分割)。
值得说明的是:原始模型本身支持无监督与有监督两种设置,但Anomalib 当前仅支持无监督训练,这一点在 lightning_model.py 的模块注释和learning_type属性中均有明确声明。
架构深潜:从源码看四个组件的具体实现
1. 上采样特征提取器
UpscalingFeatureExtractor(见 torch_model.py)封装了TimmFeatureExtractor骨干网络:
- 骨干默认取
wide_resnet50_2.tv_in1k,默认使用layer2、layer3两层特征; - 提取特征时将骨干置于
eval()模式并用torch.no_grad()冻结,因此骨干权重在训练中不被更新; - 各层特征被双线性插值上采样到第一层(最大层)尺寸的 2 倍,再按通道拼接;
- 拼接后的特征通过
nn.AvgPool2d(kernel_size=3, stride=1, padding=1)进行邻域 patch 聚合,捕捉局部上下文信息。
get_channels_dim()通过对随机输入做一次 dry-run 前向计算得到拼接后的通道总数,供下游适配器与分割头确定维度。
2. 特征适配器
FeatureAdapter(torch_model.py)是一个 1×1 卷积(等价于线性层),保持通道维度不变,用init_weights(Xavier 初始化卷积/线性层、常量 1 初始化 BN)完成初始化,用于将原始特征适配到异常检测任务。
3. 特征级合成异常生成器
AnomalyGenerator(anomaly_generator.py)实现训练阶段的合成异常逻辑,核心参数为noise_mean=0、noise_std=0.015、threshold=perlin_threshold(默认 0.2):
- 每个样本的特征被复制一份,形成
[B*2, ...]的新 batch; - 生成高斯噪声并乘上一个"噪声掩码",该掩码由两部分取交集得到:
1 - GT掩码(不与真实异常区域重叠)与二值化 Perlin 噪声掩码(perlin > threshold置 1); - 若某张 Perlin 噪声图整体低于阈值,则先做 min-max 归一化再缩放到 [-1, 1] 后重新二值化,保证掩码始终有效;
- 以 50% 概率将 Perlin 掩码整体置零(
torch.rand(1).item() > 0.5),即只有约一半样本被注入合成异常,实现类不平衡的负样本采样; - 噪声掩码与 GT 掩码相加并二值化,得到更新后的 GT 掩码;标签同步更新(掩码中任何位置为 1 则标签为 1)。
Perlin 噪声由 generate_perlin_noise 生成,尺寸向上取整到 2 的幂,再插值回特征尺寸,以保证可复现性。
4. 分割-检测模块
SegmentationDetectionModule(torch_model.py)同时承担两个头的输出:
- 分割头
seg_head:1×1 卷积(通道 →1024→ 1,带 BatchNorm 与 LeakyReLU(0.2)),输出逐像素异常图; - 分类头
cls_conv+cls_fc:将特征与分割头输出(可 detach)沿通道拼接,经 5×5 卷积 + BN + ReLU 后,分别对卷积结果和预测图做平均池化与最大池化,最终拼接成128*2 + 2 = 258维向量,由全连接层输出图像级异常分数; stop_grad=True时,从分类头回传到分割头的梯度被切断(map_dec_copy.detach()),这是无监督设置下的默认行为(见 lightning_model.py)。
异常图后处理
AnomalyMapGenerator(torch_model.py)将分割头输出双线性插值回原始输入尺寸,再用GaussianBlur2d(sigma=4,核尺寸2*ceil(3*sigma)+1)平滑,得到最终异常图;推理时异常图与分数均经过 sigmoid 归一化后封装进InferenceBatch。
ICPR 与 JIMS 两个版本的区别:adapt_cls_features
README 明确指出,ICPR 与 JIMS 两版代码目前的唯一区别是adapt_cls_features参数——它控制分类头使用的特征是否经过适配:
- ICPR 版:
adapt_cls_features=True,分类头与分割头都使用适配后的特征; - JIMS 扩展版:
adapt_cls_features=False(默认值),只适配分割头特征,分类头直接使用原始特征。
从前向实现(torch_model.py)可以看到,训练时若adapt_cls_features=True,合成噪声只施加在适配特征上(input_features=None,分割与分类共享noised_adapt);若为 False,则对原始特征与适配特征施加同一噪声,分割头用noised_adapt、分类头用noised_feat。推理路径同理:分类特征在 ICPR 模式下取adapted,JIMS 模式下取原始features。单元测试 test_model.py 对adapt_cls_features两种取值都做了前向验证,确保两条路径均可运行。
在 anomalib 中训练 SuperSimpleNet
CLI 命令行方式
README 给出的标准训练命令为:
anomalib train --model SuperSimpleNet --data MVTecAD --data.category <category>将<category>替换为 MVTecAD 的具体类别(如bottle、cable、transistor等)即可启动训练。训练过程中的关键超参数可通过额外的 CLI 参数覆盖,例如:
anomalib train --model SuperSimpleNet --data MVTecAD --data.category bottle \ --trainer.max_epochs 300 --trainer.devices 1Python API 方式
参考 lightning_model.py 中的示例,也可在代码中直接使用:
from anomalib.data import MVTecAD from anomalib.models import Supersimplenet from anomalib.engine import Engine datamodule = MVTecAD() model = Supersimplenet() engine = Engine() engine.fit(model, datamodule=datamodule) predictions = engine.predict(model, datamodule=datamodule)Supersimplenet类通过init.py 从异常检测模型注册表中导出,可直接被 Engine 与 CLI 加载。
核心参数与实现细节
SupersimplenetLightning 模块的构造参数(lightning_model.py)及其默认值如下:
| 参数 | 默认值 | 含义 |
|---|---|---|
perlin_threshold | 0.2 | Perlin 噪声二值化阈值,决定合成异常区域的分布 |
backbone | wide_resnet50_2.tv_in1k | 骨干网络名,必须使用 torchvision V1 权重 |
layers | ["layer2", "layer3"] | 骨干网络中用于特征提取的层 |
supervised | False | 是否启用有监督训练(当前 Anomalib 仅支持无监督) |
adapt_cls_features | False | 是否适配分类头特征(ICPR=True / JIMS=False) |
这些参数会透传给SupersimplenetModel,其中stop_grad由supervised间接决定:无监督时为True(并设置gradient_clip_val=0),有监督时为False(gradient_clip_val=1),见 lightning_model.py。
优化器与学习率调度
configure_optimizers(lightning_model.py)采用分组 AdamW:
- 适配器:
lr=0.0001; - 分割-检测模块:
lr=0.0002、weight_decay=0.00001; - 调度器:
MultiStepLR,milestones=[0.8*max_epochs, 0.9*max_epochs](分别取整数),gamma=0.4。
即训练到总 epoch 数的 80% 和 90% 时各衰减一次学习率至 0.4 倍。
损失函数
SSNLoss(loss.py)由三部分组成:
Lseg = Lfocal(map) + Ltruncl1(map):分割头的 Focal Loss(alpha=-1, gamma=4.0)加截断 L1 损失——将正常像素分数推向负值、异常像素分数推向正值,截断项truncation_term=0.5防止过拟合;Lcls = Lfocal(score):分类头的 Focal Loss。
数据预处理
默认预处理器(lightning_model.py)将图像 Resize 到(256, 256)(antialias=True),并使用 ImageNet 均值[0.485, 0.456, 0.406]与标准差[0.229, 0.224, 0.225]做标准化。
训练注意事项(README 特别强调)
README 用醒目格式标注了以下关键约束:
骨干网络权重:模型已验证可配合WideResNet50 的 torchvision V1 权重工作。理论上兼容大多数 ResNet 与 WideResNet,但若使用默认噪声标准差(0.015),务必使用 V1 权重。正确权重名以
.tv_[...]结尾,而非tv2(例如wide_resnet50_2.tv_in1k正确,tv2系列不正确)。
训练时长:建议以batch size 32 训练 300 个 epoch,以保证随机异常生成机制下训练的稳定性。使用更小的参数也能训练,但可能无法达到最优效果。
监督范式:弱监督、混合监督与全监督训练请参考 官方原始代码,Anomalib 当前仅支持无监督。
另外从源码还可以补充两点实操约束:其一,训练时若masks为空且 batch 中存在异常样本(labels.any()为真),会抛出RuntimeError("Training with anomalous samples without GT masks is currently not supported!")(torch_model.py),对应单元测试 test_fail_anomalous_no_mask;其二,模型在 OpenVINO 导出时对squeeze的使用做了特殊处理(reshape替代),见 torch_model.py。
MVTecAD 基准结果
以下结果由本 Anomalib 实现以 seed 0、默认参数、batch size 32 训练 300 个 epoch 得到(完整数据见 README):
| Category | AUROC (ICPR) | AUROC (JIMS) | AUPRO (ICPR) | AUPRO (JIMS) |
|---|---|---|---|---|
| Bottle | 1.000 | 1.000 | 0.903 | 0.911 |
| Cable | 0.981 | 0.951 | 0.901 | 0.893 |
| Capsule | 0.989 | 0.992 | 0.931 | 0.919 |
| Carpet | 0.985 | 0.974 | 0.929 | 0.935 |
| Grid | 0.994 | 0.998 | 0.930 | 0.938 |
| Hazelnut | 0.994 | 0.999 | 0.943 | 0.939 |
| Leather | 1.000 | 1.000 | 0.970 | 0.974 |
| Metal_nut | 0.995 | 0.993 | 0.920 | 0.925 |
| Pill | 0.962 | 0.980 | 0.936 | 0.943 |
| Screw | 0.912 | 0.854 | 0.947 | 0.946 |
| Tile | 0.994 | 0.992 | 0.854 | 0.825 |
| Toothbrush | 0.908 | 0.908 | 0.860 | 0.854 |
| Transistor | 1.000 | 1.000 | 0.907 | 0.916 |
| Wood | 0.987 | 0.991 | 0.858 | 0.872 |
| Zipper | 0.995 | 0.999 | 0.928 | 0.944 |
| Average | 0.980 | 0.975 | 0.914 | 0.916 |
在 VisA、SensumSODF 与 KSDD2 等其他数据集上的更多结果,可参阅 JIMS 2025 论文。
进一步阅读
- 模型模块入口:导出
Supersimplenet类 - PyTorch 模型实现:四个子模块与完整前向逻辑
- Lightning 训练模块:优化器、调度器、损失与预处理配置
- 合成异常生成器:Perlin 掩码与高斯噪声注入细节
- 损失函数:Focal Loss 与截断 L1 的组合
- 单元测试:ICPR/JIMS 双路径与无掩码训练行为验证
- API 参考文档:架构图与自动化 API 文档
总体而言,SuperSimpleNet 用极简的设计(冻结骨干 + 特征适配 + 特征级合成异常 + 分割-检测双头)在缺陷检测任务上取得了接近 SOTA 的性能,同时保持快速推理;在 anomalib 中只需一条 CLI 命令即可复现论文中的无监督基准结果。
【免费下载链接】anomalibAn anomaly detection library comprising state-of-the-art algorithms and features such as experiment management, hyper-parameter optimization, and edge inference.项目地址: https://gitcode.com/GitHub_Trending/an/anomalib
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考