SimpleNet:A Simple Network for Image Anomaly Detection and Localization


1. 论文信息

  • 论文标题:SimpleNet: A Simple Network for Image Anomaly Detection and Localization
  • 发表会议:CVPR 2023
  • 研究方向:工业图像异常检测与定位
  • 核心关键词:无监督异常检测、特征适配、异常特征合成、判别器
  • 文章链接:https://arxiv.org/abs/2303.15140

2. 研究背景

图像异常检测与定位(Image anomaly detection and localization)旨在准确识别出存在缺陷的异常图像,并精确标定出异常子区域的具体位置。在真实的工业场景中实现高精度的检测与定位仍面临极其严峻的挑战,这主要归结于两大技术痛点。

  • 异常样本稀缺:在运转良好的流水线上,绝大多数生产出来的零件都是合格的,很难收集到海量且全面的残次品图片来训练模型。

  • 缺陷跨度大:异常特征的形态千变万化,既包含极其细微的瑕疵,也包含大面积的结构性损坏。

3. 现有方法

工业场景下的图像异常检测,本质上是一个“单类分类”问题。我们通常只能获取大量“正常”产品的图像,而“异常”样本要么极少,要么种类未知、难以穷举。这种数据特性决定了主流方法必须是无监督或自监督的。现有异常检测方法大致可以分为三类:

3.1 基于重建的方法

这类方法通常使用自编码器或生成对抗网络,试图让模型学会完美重建正常样本。当异常样本输入时,由于其模式未被学习,重建误差会显著增大,从而被检测出来。但如果模型重建能力过强,可能连异常区域也能较好重建,从而造成漏检;如果模型能力不足,又可能连正常样本都重建不好,产生误报。

3.2 基于合成异常的方法

这类方法通过 CutPaste、噪声扰动等方式在正常图像上人工构造异常样本,然后训练模型区分正常与异常。然而,其核心挑战在于 “合成的异常不够真实” 。工业缺陷的形态、纹理、边缘特性往往非常特定,简单的图像操作合成的“伪缺陷”,其特征可能与真实的异常分布相去甚远。用这些“不真实”的负样本训练出的决策边界往往是松散甚至扭曲的,导致模型在实际复杂缺陷面前表现不佳。

3.3 基于特征嵌入的方法

这类方法利用 ImageNet 预训练网络提取图像特征,将图像映射到一个高维特征空间。在这个空间里,所有正常样本的特征应该聚集在一个紧凑的区域内,而异常样本则落在区域之外。后续通过多元高斯分布、归一化流或记忆库等统计方法,来建模这个“正常区域”的边界。虽然性能较强,但可能存在预训练特征与工业图像域不匹配,以及推理复杂度较高的问题。

4. SimpleNet

SimpleNet 的核心思路可以概括为:

不在像素空间中复杂地生成异常,而是在特征空间中构造异常特征,并训练一个轻量判别器区分正常特征和异常特征。

它继承了基于合成方法的“制造负样本”思想,以及基于嵌入方法的“利用预训练特征”优势,并通过两个关键创新点,精准地解决了上述痛点。

SimpleNet的流程可以概括为:预训练特征提取 → 特征适配 → 特征空间噪声合成 → 训练简单判别器。
Overview of the proposed SimpleNet

4.1 特征提取器:

SimpleNet 使用的特征提取器通常是 WideResNet-50 ,这是⼀个在 ImageNet 上预训练好的、能力强大的骨干网络。SimpleNet 首先利用其提取图像的中间层特征。相比直接在原始图像上判断异常,深层特征可以提供更加稳定的语义表达。

4.2 特征适配器

由于 ImageNet 预训练模型学习到的特征主要来自自然图像,而工业图像具有更强的纹理性和结构性,因此直接使用预训练特征可能存在域偏差。SimpleNet 引入特征适配器,对提取到的特征进行轻量调整,使其更适合当前工业异常检测任务。

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
class Projection(torch.nn.Module):

def __init__(self, in_planes, out_planes=None, n_layers=1, layer_type=0):
super(Projection, self).__init__()

if out_planes is None:
out_planes = in_planes
self.layers = torch.nn.Sequential()
_in = None
_out = None
for i in range(n_layers):
_in = in_planes if i == 0 else _out
_out = out_planes
self.layers.add_module(f"{i}fc",
torch.nn.Linear(_in, _out))
if i < n_layers - 1:
# if layer_type > 0:
# self.layers.add_module(f"{i}bn",
# torch.nn.BatchNorm1d(_out))
if layer_type > 1:
self.layers.add_module(f"{i}relu",
torch.nn.LeakyReLU(.2))
self.apply(init_weight)

def forward(self, x):

# x = .1 * self.layers(x) + x
x = self.layers(x)
return x

4.3 特征空间异常合成

SimpleNet 没有直接在图像上制造异常,而是在正常特征上添加高斯噪声,构造伪异常特征。这样做的好处是异常合成更加简单,同时避免了像素级异常合成不真实的问题。

4.4 判别器

模型使用一个简单的 MLP 判别器区分正常特征和合成异常特征。训练完成后,测试图像的异常分数可以由判别器输出得到,并进一步生成异常定位图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Discriminator(torch.nn.Module):
def __init__(self, in_planes, n_layers=1, hidden=None):
super(Discriminator, self).__init__()

_hidden = in_planes if hidden is None else hidden
self.body = torch.nn.Sequential()
for i in range(n_layers-1):
_in = in_planes if i == 0 else _hidden
_hidden = int(_hidden // 1.5) if hidden is None else hidden
self.body.add_module('block%d'%(i+1),
torch.nn.Sequential(
torch.nn.Linear(_in, _hidden),
torch.nn.BatchNorm1d(_hidden),
torch.nn.LeakyReLU(0.2)
))
self.tail = torch.nn.Linear(_hidden, 1, bias=False)
self.apply(init_weight)

def forward(self,x):
x = self.body(x)
x = self.tail(x)
return x

5. 实验结果

Comparison of SimpleNet with state-of-the-arts works on MVTec AD
复现结果

6. 思考

我认为 SimpleNet 的关键价值不在于结构复杂,而在于它重新思考了异常检测中的“异常样本从哪里来”这一问题。传统方法往往在图像层面制造异常,但真实工业缺陷的形态非常复杂,简单的像素扰动不一定能模拟真实缺陷。SimpleNet 将异常合成转移到特征空间中,使得模型不需要直接生成真实缺陷图像,而是学习正常特征附近的判别边界。这种设计既保留了预训练特征的表达能力,又通过适配器和判别器降低了方法复杂度,因此在性能和效率之间取得了较好的平衡。

优点

  1. 结构简单,训练和推理效率较高;
  2. 不依赖真实异常样本,适合工业异常检测场景;
  3. 特征适配器缓解了预训练模型和工业图像之间的域偏差;
  4. 特征空间异常合成比像素空间异常合成更加简洁。

不足

  1. 合成异常仍然与真实异常存在差距;
  2. 对预训练特征质量仍有一定依赖;
  3. 不同数据集和不同类别下,噪声强度等超参数可能需要调节。

改进

  1. 人工固定噪声 → 对抗可学习噪声

问题:SimpleNet 生成假瑕疵时直接往正常特征里加高斯噪声,依赖你手动去调那个噪声大小,真实的工业瑕疵可能不是简单的高斯分布。
改进:设计一个轻量级的噪声生成器,让这个生成器不断去生成最能骗过判别器的复杂瑕疵特征。这样不仅免去了手动调参的痛苦,还能自适应地生成比高斯噪声更真实的边缘和纹理缺陷。

  1. 一类一模型 → 统一多类别模型

问题:现在的代码测试 MVTec AD 的 15 个类别,就要把模型初始化 15 次,训练 15 遍,保存 15 个权重文件,如果类别过多,无法落地。
改进:尝试只训练一个 SimpleNet 模型。你可以把零件的类别信息当作一个提示向量输入给网络。当图片进来时,网络一看提示是纽扣,就自动切换到纽扣检测模式。

7. 总结

SimpleNet 给我的启发是:模型设计并不一定越复杂越好。对于工业异常检测任务,关键在于如何有效利用正常样本、如何构造合理的异常边界,以及如何在精度和效率之间取得平衡。

这篇论文通过特征适配、特征空间异常合成和轻量判别器,将异常检测问题转化为一个更加直接的特征判别问题。对于我后续学习图像异常检测、模型复现以及理解无监督视觉任务都有较大帮助。


Author: Junkai Li
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Junkai Li !
  TOC