PaDiM 无监督异常检测和定位-论文和源码阅读

目录

1. 论文

1.1 检测效果

1.2 框架 

1.2.1 特征提取embedding extraction

1.2.2 正样本学习Learning of the normality

1.2.3 计算异常图 inference: computation of the anomaly map

2. 源码

2.1 dataset

2.2 model

2.3 提取特征

2.4 infer


1. 论文

https://arxiv.org/abs/2011.08785

思路:数据特征的分布被假定为一个多元高斯分布,异常值通常在多元高斯分布中表现为远离数据集的中心(均值向量)的数据点。协方差矩阵可以描述各个特征之间的相关性和离散程度。通过计算数据点相对于协方差矩阵的马氏距离,可以识别潜在的异常点。 

1.1 检测效果

 

SPADE精度不错,但是推理会非常慢,因为算法是基于KNN,测试的时候要访问整个训练正常数据集的embedding vectors。

PaDiM backbone是resnet18,然后随机选100个特征,和backbone是Wide ResNet-50-2 (WR50) 然后随机选550个特征,可以看到后者的精度更高。

1.2 框架 

步骤如下: 

1.2.1 特征提取embedding extraction

将与此图像块相对应的三个激活图区域特征concat在一起,组成一个特征向量(这样向量就包含了多尺度信息)。N张图,就有N个特征向量。组成X_ij特征空间,该过程不需要训练,直接用预训练权重比如resnet18、resnet50;

(1) infer

layer1: batch infer -> (b,256,56,56) ->cat->(img_num,256,56,56)

layer2: batch infer -> (b,512,28,28) ->cat->(img_num,512,28,28)

layer3: batch infer -> (b,1024,14,14) ->cat->(img_num,1024,14,14)

全部cat在一起, 得到embedding_vectors (img_num,1792,56,56).

(2) 随机选择d个特征

embedding_vectors有56*56个特征块,每个块有1792长度的特征,随机选d(550)个特征。

embedding_vectors.shape -> (img_num,550,56,56).

1.2.2 正样本学习Learning of the normality

为了求整个正样本数据分布情况,作者假设数据是符合多元高斯分布,然后求取此分布的参数: 均值mean和协方差covariance。

(1)均值:特征均值是某一特征(或变量)在数据集中的平均值。它是所有样本中某一特征的数值之和除以样本数量。特征均值用来表示数据在这一特征上的中心趋势。

B, C, H, W = embedding_vectors.size()
embedding_vectors = embedding_vectors.view(B, C, H * W)  # (img_num,550,56,56) -> (img_num,550,3136). 3136块
mean = torch.mean(embedding_vectors, dim=0).numpy()  # (550,3136). 整个数据集的均值

(2)协方差:协方差反映了多个变量之间的相关性以及它们与各自均值的偏离程度,如果一个变量的值在均值附近波动较大(方差大),那么它对协方差的贡献也会较大。

(3)协方差矩阵:有p个特征,协方差矩阵是一个p x p的矩阵,其中每个元素(i, j)表示特征i和特征j之间的协方差。协方差矩阵通常用Σ来表示。 协方差矩阵的对角线上的元素是各个特征的方差(方差是协方差自己和自己的情况),而非对角线上的元素是不同特征之间的协方差。协方差矩阵计算公式如下。

x_{ij}^k是第k个图像的图像块ij的特征向量(向量长度是550,所以得到的协方差矩阵大小是(550,550)), u_{ij}是整个正样本数据集的图像块ij位置上的特征均值向量()。\epsilon I是正常项。N是数据集中正常图像个数。每个块都有自己的协方差矩阵。

1.2.3 计算异常图 inference: computation of the anomaly map

基于前面求得的正常样本均值mean和协方差矩阵covariance的逆,再求测试图像块特征的马氏距离,此距离作为每个块的异常分数。

其中x_{ij}是测试图像块的patch embedding. μ和∑是前面正常样本求得的均值和协方差。T是转置,-1是求逆。

2. 源码

https://github.com/xiahaifeng1995/PaDiM-Anomaly-Detection-Localization-master

2.1 dataset

没有数据增强,只有个中心裁剪,因为作者提出的方法没有训练过程,直接使用预训练模型,提取激活层的特征向量。

import os
# import tarfile
from PIL import Image
from tqdm import tqdm
# import urllib.requestimport torch
from torch.utils.data import Dataset
from torchvision import transforms as T# URL = 'ftp://guest:GU.205dldo@ftp.softronics.ch/mvtec_anomaly_detection/mvtec_anomaly_detection.tar.xz'
CLASS_NAMES = ['bottle', 'cable', 'capsule', 'carpet', 'grid','hazelnut', 'leather', 'metal_nut', 'pill', 'screw','tile', 'toothbrush', 'transistor', 'wood', 'zipper']# CLASS_NAMES = ['len_base']class MVTecDataset(Dataset):def __init__(self, dataset_path='D:/dataset/mvtec_anomaly_detection', class_name='bottle', is_train=True,resize=256, cropsize=224):assert class_name in CLASS_NAMES, 'class_name: {}, should be in {}'.format(class_name, CLASS_NAMES)self.dataset_path = dataset_pathself.class_name = class_name  # 'bottle', 'cable' or 'capsule' et al.self.is_train = is_train  # bool.self.resize = resize  # 256self.cropsize = cropsize  # 224  CenterCrop# load dataset: x:整个数据集的图片路径,对应的label(0/1),对应的缺陷mask图像路径(good类别使用None)。self.x, self.y, self.mask = self.load_dataset_folder() # set transformsself.transform_x = T.Compose([T.Resize(resize, Image.ANTIALIAS),T.CenterCrop(cropsize),T.ToTensor(),T.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])self.transform_mask = T.Compose([T.Resize(resize, Image.NEAREST),  # 注意maskT.CenterCrop(cropsize),T.ToTensor()])def __getitem__(self, idx):x, y, mask = self.x[idx], self.y[idx], self.mask[idx]  # 获取路径x = Image.open(x).convert('RGB')x = self.transform_x(x)if y == 0:  # good图像的mask路径是None,所以这里生成全0缺陷maskmask = torch.zeros([1, self.cropsize, self.cropsize]) else:mask = Image.open(mask)mask = self.transform_mask(mask)return x, y, maskdef __len__(self):return len(self.x)def load_dataset_folder(self):phase = 'train' if self.is_train else 'test'x, y, mask = [], [], []  # x:整个数据集的图片路径,对应的label(0/1),对应的缺陷mask图像路径(good类别使用None)。img_dir = os.path.join(self.dataset_path, self.class_name, phase)gt_dir = os.path.join(self.dataset_path, self.class_name, 'ground_truth')img_types = sorted(os.listdir(img_dir))  # train文件夹下只有good文件夹for img_type in img_types:  # 遍历不同的文件夹,比如train目录下只有good文件夹。test目录下有各种缺陷文件夹。# load imagesimg_type_dir = os.path.join(img_dir, img_type)if not os.path.isdir(img_type_dir):continueimg_fpath_list = sorted([os.path.join(img_type_dir, f)for f in os.listdir(img_type_dir)if f.endswith('.png')])x.extend(img_fpath_list)# load gt labels。二分类问题:good or not. if img_type == 'good':y.extend([0] * len(img_fpath_list))  # 如果是good类别,则当前所有图像label都0mask.extend([None] * len(img_fpath_list))  # good类别没有缺陷maskelse:y.extend([1] * len(img_fpath_list))  # 不是good,则当前所有图像label都是1gt_type_dir = os.path.join(gt_dir, img_type)img_fname_list = [os.path.splitext(os.path.basename(f))[0] for f in img_fpath_list]gt_fpath_list = [os.path.join(gt_type_dir, img_fname + '_mask.png')for img_fname in img_fname_list]mask.extend(gt_fpath_list)  # 并保存对应的mask图像路径assert len(x) == len(y), 'number of x and y should be same'return list(x), list(y), list(mask)

2.2 model

from torchvision.models import wide_resnet50_2, resnet18def main():args = parse_args()# 1, load modelif args.arch == 'resnet18':model = resnet18(pretrained=True, progress=True)t_d = 448d = 100elif args.arch == 'wide_resnet50_2':model = wide_resnet50_2(pretrained=True, progress=True)t_d = 1792  #d = 550model.to(device)model.eval()# 这一行将Python内置的random模块的随机种子设置为1024。设置种子可以确保在相同种子的情况下,使用该模块进行的任何随机操作都会生成相同的随机数序列。通常用于实现可重现性。random.seed(1024)# 这一行将PyTorch的随机数生成器的种子设置为1024。这用于确保在PyTorch内进行的任何随机操作(例如神经网络权重的初始化)在相同种子下生成相同的结果。torch.manual_seed(1024)if use_cuda:# 这一行设置PyTorch的CUDA(GPU)随机数生成器的种子。这将确保在GPU上进行的随机操作在使用种子1024时也能生成可重现的结果。torch.cuda.manual_seed_all(1024)idx = torch.tensor(sample(range(0, t_d), d))  # 在范围[0,1792]范围内随机生成550个整数。# set model's intermediate outputs。 只使用中间3个layer输出,第四个layer输出没有使用outputs = []# 提取layer1,layer2,layer3层输出结果。def hook(module, input, output):outputs.append(output)model.layer1[-1].register_forward_hook(hook)  # 每次前向都会调用一次hook,然后output会保持到outputs中。model.layer2[-1].register_forward_hook(hook)model.layer3[-1].register_forward_hook(hook)

2.3 提取特征

没有训练,则不需要定义loss函数、optimizer等等。直接dataset取数据,infer模型推理,获取中间的三个layer输出结果,然后提取特征向量的均值和协方差。

import random
from random import sample
import argparse
import numpy as np
import os
import pickle
from tqdm import tqdm
from collections import OrderedDictimport matplotlib.pyplot as plt
import torchfrom torch.utils.data import DataLoader
from torchvision.models import wide_resnet50_2, resnet18
import datasets.mvtec as mvtec# device setup
from utils import embedding_concatuse_cuda = torch.cuda.is_available()
device = torch.device('cuda' if use_cuda else 'cpu')def parse_args():parser = argparse.ArgumentParser('PaDiM')parser.add_argument('--data_path', type=str, default='D:/dataset/mvtec_anomaly_detection')parser.add_argument('--save_path', type=str, default='./mvtec_result')parser.add_argument('--arch', type=str, choices=['resnet18', 'wide_resnet50_2'], default='wide_resnet50_2')return parser.parse_args()def main():args = parse_args()# 1, load modelif args.arch == 'resnet18':model = resnet18(pretrained=True, progress=True)t_d = 448d = 100elif args.arch == 'wide_resnet50_2':model = wide_resnet50_2(pretrained=True, progress=True)t_d = 1792  # 三个feature通道数的和256+512+1024. 在范围[0,1792]范围内随机生成550个整数。d = 550  # 随机选择550个值model.to(device)  # to cudamodel.eval()# 这一行将Python内置的random模块的随机种子设置为1024。设置种子可以确保在相同种子的情况下,使用该模块进行的任何随机操作都会生成相同的随机数序列。通常用于实现可重现性。random.seed(1024)# 这一行将PyTorch的随机数生成器的种子设置为1024。这用于确保在PyTorch内进行的任何随机操作(例如神经网络权重的初始化)在相同种子下生成相同的结果。torch.manual_seed(1024)if use_cuda:# 这一行设置PyTorch的CUDA(GPU)随机数生成器的种子。这将确保在GPU上进行的随机操作在使用种子1024时也能生成可重现的结果。torch.cuda.manual_seed_all(1024)idx = torch.tensor(sample(range(0, t_d), d))  # 在范围[0,1792]范围内随机生成550个整数。# set model's intermediate outputs。 只使用中间3个layer输出,第四个layer输出没有使用outputs = []# 提取layer1,layer2,layer3层输出结果。def hook(module, input, output):outputs.append(output)model.layer1[-1].register_forward_hook(hook)  # 每次前向都会调用一次hook,然后output会保持到outputs中。model.layer2[-1].register_forward_hook(hook)model.layer3[-1].register_forward_hook(hook)# 创建特征pickle文件保存路径,其实就是存放[mean, cov],每个数据集都有一个pickle文件。os.makedirs(os.path.join(args.save_path, 'temp_%s' % args.arch), exist_ok=True)fig, ax = plt.subplots(1, 2, figsize=(20, 10))  # 一行两列figfor class_name in mvtec.CLASS_NAMES:  # 遍历不同的数据集# 2, datasettrain_dataset = mvtec.MVTecDataset(args.data_path, class_name=class_name, is_train=True)train_dataloader = DataLoader(train_dataset, batch_size=32, pin_memory=True)# layer1:所有数据集layer1输出向量,每个向量的shape是[b,256,56,56]# layer2: 所有数据集layer2输出向量,每个向量的shape是[b,512,28,28]# layer3: 所有数据集layer3输出向量,每个向量的shape是[b,1024,14,14]train_outputs = OrderedDict([('layer1', []), ('layer2', []), ('layer3', [])])# 3, extract train set features (pickle文件保存)train_feature_filepath = os.path.join(args.save_path, 'temp_%s' % args.arch, 'train_%s.pkl' % class_name)if not os.path.exists(train_feature_filepath):  # 不存在此数据集的特征pickle文件,则进行特征提取for (x, _, _) in tqdm(train_dataloader, '| feature extraction | train | %s |' % class_name):# model predictionwith torch.no_grad():  # 没有训练,x(b,32,224,224) to cuda,然后直接 batch infer_ = model(x.to(device))  # 没有使用返回结果,因为前面定义了hook保存了中间结果到outputs# get intermediate layer outputsfor k, v in zip(train_outputs.keys(), outputs):  # 保存3个cuda tensor到字典中train_outputs[k].append(v.cpu().detach())# initialize hook outputsoutputs = []  # 置空,迭代赋值for k, v in train_outputs.items():train_outputs[k] = torch.cat(v, 0)  # list of tensor(b,256,56,56). -> (img_num,256,56,56)# Embedding concatembedding_vectors = train_outputs['layer1']for layer_name in ['layer2', 'layer3']:embedding_vectors = embedding_concat(embedding_vectors, train_outputs[layer_name])# randomly select d dimensionembedding_vectors = torch.index_select(embedding_vectors, 1, idx)  # 在范围[0,1792]范围内随机生成550个整数。# calculate multivariate Gaussian distributionB, C, H, W = embedding_vectors.size()embedding_vectors = embedding_vectors.view(B, C, H * W)  # (img_num,550,56,56) -> (img_num,550,3136). 3136块mean = torch.mean(embedding_vectors, dim=0).numpy()  # (550,3136). 整个数据集的均值cov = torch.zeros(C, C, H * W).numpy()  # (550,550,3136)协方差矩阵I = np.identity(n=C)  # (550,550).单位矩阵for i in range(H * W):  # 依次求每个块的协方差矩阵(550,550)。每个块都有自己的协方差矩阵。# cov[:, :, i] = LedoitWolf().fit(embedding_vectors[:, :, i].numpy()).covariance_# np.cov: covariance matrix# rowvar=False: each column represents a variable(特征、变量), while the rows contain observations(观察,样本).cov[:, :, i] = np.cov(embedding_vectors[:, :, i].numpy(), rowvar=False) + 0.01 * I# save learned distributiontrain_outputs = [mean, cov]  # list of numpy. mean.shape=(550,3136). cov.shape=(550,550,3136)with open(train_feature_filepath, 'wb') as f:print("start dump feature: %s --------" % class_name)pickle.dump(train_outputs, f)print("finish dump-----------")else:  # 已存在,则直接载入。print('The file already exists: %s' % train_feature_filepath)if __name__ == '__main__':main()

2.4 infer

import random
from random import sample
import argparse
import numpy as np
import os
import pickle
from tqdm import tqdm
from collections import OrderedDict
from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve
from sklearn.metrics import precision_recall_curve
from scipy.spatial.distance import mahalanobis
from scipy.ndimage import gaussian_filter
import matplotlib.pyplot as pltimport torch
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision.models import wide_resnet50_2, resnet18
import datasets.mvtec as mvtec# device setup
from utils import embedding_concat, plot_figuse_cuda = torch.cuda.is_available()
device = torch.device('cuda' if use_cuda else 'cpu')def parse_args():parser = argparse.ArgumentParser('PaDiM')parser.add_argument('--data_path', type=str, default='D:/dataset/mvtec_anomaly_detection')parser.add_argument('--save_path', type=str, default='./mvtec_result')parser.add_argument('--arch', type=str, choices=['resnet18', 'wide_resnet50_2'], default='wide_resnet50_2')return parser.parse_args()def main():args = parse_args()# 1, load modelif args.arch == 'resnet18':model = resnet18(pretrained=True, progress=True)t_d = 448d = 100elif args.arch == 'wide_resnet50_2':model = wide_resnet50_2(pretrained=True, progress=True)t_d = 1792  # 三个feature通道数的和256+512+1024. 在范围[0,1792]范围内随机生成550个整数。d = 550  # 随机选择550个值model.to(device)  # to cudamodel.eval()# 这一行将Python内置的random模块的随机种子设置为1024。设置种子可以确保在相同种子的情况下,使用该模块进行的任何随机操作都会生成相同的随机数序列。通常用于实现可重现性。random.seed(1024)# 这一行将PyTorch的随机数生成器的种子设置为1024。这用于确保在PyTorch内进行的任何随机操作(例如神经网络权重的初始化)在相同种子下生成相同的结果。torch.manual_seed(1024)if use_cuda:# 这一行设置PyTorch的CUDA(GPU)随机数生成器的种子。这将确保在GPU上进行的随机操作在使用种子1024时也能生成可重现的结果。torch.cuda.manual_seed_all(1024)idx = torch.tensor(sample(range(0, t_d), d))  # 在范围[0,1792]范围内随机生成550个整数。# set model's intermediate outputs。 只使用中间3个layer输出,第四个layer输出没有使用outputs = []# 提取layer1,layer2,layer3层输出结果。def hook(module, input, output):outputs.append(output)model.layer1[-1].register_forward_hook(hook)  # 每次前向都会调用一次hook,然后output会保持到outputs中。model.layer2[-1].register_forward_hook(hook)model.layer3[-1].register_forward_hook(hook)# 创建特征pickle文件保存路径,其实就是存放[mean, cov],每个数据集都有一个pickle文件。os.makedirs(os.path.join(args.save_path, 'temp_%s' % args.arch), exist_ok=True)fig, ax = plt.subplots(1, 2, figsize=(20, 10))  # 一行两列figfig_img_rocauc = ax[0]  # 第0列figfig_pixel_rocauc = ax[1]  # 第1列figtotal_roc_auc = []  # test auc list. 不同class的auctotal_pixel_roc_auc = []  #for class_name in mvtec.CLASS_NAMES:  # 遍历不同的数据集train_feature_filepath = os.path.join(args.save_path, 'temp_%s' % args.arch, 'train_%s.pkl' % class_name)with open(train_feature_filepath, 'rb') as f:train_outputs = pickle.load(f)# 2, datasettest_dataset = mvtec.MVTecDataset(args.data_path, class_name=class_name, is_train=False)test_dataloader = DataLoader(test_dataset, batch_size=32, pin_memory=True)# layer1:所有数据集layer1输出向量,每个向量的shape是[b,256,56,56]# layer2: 所有数据集layer2输出向量,每个向量的shape是[b,512,28,28]# layer3: 所有数据集layer3输出向量,每个向量的shape是[b,1024,14,14]test_outputs = OrderedDict([('layer1', []), ('layer2', []), ('layer3', [])])gt_list = []gt_mask_list = []test_imgs = []# 4, extract test set featuresfor (x, y, mask) in tqdm(test_dataloader, '| feature extraction | test | %s |' % class_name):test_imgs.extend(x.cpu().detach().numpy())gt_list.extend(y.cpu().detach().numpy())gt_mask_list.extend(mask.cpu().detach().numpy())# model predictionwith torch.no_grad():_ = model(x.to(device))  # x to cuda. x.shape=(32,3,224,224)# get intermediate layer outputsfor k, v in zip(test_outputs.keys(), outputs):test_outputs[k].append(v.cpu().detach())# initialize hook outputsoutputs = []for k, v in test_outputs.items():test_outputs[k] = torch.cat(v, 0)# Embedding concat. 将不同层的输出cat在一起# layer1(img_num,256,56,56),layer2(img_num,512,28,28),layer3(img_num,1024,14,14)embedding_vectors = test_outputs['layer1']for layer_name in ['layer2', 'layer3']:embedding_vectors = embedding_concat(embedding_vectors, test_outputs[layer_name])# randomly select d dimension. embedding_vectors.shape=torch.Size([img_num, 1792, 56, 56])# 注意idx,要和前面train一样。embedding_vectors = torch.index_select(embedding_vectors, 1, idx)  # (img_num,1792,56,56)->(img_num,550,56,56)# 5. calculate distance matrixB, C, H, W = embedding_vectors.size()embedding_vectors = embedding_vectors.view(B, C, H * W).numpy()  # (img_num,550,56,56)->(img_num,550,56*56)dist_list = []for i in range(H * W):  # 遍历每个patchmean = train_outputs[0][:, i]  # train_outputs=[mean(550,3136), cov(550,550,3136)]. 第i块均值conv_inv = np.linalg.inv(train_outputs[1][:, :, i])  # 第i块协方差矩阵的逆矩阵。(550,550)# embedding_vectors.shape(img_num,550,3136). sample.shape(550,3136)# 特征块 sample[:, i].shape(550). 均值mean.shape(550). 协方差矩阵的逆矩阵conv_inv(550,550)# dist. list of dis. len=(img_num). 每张图像的每个patch有一个距离值. scipy.spatial.distancedist = [mahalanobis(sample[:, i], mean, conv_inv) for sample in embedding_vectors]  # 遍历测试图像,马氏距离dist_list.append(dist)  # {0: list(img_num), 1: list(img_num), patch_idx: list(img_num)}dist_list = np.array(dist_list).transpose(1, 0).reshape(B, H, W)  # (patch_num,image_num)->(83,3136)->(83,56,56)# 6. upsample distance map.dist_list = torch.tensor(dist_list)score_map = F.interpolate(dist_list.unsqueeze(1), size=x.size(2), mode='bilinear',align_corners=False).squeeze().numpy()  # (83,56,56)->(83,224,224)# 7. apply gaussian smoothing on the score map. 将patch score平滑成image score.for i in range(score_map.shape[0]):  # each test image. scipy.ndimagescore_map[i] = gaussian_filter(score_map[i], sigma=4)  # (224,224)# Normalization. val->(0,1)max_score = score_map.max()min_score = score_map.min()scores = (score_map - min_score) / (max_score - min_score)  # (img_num,224,224)# 8. calculate image-level ROC AUC score. 图像级别的异常分数img_scores = scores.reshape(scores.shape[0], -1).max(axis=1)  # (img_num,224,224) -> (img_num,224*224) ->(img_num)gt_list = np.asarray(gt_list)  # (img_num,)fpr, tpr, _ = roc_curve(gt_list, img_scores)  # sklearn.metrics. shape (img_num)img_roc_auc = roc_auc_score(gt_list, img_scores)  # auc.total_roc_auc.append(img_roc_auc)  # auc listprint('image ROCAUC: %.3f' % (img_roc_auc))  # 当前数据集类别(bottle)的aucfig_img_rocauc.plot(fpr, tpr, label='%s img_ROCAUC: %.3f' % (class_name, img_roc_auc))# get optimal threshold:# 使用最大F值对应的那个阈值(同时考虑召回率和准确率)gt_mask = np.asarray(gt_mask_list)  # list of tensor. -> (img_num,1,224,224). scores.shape(img_num,224,224)# thresholds不同的阈值,返回不同的准确率和召回率precision, recall, thresholds = precision_recall_curve(gt_mask.flatten(), scores.flatten())  # sklearn.metricsa = 2 * precision * recall  # 不同的阈值下,求F值b = precision + recall  # a/bf1 = np.divide(a, b, out=np.zeros_like(a), where=b != 0)  # 防止除0操作threshold = thresholds[np.argmax(f1)]  # 使用最大F值对应的那个阈值。# calculate per-pixel level ROCAUC. (img_num,1,224,224). scores.shape(img_num,224,224)fpr, tpr, _ = roc_curve(gt_mask.flatten(), scores.flatten())per_pixel_rocauc = roc_auc_score(gt_mask.flatten(), scores.flatten())total_pixel_roc_auc.append(per_pixel_rocauc)print('pixel ROCAUC: %.3f' % (per_pixel_rocauc))fig_pixel_rocauc.plot(fpr, tpr, label='%s ROCAUC: %.3f' % (class_name, per_pixel_rocauc))save_dir = args.save_path + '/' + f'pictures_{args.arch}'os.makedirs(save_dir, exist_ok=True)# 原图list (3,224,224).plot_fig(test_imgs, scores, gt_mask_list, threshold, save_dir, class_name)print('Average ROCAUC: %.3f' % np.mean(total_roc_auc))fig_img_rocauc.title.set_text('Average image ROCAUC: %.3f' % np.mean(total_roc_auc))fig_img_rocauc.legend(loc="lower right")print('Average pixel ROCUAC: %.3f' % np.mean(total_pixel_roc_auc))fig_pixel_rocauc.title.set_text('Average pixel ROCAUC: %.3f' % np.mean(total_pixel_roc_auc))fig_pixel_rocauc.legend(loc="lower right")fig.tight_layout()fig.savefig(os.path.join(args.save_path, 'roc_curve.png'), dpi=100)if __name__ == '__main__':main()

参考:GitHub - xiahaifeng1995/PaDiM-Anomaly-Detection-Localization-master: This is an unofficial implementation of the paper “PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization”.This is an unofficial implementation of the paper “PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization”. - GitHub - xiahaifeng1995/PaDiM-Anomaly-Detection-Localization-master: This is an unofficial implementation of the paper “PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization”.icon-default.png?t=N7T8https://github.com/xiahaifeng1995/PaDiM-Anomaly-Detection-Localization-master

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/127652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图扑 HT for Web 手机端运维管理系统

随着信息技术的快速发展,网络技术的应用涉及到人们生活的方方面面。其中,手机运维管理系统可提供数字化、智能化的方式,帮助企业和组织管理监控企业的 IT 环境,提高运维效率、降低维护成本、增强安全性、提升服务质量,…

分享一下微信小程序里怎么开店

如何在微信小程序中成功开店:从选品到运营的全方位指南 一、引言 随着微信小程序的日益普及,越来越多的人开始尝试在微信小程序中开设自己的店铺。微信小程序具有便捷、易用、即用即走等特点,使得开店门槛大大降低。本文将详细介绍如何在微…

【计算系统】5分钟了解超算,高性能计算,并行计算,分布式计算,网格计算,集群计算以及云计算的区别

5分钟了解超算,高性能计算,并行计算,分布式计算,网格计算,集群计算以及云计算的区别 1. 超算2. 高性能计算3. 并行计算4. 分布式计算5. 网格计算6. 集群计算7. 云计算小结相关资料 1. 超算 超级计算机(Sup…

分享88个工作总结PPT,总有一款适合您

分享88个工作总结PPT,总有一款适合您 88个工作总结PPT下载链接:https://pan.baidu.com/s/1y08X9RMdIOCncbs28aMgDw?pwd8888 提取码:8888 Python采集代码下载链接:采集代码.zip - 蓝奏云 蓝色水彩风年终总结PPT模板 清新水彩简…

深度学习之基于YoloV5火灾烟雾报警系统(GUI界面)

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、火灾烟雾报警系统四. 总结 一项目简介 YoloV5 是深度学习中用于目标检测的一种算法,可以对输入的图像进行识别,标识出…

windows server 2016-IIS静态服务器-设置详细过程

文章目录 1.打开仪表盘新建角色2.iis功能模块3.启动服务器4.优点 1.打开仪表盘新建角色 2.iis功能模块 能选上的尽量选上,除非知道自己用不上。 然后确认,下一步,安装。 3.启动服务器 搜索IIS,启动IIS管理器。 启动网站。 右…

HHDBCS扩展数据库类型

为应对市面上的数据库种类繁多的问题,HHDBCS设置了扩展数据库功能。 在登陆界面点击“工具”,选择“扩展数据库类型”; 注:HHDBCS支持已kingbase,本文仅用来举例。 填入名称、所需数据库的信息,上传驱动…

【tensorboard打开失败】No dashboards are active for the current data set.

这里我再跟视频学的时候,找了很多的指令,说是对应版本不一样,但是发现用了很多指令都可以弹出来跳转的url,那应该就不是输入指令的问题 直到我想把logs里面的文件删掉重新跑的时候,我突然注意到这里有中文字符&#xf…

Android Icon 添加水印 Python脚本

源代码 # -*- coding: utf-8 -*- from PIL import Image 图片合成def mergePictureLXJ():commonIcon Image.open("icon.png")markIcon Image.open("领现金.png")markLayer Image.new(RGBA, commonIcon.size, (0, 0, 0, 0))markLayer.paste(markIcon, (0…

StoneDB-8.0-V2.1.0 企业版正式发布!免费公测中!

很高兴告诉大家,我们StoneDB-8.0-V2.1.0企业版正式发布了!经过一个月的开发,我们的研发团队用极高的效率对2.0新架构版本查漏补缺,完善了最新架构的代码,并对性能、稳定性做出了优化,同时也修复了一些用户们…

C语言到底算高级程序语言还是低级程序语言?

C语言到底算高级程序语言还是低级程序语言? 高级语言 这里的高级都是相对于汇编语言来说的,是指脱离了计算机的硬件系统,可以按照人的思维逻辑进 行编程的语言 低级语言-般都依赖硬件, 基本换个硬件就得重写,很多人说C语言是低…

pytorch笔记 GRUCELL

1 介绍 GRU的一个单元 2 基本使用方法 torch.nn.GRUCell(input_size, hidden_size, biasTrue, deviceNone, dtypeNone) 输入:(batch,input_size) 输出和隐藏层:(batch,hidden_size&#xf…

基于深度学习的人脸专注度检测计算系统 - opencv python cnn 计算机竞赛

文章目录 1 前言2 相关技术2.1CNN简介2.2 人脸识别算法2.3专注检测原理2.4 OpenCV 3 功能介绍3.1人脸录入功能3.2 人脸识别3.3 人脸专注度检测3.4 识别记录 4 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于深度学习的人脸专注度…

怎样去除视频中的杂音,保留人声部分?

怎样去除视频中的杂音,保留人声部分?这个简单嘛!两种办法可以搞定:一是进行音频降噪,把无用的杂音消除掉;二是提取人声,将要保留的人声片段提取出来。 这就将两种实用的办公都分享出来&#xf…

HashMap源码分析——Java全栈知识(8)

jdk1.7和jdk1.8的HashMap的原理有一点出入我们就分开讲解: 1、JDK1.7中的HashMap JDK1.7中的HashMap是通过数组加链表的方式存储数据。他的底层维护了一个Entry数组,通过哈希函数的计算出来哈希值,将待填数据根据计算出来的哈希值填入到对应…

何恺明:在cuhk解答科研问题

文章目录 1. 大模型的未来:数据效益是个问题2. 未来三年研究重点:视觉自监督学习3. 选择课题的标准:好奇心和热情4. AI将成为几乎所有事情的基础工具5. 用疑问解答AI模型可解释性问题AcknowledgementReference何恺明最近在香港中文大学参加一个讲座过程中所述: 1. 大模型的…

循环神经网络(RNN)与长短期记忆网络(LSTM)

前言: 通过前面的学习,我们以BP神经网络为基础,认识到了损失函数,激活函数,以及梯度下降法的原理;而后学习了卷积神经网络,知道图像识别是如何实现的。今天这篇文章,讲述的就是计算机…

【数据结构】树形结构所有路径复原为链表

目录 1. 树形结构可视化 2. 树形结构转为链表 此目标是要还原树形结构的所有路径。树形结构是一种常见的数据结构,它表示元素之间层次关系。在树形结构中,每个节点可能拥有一个或多个子节点,形成了一个分层的结构。为了还原树形结构的路径&…

自定义微信公众号源码系统 带完整搭建教程

在我们现在的互联网时代,越来越多的人开始尝试通过微信公众号来传播自己的信息、提供服务或者进行营销。但是,市面上的微信公众号平台往往功能有限,不能满足部分用户的需求。这时,自定义微信公众号源码系统就成为了解决问题的最佳…

AUC的解释,以及其他指标

因为做比赛,又开始看一些关于评价指标的问题,下面这篇帖子不错,贴过来吧。 参考 ROC曲线画法。 https://www.zhihu.com/question/22844912