Anomalib 中 L2BT 异常检测模型:Teacher-Student 蒸馏机制、参数配置与训练推理全流程
【免费下载链接】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
本篇基于 anomalib 仓库中 L2BT 模型的参考文档及其完整源码实现,系统讲解 L2BT(Learning to Be a Transformer to Pinpoint Anomalies)的算法原理与工程落地:包括L2BT(Lightning 层)与L2BTModel(PyTorch 层)的完整参数含义、Teacher-Student 特征蒸馏的训练流程、异常图生成与打分机制,以及 CLI 与 API 两种方式下的训练、推理和配置方法。读完后你可以直接复现 L2BT 的训练命令、按需调整layers、topk_ratio等超参数,并理解每个参数在源码中是如何生效的。
方法概述:用“学会当 Transformer”的预文本任务定位异常
L2BT 的核心思想来自 L2BT 模型 README 中的介绍:工业异常检测(IADS)方法通常依赖强大的预训练特征提取器,但为了处理高效性往往将输入降采样到 224×224 的低分辨率,这会削弱方法定位微小缺陷的能力。L2BT 采用 Teacher-Student 范式,在保留强预训练特征的同时,用轻量网络高效处理高分辨率输入:
- Teacher(教师):一个冻结的预训练 Vision Transformer(仓库实现中为 DINOv2-reg ViT-Base/14),负责提取两个指定 Transformer 层的 patch embeddings;
- Students(学生):两个浅层 MLP(
backward_net与forward_net),在正常(nominal)图像上训练,学习“中间层 patch 特征 → 最后一层 patch 特征”的正向映射,以及反向映射。
这一学习过程本身构成了一个有挑战性的预文本任务(pretext task):小容量的学生网络在正常图像上能够学会层的间映射,但在分布外(异常)图像上难以复现该映射,由此暴露异常。模型学习类型为LearningType.ONE_CLASS(单类学习),即只用正常图像训练,定义见 L2BT 类。
整体结构:L2BT(Lightning 层)与 L2BTModel(PyTorch 层)
anomalib 遵循统一的模型分层设计:L2BT继承自AnomalibModule,负责与 Lightning 训练器、数据模块、预处理/后处理、指标评估和可视化的集成;而真正的前向计算放在纯 PyTorch 的L2BTModel中。两者分别在以下文件定义:
- L2BT(Lightning 实现)
- L2BTModel(PyTorch 架构实现)
- FeatureExtractor(冻结 Teacher)
- FeatureProjectionMLP(学生网络)
- L2BTAnomalyMapGenerator(异常图生成器)
L2BT 完整参数说明
L2BT构造函数的全部参数、默认值及源码行为如下(默认值取自 L2BT.init):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
lr | float | 1e-4 | 学生网络的优化学习率。注意:只有两个学生 MLP 会被优化,Teacher 全程冻结 |
layers | Sequence[int] | (7, 11) | 从 ViT 中提取 patch 特征的恰好两个层索引,分别作为“中间层”和“最后一层”喂给两个学生网络 |
blur_w_l | int | 5 | 第一级模糊核宽度(必须为奇数) |
blur_w_u | int | 7 | 第二级模糊核宽度(必须为奇数) |
blur_pad_l | int | 2 | 第一级模糊的 padding,必须等于blur_w_l // 2以保持尺寸 |
blur_pad_u | int | 3 | 第二级模糊的 padding,必须等于blur_w_u // 2 |
blur_repeats_l | int | 5 | 第一级模糊的重复应用次数 |
blur_repeats_u | int | 3 | 第二级模糊的重复应用次数 |
topk_ratio | float | 0.001 | 计算图像级异常分时取异常图 top 比例位置的平均值(取值范围0 < topk_ratio <= 1) |
pre_processor | PreProcessor | bool | True | 输入预处理器,True时使用 L2BT 默认预处理管线 |
post_processor | PostProcessor | bool | True | 输出后处理器,True时使用默认 |
evaluator | Evaluator | bool | True | 指标评估器,True时使用默认 |
visualizer | Visualizer | bool | True | 结果可视化器,True时使用默认 |
两个容易被忽略的实现细节:
- 模糊参数存在强校验。
L2BTAnomalyMapGenerator在构造时会断言核宽必须为奇数、且 padding 必须等于kernel_size // 2,否则会抛出ValueError(见 _validate_blur_params);topk_ratio不满足0 < topk_ratio <= 1同样报错(见 _validate_topk_ratio)。 - 旧 checkpoint 的自动迁移。L2BT 的冻结 DINOv2 Teacher 曾从自定义 ViT 迁移到
TimmFeatureExtractor,on_load_checkpoint会调用restore_frozen_encoder_weights丢弃旧编码器权重、替换为当前 timm 编码器权重,从而保证严格 state-dict 加载仍能成功(见 on_load_checkpoint)。
默认预处理管线:SquarePad → Resize → ImageNet 归一化
L2BT通过configure_pre_processor定义了与原论文一致的输入变换(见 configure_pre_processor):
SquarePad:边缘复制填充为正方形;Resize(224, 224):双三次插值(bicubic)+ 抗锯齿;Normalize:ImageNet 均值[0.485, 0.456, 0.406]、标准差[0.229, 0.224, 0.225]。
image_size可传入自定义目标尺寸,默认为(224, 224)。由于 Teacher 的 timm 后端启用了dynamic_img_size=True,从源码结构看模型支持动态分辨率输入——这也正是原论文能在高分辨率(1036×1036)下运行的关键前提。
L2BTModel 前向流程:训练与推理双模式
L2BTModel.forward 依据模块所处模式分派两条路径:
训练路径:双向余弦相似度损失
训练时(self.training=True),流程为:
extract_teacher_features:在torch.no_grad()下调用冻结 Teacher,得到中间层与最后一层的 patch 特征张量(middle_patch、last_patch)。Teacher 还会被强制eval(),确保父模块处于 train 模式时 Teacher 行为不变(见 extract_teacher_features);predict_student_features:backward_net从最后一层特征预测中间层特征,forward_net从中间层特征预测最后一层特征(见 predict_student_features);compute_losses:对两个方向分别计算1 - 余弦相似度均值,总损失为两者之和:
loss_middle = 1 - self.cos_sim(predicted_middle_patch, middle_patch).mean() loss_last = 1 - self.cos_sim(predicted_last_patch, last_patch).mean() loss = loss_middle + loss_last(见 compute_losses)
Lightning 层的training_step会将train_loss、train_loss_middle、train_loss_last分别记录到日志(见 training_step)。
优化器:只训练学生
configure_optimizers使用 Adam,参数组仅包含backward_net与forward_net的参数(Teacher 的所有参数requires_grad=False),学习率即构造参数lr(见 configure_optimizers 与 Teacher 冻结逻辑)。
Teacher 与 Student 的底层实现
- Teacher(teacher.py):通过
TimmFeatureExtractor加载vit_base_patch14_reg4_dinov2(DINOv2-reg ViT-Base/14,patch 大小 14),按layers参数抽取blocks.{i}的 patch tokens(去掉 CLS/register token),并应用 backbone 的最终 norm。layers长度必须恰好为 2,否则抛出ValueError。 - Student(FeatureProjectionMLP):三层全连接结构
Linear(in→hidden) → GELU → Linear(hidden→hidden) → GELU → Linear(hidden→out),其中hidden_dim = (in_features + out_features) // 2。由于输入输出维度均为 Teacher 的embed_dim,实际为 768→768→768→768 的浅层投影网络。
推理路径:从特征失配到异常图
推理时(self.training=False),L2BTAnomalyMapGenerator将“学生特征与教师特征的失配”转换为空间异常图(见 forward):
- 逐 patch 失配:对两组(中间层、最后一层)的归一化特征向量分别计算逐 token 的 L2 距离,得到两个 patch 网格上的失配图;
- 相乘融合:
combined = middle_anom * last_anom,重排为(B, 1, h_p, w_p)的 patch 网格(h_p = h // patch_size,要求输出尺寸能被 patch_size 整除,否则报错); - 上采样:双线性插值放大到输入图像的完整分辨率
(h, w); - 平滑:两级均匀核
conv2d模糊——先以blur_w_l核重复blur_repeats_l次,再以blur_w_u核重复blur_repeats_u次,缓解 patch 级网格的块状伪影; - 图像级打分:取异常图中最大的
k = max(1, int(H*W * topk_ratio))个值求均值,作为该图的pred_score(见 _score_topk_mean)。
推理输出为InferenceBatch(pred_score=..., anomaly_map=...),形状分别为(B,)与(B, 1, H, W)。单元测试 test_l2bt.py 验证了 224×224 输入下anomaly_map.shape == (1, 1, 224, 224)与pred_score.shape == (1,);该测试用一个轻量DummyTeacher替换真实 DINOv2 骨干以避免加载重型权重,同时 test_training_forward 验证训练前向返回loss、loss_middle、loss_last三个键。
配置与训练
模型配置文件
L2BT 提供开箱即用的配置样例 l2bt.yaml,可通过anomalib config --model_path引用:
model: class_path: anomalib.models.L2BT init_args: lr: 1.0e-4 layers: [7, 11] blur_w_l: 5 blur_w_u: 7 blur_pad_l: 2 blur_pad_u: 3 blur_repeats_l: 5 blur_repeats_u: 3 topk_ratio: 0.001CLI 训练
在支持 anomalib 框架的数据集上训练(以 VisA 的 capsules 类别为例):
anomalib train \ --model L2BT \ --data Visa \ --data.category capsules若在机器上遇到 dataloader 或共享内存问题,可减小 workers 与 batch size(引自 模型 README):
anomalib train \ --model L2BT \ --data Visa \ --data.category capsules \ --data.num_workers 0 \ --data.train_batch_size 1 \ --data.eval_batch_size 1 \ --trainer.max_epochs 1训练过程中 anomalib 自动管理数据集加载、实验日志、checkpoint 保存与评估指标;checkpoint 无需外部管理,会存入实验输出目录。
API 训练
等价的 Python API 用法(来自 lightning_model.py 模块 docstring):
from anomalib.models.image import L2BT from anomalib.engine import Engine from anomalib.data import MVTecAD datamodule = MVTecAD() model = L2BT( layers=(7, 11), topk_ratio=0.001, ) engine = Engine(model=model, datamodule=datamodule) engine.fit() predictions = engine.predict()CLI 推理
训练完成后即可用 checkpoint 对单张图像或整个目录做预测:
anomalib predict \ --model L2BT \ --data <PATH_TO_IMAGE_OR_FOLDER> \ --ckpt_path <PATH_TO_CHECKPOINT>典型示例:
anomalib predict \ --model L2BT \ --data datasets/visa/visa_pytorch/capsules/test \ --ckpt_path results/L2BT/Visa/capsules/latest/weights/lightning/model.ckpt该命令会输出每张图像的异常分数(anomaly score)与异常图(anomaly map)。
适用前提与性能参考
使用 L2BT 需注意以下适用前提(均来自 模型 README 与源码实现):
- 分辨率与学习率差异:原论文使用高分辨率输入(1036×1036)与
lr=0.001;anomalib 默认输入为 224×224 且lr=1e-4。README 明确指出这两处差异可以解释复现指标与论文报告指标之间的差距(论文报告:MVTec AD I-AUROC 0.988,VisA I-AUROC 0.964)。若追求论文级表现,需要借助 Teacher 的动态分辨率能力输入更高分辨率图像并调高学习率; layers必须恰好两个索引:Teacher 的输出会被解包为(middle_patch, last_patch)分别供两个学生网络使用,传入其他数量的索引会在构造阶段直接抛出ValueError(见 L2BTModel.init校验);- 单类学习:训练数据应仅为正常(nominal)图像,与
LearningType.ONE_CLASS一致; - 评测数据集:原论文在 MVTec AD 与 VisA 上评测;在 anomalib 中则可使用框架支持的任意数据集。
性能参考方面,模型 README 给出了以 seed42、max_epochs=50采集的 MVTec AD 与 VisA 逐类别指标表(Image/Pixel 级 AUC 与 F1),例如 MVTec AD 平均 Image AUC 为 0.977、Pixel AUC 为 0.973,VisA 平均 Pixel AUC 为 0.977。这些是仓库自述的复现结果,实际数值会随输入分辨率、训练轮数与随机种子变化,引用时建议以仓库文件为准自行复现。
小结
anomalib 中的 L2BT 将“冻结 DINOv2 Teacher + 两个浅层学生 MLP”的 Teacher-Student 蒸馏范式完整工程化:L2BT负责 Lightning 训练循环、默认预处理(SquarePad → Resize → ImageNet 归一化)与只优化学生的 Adam 优化器;L2BTModel负责双向余弦损失训练与特征失配推理;L2BTAnomalyMapGenerator负责 patch 级失配相乘、双线性上采样、两级模糊平滑与 top-k 均值打分。配合 l2bt.yaml 配置与anomalib train/anomalib predict命令,即可在单类正常数据上完成训练并输出异常分数与异常图。
【免费下载链接】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),仅供参考