本文最后更新于:2024年1月14日 晚上

本文介绍异常检测 2023 年一篇优秀工作 —— SimpleNet。

基本信息

项目 内容 备注
方法名称 SimpleNet
论文题目 SimpleNet: A Simple Network for Image Anomaly Detection and Localization
论文连接 https://arxiv.org/pdf/2303.15140v2.pdf
开源代码 https://github.com/donaldrr/simplenet
发表时间 2023.05.28
方法类别 深度学习 -> 基于特征 -> 自监督学习
Detection AU-ROC 99.6%
Segmentation AU-ROC 98.1%
Segmentation AU-PRO -
FPS 77 (FP32 on 3080ti)
核心思想 提取正常图像特征,人工在正常图像特征上加噪作为异常特征,训练分类网络进行分类判断

论文解析

框架

SimpleNet 包含 特征提取器,特征适配器,异常特征生成器和判别器。

特征提取器

用类似 ResNet 网络提取不同层级的图像特征:

$$ \phi^{l,i}\sim\phi^{l}(x_{i})\in\mathbb{R}^{H_{l}\times\dot{W_{l}}\times C_{l}}, $$

$l$ 表示层级,$x_i$ 为输入数据,$\phi$ 为特征

定义 Patch Size $p$ ,将特征分为 $p \times p$ 的小块,自适应池化:

$$ z_{h,w}^{l,i}=f_{agg}(\{\phi_{h',y'}^{l,i}|(h',y')\in\mathcal{N}_{p}^{h,w}\}) $$

将来自不同层级的特征 resize 到相同(最大)尺寸,将特征在通道层拼接起来:

$$ o^i=f_{cat}(resize(z^{l',i},(H_0,W_0))|l'\in L $$ 简写为: $$ o^i=F_\phi(x^i) $$

特征适配器

由于工业图像通常与主干预训练中使用的数据集具有不同的分布,因此采用特征适配器 $G_θ$ 将训练特征转移到目标域 $q$。

$$ q_{h,w}^i=G_\theta(o_{h,w}^i) $$

特征适配器可以由简单的神经块组成,例如全连接层或多层感知器(MLP)。通过实验发现单个全连接层性能就足够好了。

异常特征生成器

为了训练判别器估计样本正常的似然概率,最简单的方法是对负样本(缺陷特征)进行采样,并将其与正常样本一起优化。但是异常样本的数量往往不足以支持训练,不同于其他文章生成异常图像,本文在特征空间中的正常样本上添加简单的噪声生成异常特征(文章声明该方法优于其他手工方法)。

异常特征是通过在正常特征 $q_{h,w}^i\in{\mathbb{R}^C}$ 上添加高斯噪声生成的,噪声 $\epsilon\in\mathbb{R}^C$ 独立同分布地采样于高斯分布 $\mathcal{N}(\mu,\sigma^2)$,异常特征 $q^{i-}_{h,w}$ 表示为: $$ q_{h,w}^{i-}=q_{h,w}^{i}+\epsilon $$

判别器

判别器用于判断数据是否为异常,直接输出 $(h,w)$ 位置的正常水平,正常与添加过异常扰动的人工异常特征共同训练,相当于训练分类网络。

文章仅使用 2 层感知机来完成这个步骤。

判别器标记为 :
$$
D_{\psi}(q_{h,w})\in\mathbb{R}.
$$

训练

端到端训练判别器、特征适配器、特征提取器

单个 Patch 损失函数为:

$$ l_{h,w}^{i}=\max(0,th^{+}-D_{\psi}(q_{h,w}^{i}))+\max(0,-th^{-}+D_{\psi}(q_{h,w}^{i-})) $$

其中 $th^+ = 0.5,th^-=-0.5$

整体损失函数:

$$ \mathcal{L}=\min_{\theta,\psi}\sum_{x^i\in\mathcal{X}_{train}}\sum_{h,w}\frac{l_{h,w}^i}{H_0*W_0} $$

推断时不需要异常生成器了,异常分数直接由一系列前向推导得到:

$$ s_{h,w}^i=-D(q^i_{h,w}) $$ 推理过程中异常定位的异常图定义为: $$ S_{AL}(x_{i}):=\{s_{h,w}^{i}|(h,w)\in W_{0}\times H_{0}\} $$

将结果 resize 到原始图像大小即可得到异常定位图。

同时图像级异常检测结果的得分:
$$
S_{AD}(x_i):=\max_{(h,w)\in W_0\times H_0}s_{h,w}^i
$$
由于网络简单,在 3080Ti 上 256*256 的图在未经过量化的模型上可以达到接近 80 的FPS。

源码解析

代码仓库:https://github.com/donaldrr/simplenet

数据集依赖

需要下载 MVTec AD 数据集

放到仓库根目录 data/MVtec_ad

环境依赖

说明文档中没有说清楚环境依赖,我根据环境配置过程整理了 requirements.txt

1
2
3
4
5
6
7
8
9
10
11
torch==1.12.1
torchvision==0.13.1
numpy==1.22.4
opencv-python==4.5.1.48
pandas==1.2
scikit-learn
timm
click
scikit-image
tensorboardX
matplotlib

文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
.
├── backbones.py // 特征提取 backbone
├── common.py // 网络结构抽象概念聚合
├── data // 原始数据
│ └── MVTec_ad
├── datasets // 数据加载方式
│ ├── btad.py
│ ├── cifar10.py
│ ├── __init__.py
│ ├── mvtec.py
│ ├── sdd2.py
│ └── sdd.py
├── imgs // 网络结构示意图
│ └── cover.png
├── LICENSE
├── main.py // 核心入口文件
├── metrics.py // 评价指标计算
├── README.md
├── requirement.txt // 环境依赖(手动添加,原始仓库没有)
├── resnet.py // Resnet 网络
├── results // 输出结构示意
│ └── MVTecAD_Results
│ └── simplenet_mvtec
│ └── run
│ ├── models
│ │ └── 0
│ │ ├── mvtec_bottle
│ │ │ ├── ckpt.pth
│ │ │ └── tb
│ │ │ └── events.out.tfevents.1704393817.XDHN-SH-026
│ └── results.csv
├── run.sh // 运行程序文件(Python 用了 click 包,正常情况下仅支持命令行运行)
├── simplenet.py // 包含网络定义,辨别器、特征转换,模型训练、评估等网络结构、过程代码
├── utils.py // 工具代码
└── VERSION

运行方式

该仓库使用了 Python click 包,将略复杂的训练流程整合成一行命令方便用户运行,直接运行 $run.sh$ 即可。

但是问题是通过脚本调用 Python 命令无法调试代码,因此在 VSCode 中做了调整:

创建 launch.json 文件,将命令添加到 python 配置 args 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false,
"args": ["--gpu", "0", "--seed", "0", "--log_group", "simplenet_mvtec", "--log_project", "MVTecAD_Results", "--results_path", "results", "--run_name", "run", "net", "-b", "wideresnet50", "-le", "layer2", "-le", "layer3", "--pretrain_embed_dimension", "1536", "--target_embed_dimension", "1536", "--patchsize", "3", "--meta_epochs", "40", "--embedding_size", "256", "--gan_epochs", "4", "--noise_std", "0.015", "--dsc_hidden", "1024", "--dsc_layers", "2", "--dsc_margin", ".5", "--pre_proj", "1", "dataset", "--batch_size", "8", "--resize", "329", "--imagesize", "288", "-d", "screw", "mvtec", "./data/MVTec_ad"]

}
]
}

即可不改变代码结构的同时在 VSCode 中运行、调试代码。

数据流

  1. 数据入口为 main.py 文件,

    • net 函数根据配置确定 simplenet 网络

    • dataset 函数根据配置确定训练、测试数据集(可以多个)

    • run 函数执行训练流程

      • 核心代码为

        1
        i_auroc, p_auroc, pro_auroc = SimpleNet.train(dataloaders["training"], dataloaders["testing"])
  2. 随后进入 simplenet.py 文件中 simplenettrain 流程

    • 训练代码为

      1
      self._train_discriminator(training_data)
    • 进入训练特征适配器和判别器的流程

  3. 训练完成进入测试、评估流程

其中存在一个问题是模型性能是在多次对测试集进行评估后选择最高的作为结果,这其实并不合理,但总的来说瑕不掩瑜,能用如此简单的结构实现较好的结果已经证明了该工作的价值

原始论文

参考资料



文章链接:
https://www.zywvvd.com/notes/study/deep-learning/anomaly-detection/simple-net/simple-net/


“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付

异常检测 SimpleNet
https://www.zywvvd.com/notes/study/deep-learning/anomaly-detection/simple-net/simple-net/
作者
Yiwei Zhang
发布于
2023年12月25日
许可协议