一、前言

本项目为百度论文复现赛《Self-Supervised Predictive Convolutional Attentive Block for Anomaly Detection》论文复现代码。

依赖环境:

  • paddlepaddle-gpu2.3.1
  • python3.7

结合CutPaste方法在MVTec-AD数据集3-way detection AUROC:

modelCutPaste (3-way)SSPCABSSPCAB(复现)
average95.296.196.4

二、模型背景及其介绍

在成功的异常检测方法中,有一类方法依赖于对被mask掉信息的预测并利用与被mask信息相关的重建误差作为异常分数。与相关方法不同,文章提出将基于重建的功能集成到一个新的自监督的预测体系结构模块中。作者主要是通过一个带有扩张卷积的卷积层进行卷积,然后将结果通过通道注意力模块。提出的自监督块是通用的,可以很容易地纳入各种最新的异常检测方法。

1、SSPCAB架构

在这里插入图片描述

在本文中使用masked卷积核和channel注意力机制,执行一个重构masked信息的自监督任务用于训练,原始特征经过该block达到增强特征的效果。

2、提出的卷积方法接收域

在这里插入图片描述

该部分的主要思想是对于一个选定的感知野范围,部分像素点进行卷积操作,忽略掉剩下的像素空间,四部分进行卷积之后再经过一系列的操作得到一个值,该值来代表该感知野的特征表示。

在本文中,作者提出的模块,旨在学习使用上下文信息预测(或重建)隐藏信息,将位置关系进行了建模,为了获得高精度的重建结果,作者的模块被迫学习局部模式的全局结构。从这个角度来说,作者认为这种能力可以使得SSPCAB适用于广泛的任务,但该模块在异常检测中具有自然和直接的适用性。当整合到一个基于正常训练数据训练的CNN中时,SSPCAB将只学习正常示例的全局结构,而当在推断时出现异常数据样本时,模块可能会提供较差的重建。

3、重构损失

设GGG表示SSPCAB函数,定义自监督重建损失为均方误差(MSE)之间的输入和输出,如下所示:
在这里插入图片描述

三、数据集

MVTec AD是MVtec公司提出的一个用于异常检测的数据集。与之前的异常检测数据集不同,该数据集模仿了工业实际生产场景,并且主要用于unsupervised anomaly detection。数据集为异常区域都提供了像素级标注,是一个全面的、包含多种物体、多种异常的数据集。数据集包含不同领域中的五种纹理以及十种物体,且训练集中只包含正常样本,测试集中包含正常样本与缺陷样本,因此需要使用无监督方法学习正常样本的特征表示,并用其检测缺陷样本。

数据集下载链接:AiStudio数据集

四、运行

1、解压预训练数据

# 解压数据集
%cd /home/aistudio/data/
!tar xvf data116034/mvtec_anomaly_detection.tar.xz
# 加载环境
%cd /home/aistudio/work/
!pip install -r requirement.txt

2、训练

# 全量数据训练
%cd /home/aistudio/work/Paddle-SSPCAB
!python3 train.py --batch_size 96 
# 少量数据训练
%cd /home/aistudio/work/Paddle-SSPCAB
!python3 train.py --data_dir lite_data --type bottle --epochs 10 --test_epochs 5 --batch_size 5 
/home/aistudio/work/Paddle-SSPCAB
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import MutableMapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Iterable, Mapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Sized
Namespace(batch_size=5, cuda=True, data_dir='lite_data', epochs=10, freeze_resnet=20, head_layer=1, logs_dir='logdirs', lr=0.03, model_dir='models', optim='sgd', output=None, pretrained=True, test_epochs=5, type='bottle', variant='3way', workers=0)
using device: cuda
training bottle
loading images
loaded 9 images
W0809 18:56:04.544744  6106 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0809 18:56:04.549911  6106 gpu_resources.cc:91] device: 0, cuDNN Version: 7.6.
  0%|                                                    | 0/10 [00:00<?, ?it/s]Type : bottle Train [ Epoch 1/10 ], loss: 1.3939, avg_reader_cost: 0.0626 avg_batch_cost: 1.4055, avg_ips: 3.5575.
 10%|████▍                                       | 1/10 [00:01<00:12,  1.41s/it]Type : bottle Train [ Epoch 2/10 ], loss: 0.9911, avg_reader_cost: 0.0002 avg_batch_cost: 0.0265, avg_ips: 188.5572.
Type : bottle Train [ Epoch 3/10 ], loss: 0.9325, avg_reader_cost: 0.0001 avg_batch_cost: 0.0276, avg_ips: 181.3361.
Type : bottle Train [ Epoch 4/10 ], loss: 0.8393, avg_reader_cost: 0.0014 avg_batch_cost: 0.0277, avg_ips: 180.6830.
Type : bottle Train [ Epoch 5/10 ], loss: 0.7415, avg_reader_cost: 0.0367 avg_batch_cost: 0.0647, avg_ips: 77.3366.
loading images
loaded 9 images
WARNING: Detect dataset only contains single fileds, return format changed since Paddle 2.1. In Paddle <= 2.0, DataLoader add a list surround output data(e.g. return [data]), and in Paddle >= 2.1, DataLoader return the single filed directly (e.g. return data). For example, in following code: 

import numpy as np
from paddle.io import DataLoader, Dataset

class RandomDataset(Dataset):
    def __getitem__(self, idx):
        data = np.random.random((2, 3)).astype('float32')

        return data

    def __len__(self):
        return 10

dataset = RandomDataset()
loader = DataLoader(dataset, batch_size=1)
data = next(loader())

In Paddle <= 2.0, data is in format '[Tensor(shape=(1, 2, 3), dtype=float32)]', and in Paddle >= 2.1, data is in format 'Tensor(shape=(1, 2, 3), dtype=float32)'

using density estimation GaussianDensityPaddle
Type : bottle Val [ Epoch 5/10 ] max_auc: 1.0000 roc_auc : 1.000000.
 50%|██████████████████████                      | 5/10 [00:02<00:05,  1.10s/it]Type : bottle Train [ Epoch 6/10 ], loss: 0.7367, avg_reader_cost: 1.3864 avg_batch_cost: 1.4223, avg_ips: 3.5155.
Type : bottle Train [ Epoch 7/10 ], loss: 1.2175, avg_reader_cost: 0.0002 avg_batch_cost: 0.0306, avg_ips: 163.2431.
Type : bottle Train [ Epoch 8/10 ], loss: 0.7875, avg_reader_cost: 0.0001 avg_batch_cost: 0.0289, avg_ips: 172.8242.
Type : bottle Train [ Epoch 9/10 ], loss: 0.6306, avg_reader_cost: 0.0001 avg_batch_cost: 0.0282, avg_ips: 177.4766.
 90%|███████████████████████████████████████▌    | 9/10 [00:03<00:00,  1.28it/s]Type : bottle Train [ Epoch 10/10 ], loss: 0.6191, avg_reader_cost: 0.0002 avg_batch_cost: 0.0298, avg_ips: 167.5898.
loading images
loaded 9 images
using density estimation GaussianDensityPaddle
Type : bottle Val [ Epoch 10/10 ] max_auc: 1.0000 roc_auc : 1.000000.
100%|███████████████████████████████████████████| 10/10 [00:04<00:00,  2.08it/s]
# 加载训练好的模型
%cd /home/aistudio/work/Paddle-SSPCAB
!unzip -oq /home/aistudio/data/data162384/models_sspcab.zip
/home/aistudio/work/Paddle-SSPCAB

3、评估

# 全量数据模型评估
%cd /home/aistudio/work/Paddle-SSPCAB
!python eval.py --type all --data_dir /home/aistudio/data/ --head_layer 8 --density paddle
# 少量数据模型评估
%cd /home/aistudio/work/Paddle-SSPCAB
!python3 eval.py --data_dir lite_data --type bottle
/home/aistudio/work/Paddle-SSPCAB
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import MutableMapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Iterable, Mapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Sized
Namespace(cuda=False, data_dir='lite_data', density='paddle', head_layer=8, model_dir='models', pretrained=None, save_plots=True, type='bottle')
evaluating bottle
loading model model-bottle
[512, 512, 512, 512, 512, 512, 512, 512, 128]
W0804 15:58:06.068346 15603 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0804 15:58:06.071348 15603 gpu_resources.cc:91] device: 0, cuDNN Version: 7.6.
loading images
loaded 9 images
WARNING: Detect dataset only contains single fileds, return format changed since Paddle 2.1. In Paddle <= 2.0, DataLoader add a list surround output data(e.g. return [data]), and in Paddle >= 2.1, DataLoader return the single filed directly (e.g. return data). For example, in following code: 

import numpy as np
from paddle.io import DataLoader, Dataset

class RandomDataset(Dataset):
    def __getitem__(self, idx):
        data = np.random.random((2, 3)).astype('float32')

        return data

    def __len__(self):
        return 10

dataset = RandomDataset()
loader = DataLoader(dataset, batch_size=1)
data = next(loader())

In Paddle <= 2.0, data is in format '[Tensor(shape=(1, 2, 3), dtype=float32)]', and in Paddle >= 2.1, data is in format 'Tensor(shape=(1, 2, 3), dtype=float32)'

[t-SNE] Computing 7 nearest neighbors...
[t-SNE] Indexed 8 samples in 0.000s...
[t-SNE] Computed neighbors for 8 samples in 0.001s...
[t-SNE] Computed conditional probabilities for sample 8 / 8
[t-SNE] Mean sigma: 1125899906842624.000000
[t-SNE] KL divergence after 250 iterations with early exaggeration: 60.180157
[t-SNE] KL divergence after 500 iterations: 0.205797
using density estimation GaussianDensityPaddle
bottle AUC: 1.0

4、预测

# 模型预测
%cd /home/aistudio/work/Paddle-SSPCAB
!python3 predict.py --data_type bottle --img_file images/good.png
# 基于推理引擎导出模型
%cd /home/aistudio/work/Paddle-SSPCAB
!python3 deploy/export_model.py
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/dygraph_to_static/convert_call_func.py:93: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  func_in_dict = func == v
inference model has been saved into deploy
# 基于推理引擎的模型预测
%cd /home/aistudio/work/Paddle-SSPCAB
!python3 deploy/infer.py --data_type bottle --img_path images/good.png
/home/aistudio/work/Paddle-SSPCAB
image_name: images/good.png, data is normal, score is 26.223722457885742, threshold is 51.2691650390625

5、自动化测试脚本

自动化测试为论文复现赛所需,可以自动验证项目的训练测试以及推理的正确性。

%cd /home/aistudio/work/Paddle-SSPCAB
!bash test_tipc/test_train_inference_python.sh test_tipc/configs/resnet18/train_infer_python.txt lite_train_lite_infer
/home/aistudio/work/Paddle-SSPCAB
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import MutableMapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Iterable, Mapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Sized
Namespace(batch_size=5, cuda=True, data_dir='lite_data', epochs=3, freeze_resnet=20, head_layer=1, logs_dir='logdirs', lr=0.03, model_dir='./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0', optim='sgd', output=None, pretrained=True, test_epochs=3, type='bottle', variant='3way', workers=0)
using device: cuda
training bottle
loading images
loaded 9 images
W0809 18:53:33.119930  5435 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0809 18:53:33.125115  5435 gpu_resources.cc:91] device: 0, cuDNN Version: 7.6.
  0%|                                                     | 0/3 [00:00<?, ?it/s]Type : bottle Train [ Epoch 1/3 ], loss: 1.2079, avg_reader_cost: 0.0634 avg_batch_cost: 1.3776, avg_ips: 3.6294.
 33%|███████████████                              | 1/3 [00:01<00:02,  1.38s/it]Type : bottle Train [ Epoch 2/3 ], loss: 1.1285, avg_reader_cost: 0.0002 avg_batch_cost: 0.0273, avg_ips: 183.4474.
Type : bottle Train [ Epoch 3/3 ], loss: 1.1417, avg_reader_cost: 0.0001 avg_batch_cost: 0.0284, avg_ips: 175.9341.
loading images
loaded 9 images
WARNING: Detect dataset only contains single fileds, return format changed since Paddle 2.1. In Paddle <= 2.0, DataLoader add a list surround output data(e.g. return [data]), and in Paddle >= 2.1, DataLoader return the single filed directly (e.g. return data). For example, in following code: 

import numpy as np
from paddle.io import DataLoader, Dataset

class RandomDataset(Dataset):
    def __getitem__(self, idx):
        data = np.random.random((2, 3)).astype('float32')

        return data

    def __len__(self):
        return 10

dataset = RandomDataset()
loader = DataLoader(dataset, batch_size=1)
data = next(loader())

In Paddle <= 2.0, data is in format '[Tensor(shape=(1, 2, 3), dtype=float32)]', and in Paddle >= 2.1, data is in format 'Tensor(shape=(1, 2, 3), dtype=float32)'

using density estimation GaussianDensityPaddle
Type : bottle Val [ Epoch 3/3 ] max_auc: 1.0000 roc_auc : 1.000000.
100%|█████████████████████████████████████████████| 3/3 [00:03<00:00,  1.22s/it]
[33m Run successfully with command - python train.py --test_epochs=3 --data_dir=lite_data --type=bottle --model_dir=test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0 --model_dir=./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0 --epochs=3   --batch_size=5!  [0m
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import MutableMapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Iterable, Mapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Sized
Namespace(cuda=False, data_dir='lite_data', density='paddle', head_layer=8, model_dir='test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0', pretrained='./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0/', save_plots=True, type='bottle')
evaluating bottle
loading model model-bottle
[512, 512, 512, 512, 512, 512, 512, 512, 128]
W0809 18:53:42.529196  5524 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0809 18:53:42.532593  5524 gpu_resources.cc:91] device: 0, cuDNN Version: 7.6.
loading images
loaded 9 images
WARNING: Detect dataset only contains single fileds, return format changed since Paddle 2.1. In Paddle <= 2.0, DataLoader add a list surround output data(e.g. return [data]), and in Paddle >= 2.1, DataLoader return the single filed directly (e.g. return data). For example, in following code: 

import numpy as np
from paddle.io import DataLoader, Dataset

class RandomDataset(Dataset):
    def __getitem__(self, idx):
        data = np.random.random((2, 3)).astype('float32')

        return data

    def __len__(self):
        return 10

dataset = RandomDataset()
loader = DataLoader(dataset, batch_size=1)
data = next(loader())

In Paddle <= 2.0, data is in format '[Tensor(shape=(1, 2, 3), dtype=float32)]', and in Paddle >= 2.1, data is in format 'Tensor(shape=(1, 2, 3), dtype=float32)'

[t-SNE] Computing 7 nearest neighbors...
[t-SNE] Indexed 8 samples in 0.000s...
[t-SNE] Computed neighbors for 8 samples in 0.001s...
[t-SNE] Computed conditional probabilities for sample 8 / 8
[t-SNE] Mean sigma: 1125899906842624.000000
[t-SNE] KL divergence after 250 iterations with early exaggeration: 42.278301
[t-SNE] KL divergence after 500 iterations: 0.198680
using density estimation GaussianDensityPaddle
bottle AUC: 1.0
[33m Run successfully with command - python eval.py --data_dir=lite_data --type=bottle --model_dir=test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0 --pretrained=./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0/!  [0m
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/dygraph_to_static/convert_call_func.py:93: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  func_in_dict = func == v
inference model has been saved into ./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0
[33m Run successfully with command - python deploy/export_model.py --model_path=test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0 --data_type=bottle --pretrained=./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0/ --save_inference_dir=./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0!  [0m
image_name: images/good.png, data is normal, score is 29.14488983154297, threshold is 37.34718704223633
[33m Run successfully with command - python deploy/infer.py --use_gpu=True --model_dir=./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0 --batch_size=1   --data_type=bottle > ./test_tipc/output/resnet18/lite_train_lite_infer/python_infer_gpu_batchsize_1.log 2>&1 !  [0m
image_name: images/good.png, data is normal, score is 29.144872665405273, threshold is 37.34718704223633
[33m Run successfully with command - python deploy/infer.py --use_gpu=False --model_dir=./test_tipc/output/resnet18/lite_train_lite_infer/norm_train_gpus_0 --batch_size=1   --data_type=bottle > ./test_tipc/output/resnet18/lite_train_lite_infer/python_infer_cpu_batchsize_1.log 2>&1 !  [0m

五、代码结构

   |--images                        # 测试使用的样例图片,两张
    |--deploy                        # 预测部署相关
        |--export_model.py    # 导出模型
        |--infer.py                   # 部署预测
    |--data                            # 训练和测试数据集
    |--lite_data                     # 自建立的小数据集,含有bottle
    |--logdirs                        # 训练train和测试eval打印的日志信息  
    |--eval                            # eval输出文
    |--models                       # 训练的模型权值
    |--test_tipc                     # tipc代码
    |--tools                           # 工具类文件
        |--cutpaste.py            # 论文代码
        |--dataset.py              # 数据加载
        |--density.py               # 高斯聚类代码
        |--model.py                # 论文模型
    |--predict.py                   # 预测代码
    |--eval.py                        # 评估代码
    |--train.py                       # 训练代码
    |----README.md            # 用户手册

六、复现总结

深度学习的快速发展使其在缺陷检测领域得到越来越广泛的应用。目前这个方向主要通过无监督的技术去实现异常检测,不需要过多时间的训练。在现实中,基于深度学习的表面缺陷检测方法往往不能直接用于工业产品的表面缺陷检测任务。主要原因之一是现代工业流程的不断优化导致缺陷样本越来越少,即缺陷图像的数量非常有限,未来我们可以加大从少量样本中展开研究,运用小样本学习方向的技术。

声明

此项目为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐