Anomaly-transformer技术文档
介绍
Anomaly Transformer是一种基于Transformer的神经网络模型,用于检测异常点。与传统的异常检测方法相比,Anomaly Transformer不需要事先对数据进行特征工程,因为它可以自动学习数据中的特征。该模型的设计思想是在Transformer模型的基础上增加一个用于异常检测的头部(head)。
在Transformer模型中,输入数据首先被分成多个token,然后通过多层自注意力机制(self-attention)和全连接神经网络(feed-forward network)进行处理。在Anomaly Transformer中,异常检测头部的作用是将模型的输出转换成一个异常得分(anomaly score),该得分反映了输入数据与正常数据之间的差异程度。
Anomaly Transformer的训练过程可以分为两个阶段。首先,在正常数据上训练模型,以学习数据的正常分布。在这个阶段中,异常检测头部的输出将被忽略。接着,在异常数据上进行微调,使得模型可以识别出异常点。在这个阶段中,异常检测头部的输出将被用于计算异常得分。
Anomaly Transformer在异常检测方面具有一些优势。首先,它可以自动学习数据中的特征,因此不需要进行繁琐的特征工程。其次,它可以处理不同类型的数据,包括时间序列、文本和图像数据等。最后,由于Anomaly Transformer是一个端到端的模型,因此可以快速处理大规模数据集。
需要注意的是,Anomaly Transformer并不是万能的,它也存在一些局限性。例如,当数据集中的异常点比正常点更多时,模型可能会难以识别异常点。此外,由于异常点通常是少数,因此在训练过程中需要特别关注异常点的权重分配。
实现方法
Anomaly Transformer的实现方法通常可以分为以下几个步骤:
数据预处理
首先需要对数据进行预处理,将原始数据转换为适合输入到Anomaly Transformer模型的形式。具体而言,需要将数据分成多个token,并将每个token表示为向量。
模型设计
Anomaly Transformer的模型设计基于Transformer模型,但是在其基础上增加了一个用于异常检测的头部。该头部通常由一些全连接层和一个输出层组成,用于将Transformer的输出转换为一个异常得分。在训练过程中,需要注意调整异常检测头部的参数,以使得模型可以识别出异常点。
模型训练
在训练过程中,需要使用正常数据来训练模型,以学习数据的正常分布。训练过程通常使用反向传播算法和优化器来更新模型的参数。为了确保模型能够识别出异常点,还需要在训练集中添加一定比例的异常点,并将其标记为异常数据。
模型评估
在训练完成后,需要对模型进行评估,以确定其在异常检测方面的性能。通常可以使用准确率、召回率和F1值等指标来评估模型的性能。此外,还需要对模型进行调优,以进一步提高其性能。
部署模型
最后,可以将训练好的模型部署到生产环境中,以进行实时的异常检测。在部署过程中,需要考虑模型的效率和可靠性,并保证其可以处理不同类型的数据。此外,还需要定期对模型进行更新和维护,以保证其持续的性能。
涉及知识点
Anomaly Transformer 是一种基于 Transformer 的神经网络模型,用于检测异常点。它主要涉及以下知识点:
- Transformer 模型:Anomaly Transformer 是在 Transformer 模型的基础上进行扩展的。因此,理解 Transformer 模型的原理和机制对于理解 Anomaly Transformer 的实现非常重要。Transformer 模型包括自注意力机制和前馈神经网络等组件,可以实现输入序列的编码和解码。
- 自注意力机制:自注意力机制是 Transformer 模型的核心组件之一,用于计算输入序列中每个 token 与其他 token 之间的相关性。Anomaly Transformer 在自注意力机制的基础上增加了一个异常检测头部,用于将模型的输出转换成一个异常得分。
- 异常检测:Anomaly Transformer 的主要目的是检测异常点。因此,需要理解常见的异常检测方法,例如基于统计学的方法和基于机器学习的方法等。同时,还需要了解异常点的定义和特征。
- 神经网络训练和优化:Anomaly Transformer 是一个基于神经网络的模型,需要使用反向传播算法和优化器来更新模型的参数。因此,需要了解神经网络的训练和优化过程,包括损失函数的设计、学习率的调整和梯度下降等。
- 数据预处理:在训练 Anomaly Transformer 模型之前,需要对数据进行预处理,将原始数据转换为适合输入到模型的形式。这通常涉及到将数据分成多个 token,并将每个 token 表示为向量。
- 模型评估和调优:Anomaly Transformer 的性能需要通过评估指标来衡量,例如准确率、召回率和 F1 值等。同时,还需要对模型进行调优,以进一步提高其性能。这涉及到选择合适的训练集和测试集、调整模型参数、采用正则化技术等。
- 模型部署和维护:最后,Anomaly Transformer 模型需要部署到生产环境中进行实时的异常检测。在部署过程中,需要考虑模型的效率和可靠性,并保证其可以处理不同类型的数据。此外,还需要定期对模型进行更新和维护,以保证其持续的性能。
Transformer 输入模块
代码
# -*- coding: utf-8 -*-
import copy
import torch
import torch.nn as nn
import math
from torch.autograd import Variable
import torch.nn.functional as F
class Embeddings(nn.Module):
def __init__(self,d_model,vocab):
#d_model是词嵌入的维度,vacab是词表大小
super().__init__()
self.lut=nn.Embedding(vocab,d_model)
self.d_model=d_model
def forward(self,x):
return self.lut(x)*math.sqrt(self.d_model)
d_model=512#维度大小
vocab=1000#词表大小
x=Variable(torch.LongTensor([[100,2,421,999],[491,998,1,221]]))
#数字相当于词表中词语的下标,词表大小为1000,所以tensor数字范围为[0,999]
emb=Embeddings(d_model, vocab)
embr=emb(x)
#等效于emb.forward(x)
print(embr)
print(embr.shape)
这是一个用 PyTorch 实现的词嵌入(embedding)层,它将一个整数张量(tensor)编码为密集的实数向量(vector)。
具体来说,代码定义了一个名为 Embeddings 的类,它继承自 PyTorch 的 nn.Module 类。Embeddings 类的初始化方法 init() 接受两个参数:d_model 和 vocab。其中,d_model 表示词嵌入向量的维度大小,vocab 表示词表大小。
在初始化方法中,类定义了一个 nn.Embedding 对象,它是 PyTorch 中的内置词嵌入层。该层的输入是一个整数,输出是一个形状为 [batch_size, seq_length, d_model] 的实数张量。在本例中,batch_size 为 2,seq_length 为 4,因此 Embeddings 层的输出形状为 [2, 4, 512]。在 forward() 方法中,Embeddings 层首先调用 nn.Embedding 对象,将整数张量 x 转换为实数张量,然后乘以 math.sqrt(self.d_model)。这是为了缩放词嵌入向量,以使其大小适合于 Transformer 模型。
在这个代码片段的最后,程序通过一个输入整数张量 x 调用 Embeddings 类的 forward() 方法。x 的形状是 [2, 4],表示一个大小为 2 的 batch,每个 batch 包含 4 个整数。在调用 forward() 方法之后,程序输出了词嵌入层的输出 embr,以及其形状 [2, 4, 512]。其中,512 表示词嵌入向量的维度大小。
batch(批次)
在机器学习中,batch(批次)是指一组同时被输入到神经网络进行训练或推理的数据样本。通常,每个样本都是一个张量(tensor),而一个 batch 则是一个张量列表,其中每个张量的第一个维度都是相同的,表示该 batch 的大小。
以图像分类任务为例,假设有一个包含 10,000 张图像的数据集,每张图像的大小为 224×224 像素,并且有 100 个不同的类别。如果我们将所有图像同时输入到神经网络进行训练,会占用很多内存,训练时间也会很长。相反,我们可以将数据集分成若干个 batch,每个 batch 包含 32 或 64 张图像,然后依次将这些 batch 输入到网络中进行训练。这样做的好处是可以节省内存,加速训练,同时也有助于模型泛化,避免过度拟合。
在实际应用中,batch 的大小通常是一个超参数,需要通过实验来进行调整。较小的 batch 大小会使梯度更新更加频繁,但也可能会增加噪声和不稳定性;而较大的 batch 大小则可能会导致内存不足、训练时间过长等问题。因此,需要在实践中进行测试和优化,选择一个合适的 batch 大小以获得最佳的训练效果。
张量
在深度学习中,张量(tensor)是一种多维数组,是神经网络中最基本的数据结构之一。张量可以看作是向量、矩阵和其他更高维度数组的推广,可以存储和表示各种类型的数据,例如图像、音频、文本等。
在神经网络中,输入数据和模型参数都是以张量的形式传递和存储的。具体来说,输入数据经过张量的形式进行表示和处理,经过一系列的线性变换、非线性激活和池化等操作,最终输出一个或多个张量作为预测结果或特征表示。而模型参数则是通过反向传播算法更新,以最小化损失函数,并提高模型的准确性和泛化能力。
张量的重要性还体现在它的高效性上,张量运算可以通过GPU等硬件加速,加快神经网络的训练和推理速度,实现对大规模数据和模型的高效处理。
维度
在深度学习中,维度(dimension)通常指的是张量的秩(rank),即张量中包含元素的轴数。例如,标量(scalar)的秩为0,向量(vector)的秩为1,矩阵(matrix)的秩为2,以此类推。
每个轴的长度表示该轴方向上元素的数量,例如一个形状为 (3, 4, 5) 的张量,其中第一个轴的长度为3,第二个轴的长度为4,第三个轴的长度为5。对于三维张量,可以将其视为一个由3个二维矩阵组成的立体图像,其中每个矩阵代表一个水平切片。
在深度学习中,维度的重要性在于它与神经网络的结构和计算有着密切的关系。例如,在卷积神经网络中,通过改变卷积层的输入和输出张量的维度,可以实现不同形式的卷积操作,如同卷积核大小、步长、填充方式等。此外,维度还涉及到张量的形状变换、扩展、拼接等常见操作,这些操作是深度学习中必不可少的技能。
Embedding(词嵌入)
在自然语言处理中,Embedding(词嵌入)是将词语映射到低维稠密向量空间中的过程。它是一种常用的将离散符号转换为连续向量表示的方法,是自然语言处理中的一项重要技术。
在深度学习中,Embedding通常通过一个矩阵来实现,该矩阵的行数等于词表中单词的数量,列数等于嵌入的维度。每个单词被表示为该矩阵中的一行,也就是一个低维的稠密向量,称为该单词的Embedding向量。Embedding向量的长度通常在几十到几百之间,不同的应用场景和任务需要不同的长度。
使用Embedding可以将离散的单词或字符转换为连续的向量,使得神经网络可以更好地处理文本数据。例如,在自然语言处理中,可以将每个单词映射为一个Embedding向量,然后使用这些向量作为输入,进行后续的文本分类、情感分析、机器翻译等任务。
forward方法
在神经网络中,forward()方法是一个重要的方法,用于定义模型的前向传播过程。当给定输入数据时,神经网络将按照forward()方法中的定义,对输入进行一系列的计算和转换,并输出最终的结果。
在PyTorch中,定义一个神经网络时需要继承nn.Module类,并重写其中的forward()方法。在重写forward()方法时,需要使用PyTorch提供的各种张量操作(如卷积、池化、全连接等),来实现模型的具体功能。
在模型训练过程中,每次输入数据时,forward()方法将被自动调用,并返回模型的输出结果。然后通过计算输出结果与实际标签之间的差异,再使用反向传播算法来更新模型的参数,使得模型能够不断地逐渐优化,最终达到预期的效果。
其运行结果为:
tensor([[[ -1.2578, 2.7126, -34.2691, ..., -10.0247, -24.6125, -18.9279],
[ 10.4483, -26.1474, 9.3407, ..., -56.0667, 26.7694, -8.8562],
[-25.8409, -10.9467, -15.8338, ..., -43.4646, -46.5105, -39.5861],
[ 4.4863, -10.8304, 18.8405, ..., 20.7088, -13.4723, 11.8239]],
[[-19.8429, -2.2979, -49.3931, ..., -11.1465, -41.0410, 12.8204],
[ 42.3238, 3.9417, -8.6706, ..., 27.7693, 17.9512, -8.0164],
[ 10.4532, 28.1825, 48.5035, ..., 16.2428, -10.8276, 39.2088],
[ 13.9543, -7.9238, 11.3745, ..., 5.7166, 23.8743, 4.5359]]],
grad_fn=<MulBackward0>)
torch.Size([2, 4, 512])
mian.py代码详细解读
import os
import argparse
from torch.backends import cudnn
from utils.utils import *
from solver import Solver
def str2bool(v):
return v.lower() in ('true')
def main(config):
cudnn.benchmark = True
if (not os.path.exists(config.model_save_path)):
mkdir(config.model_save_path)
solver = Solver(vars(config))
if config.mode == 'train':
solver.train()
elif config.mode == 'test':
solver.test()
return solver
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--lr', type=float, default=1e-4)
parser.add_argument('--num_epochs', type=int, default=1) #训练轮数
parser.add_argument('--k', type=int, default=2)
parser.add_argument('--win_size', type=int, default=10) #窗口大小
parser.add_argument('--input_c', type=int, default=25) #输入维度
parser.add_argument('--output_c', type=int, default=25) #输出维度
parser.add_argument('--batch_size', type=int, default=16) #批次大小
parser.add_argument('--pretrained_model', type=str, default=None)
parser.add_argument('--dataset', type=str, default='SMAP')
parser.add_argument('--mode', type=str, default='test', choices=['train', 'test'])
parser.add_argument('--data_path', type=str, default='./Scripts/SMAP')
parser.add_argument('--model_save_path', type=str, default='Scripts/SMAP/checkpoints')
parser.add_argument('--anormly_ratio', type=float, default=.8)
config = parser.parse_args()
args = vars(config)
print('------------ Options -------------')
for k, v in sorted(args.items()):
print('%s: %s' % (str(k), str(v)))
print('-------------- End ----------------')
main(config)
Solver.py代码详细解读
my_kl_loss(p, q)
def my_kl_loss(p, q):
res = p * (torch.log(p + 0.0001) - torch.log(q + 0.0001))
return torch.mean(torch.sum(res, dim=-1), dim=1)
这是一个定义KL散度(Kullback-Leibler divergence)损失函数的函数。KL散度是一种衡量两个概率分布之间差异的方法,通常用于度量模型生成的概率分布与真实概率分布之间的距离。
输入参数p和q分别表示两个概率分布,它们的形状为(batch_size, num_classes),其中batch_size表示批量大小,num_classes表示类别数。函数首先计算两个分布之间的KL散度,然后取所有样本的KL散度的平均值作为损失函数的值。
具体来说,函数中的res变量表示KL散度的分子部分,即p和q之间的差异。由于计算中会涉及到对数运算,因此在函数中使用torch.log()函数来计算对数。在计算过程中,为了避免对数的结果出现负无穷的情况,通常会对p和q都加上一个很小的常数,这里的常数是0.0001。然后,使用torch.sum()函数计算res中所有元素的和,得到每个样本的KL散度值。最后,使用torch.mean()函数计算所有样本的KL散度的平均值,并返回该值作为损失函数的输出。
需要注意的是,由于KL散度的计算方式是不对称的,即KL(p||q) != KL(q||p),因此在使用该损失函数时需要明确p和q的顺序。通常情况下,我们会将真实概率分布p作为第一个参数,将生成概率分布q作为第二个参数,以计算KL(p||q)。
adjust_learningrate(optimizer, epoch, lr)
def adjust_learning_rate(optimizer, epoch, lr_):
lr_adjust = {epoch: lr_ * (0.5 ** ((epoch - 1) // 1))}
if epoch in lr_adjust.keys():
lr = lr_adjust[epoch]
for param_group in optimizer.param_groups:
param_group['lr'] = lr
print('Updating learning rate to {}'.format(lr))
这是一个用于动态调整学习率的函数。在神经网络训练中,学习率是控制模型参数更新步长的重要超参数,调整学习率可以帮助模型更好地收敛并避免训练过程中的震荡。
该函数接收三个参数:optimizer表示优化器对象,epoch表示当前训练轮数,lr_表示初始学习率。其中,optimizer用于更新模型参数,epoch用于控制学习率更新的时间,lr_用于设置初始学习率。
函数中,我们首先定义了一个字典lr_adjust,用于存储每个epoch对应的学习率大小。字典的键为epoch,值为学习率大小。这里采用了一种动态调整学习率的方法,即每隔1个epoch就将学习率乘以0.5。这样,每隔1个epoch,学习率就会减半。例如,在第1个epoch结束后,学习率将变为初始学习率的一半;在第2个epoch结束后,学习率将变为初始学习率的四分之一;在第3个epoch结束后,学习率将变为初始学习率的八分之一,以此类推。
接下来,函数检查当前epoch是否需要调整学习率。如果当前epoch在lr_adjust字典中,说明需要调整学习率。此时,我们从字典中获取当前epoch对应的学习率,并使用optimizer.param_groups将优化器中所有参数组的学习率更新为新的学习率。最后,函数输出调整后的学习率。
vali(self, vali_loader)
def vali(self, vali_loader):
self.model.eval()
loss_1 = []
loss_2 = []
for i, (input_data, _) in enumerate(vali_loader):
input = input_data.float().to(self.device)
output, series, prior, _ = self.model(input)
series_loss = 0.0
prior_loss = 0.0
for u in range(len(prior)):
series_loss += (torch.mean(my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach())) + torch.mean(
my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach(),
series[u])))
prior_loss += (torch.mean(
my_kl_loss((prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach())) + torch.mean(
my_kl_loss(series[u].detach(),
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)))))
series_loss = series_loss / len(prior)
prior_loss = prior_loss / len(prior)
rec_loss = self.criterion(output, input)
loss_1.append((rec_loss - self.k * series_loss).item())
loss_2.append((rec_loss + self.k * prior_loss).item())
return np.average(loss_1), np.average(loss_2)
这是一个PyTorch中的函数,用于在验证集上进行模型评估。该函数接受一个验证集数据加载器vali_loader
作为输入,并返回两个平均损失值loss_1
和loss_2
。
在函数内部,模型被设置为评估模式,即self.model.eval()
。然后遍历验证集中的每个数据样本,将输入数据转换为浮点张量并传输到设备(GPU或CPU)。然后,通过将输入传递到self.model
获得模型的输出、series、prior和一个未使用的变量。这些输出将被用于计算损失。
在下面的循环中,遍历先前得到的prior,并计算series_loss和prior_loss,这些都是通过调用my_kl_loss
函数来计算的。其中,my_kl_loss
计算输入分布(p)和目标分布(q)之间的KL散度。
在下面的代码中,有一个系数k,用于调整系列损失和先前损失之间的平衡。通过计算重构损失与k×series_loss之间的差异,可以获得loss_1
。通过计算重构损失与k×prior_loss之间的总和,可以获得loss_2
。这些损失值将被存储在loss_1
和loss_2
中,并最终返回它们的平均值。
train(self)
def train(self):
print("======================TRAIN MODE======================")
time_now = time.time()
path = self.model_save_path
if not os.path.exists(path):
os.makedirs(path)
early_stopping = EarlyStopping(patience=3, verbose=True, dataset_name=self.dataset)
train_steps = len(self.train_loader)
for epoch in range(self.num_epochs):
iter_count = 0
loss1_list = []
epoch_time = time.time()
self.model.train()
for i, (input_data, labels) in enumerate(self.train_loader):
self.optimizer.zero_grad()
iter_count += 1
input = input_data.float().to(self.device)
output, series, prior, _ = self.model(input)
# calculate Association discrepancy
series_loss = 0.0
prior_loss = 0.0
for u in range(len(prior)):
series_loss += (torch.mean(my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach())) + torch.mean(
my_kl_loss((prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach(),
series[u])))
prior_loss += (torch.mean(my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach())) + torch.mean(
my_kl_loss(series[u].detach(), (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)))))
series_loss = series_loss / len(prior)
prior_loss = prior_loss / len(prior)
rec_loss = self.criterion(output, input)
loss1_list.append((rec_loss - self.k * series_loss).item())
loss1 = rec_loss - self.k * series_loss
loss2 = rec_loss + self.k * prior_loss
if (i + 1) % 100 == 0:
speed = (time.time() - time_now) / iter_count
left_time = speed * ((self.num_epochs - epoch) * train_steps - i)
print('\tspeed: {:.4f}s/iter; left time: {:.4f}s'.format(speed, left_time))
iter_count = 0
time_now = time.time()
# Minimax strategy
loss1.backward(retain_graph=True)
loss2.backward()
self.optimizer.step()
print("Epoch: {} cost time: {}".format(epoch + 1, time.time() - epoch_time))
train_loss = np.average(loss1_list)
vali_loss1, vali_loss2 = self.vali(self.test_loader)
print(
"Epoch: {0}, Steps: {1} | Train Loss: {2:.7f} Vali Loss: {3:.7f} ".format(
epoch + 1, train_steps, train_loss, vali_loss1))
early_stopping(vali_loss1, vali_loss2, self.model, path)
if early_stopping.early_stop:
print("Early stopping")
break
adjust_learning_rate(self.optimizer, epoch + 1, self.lr)
这是一个训练深度学习模型的代码。以下是对主要代码块的解释:
train
: 此方法是训练模型的主要函数。它将数据加载器传递给模型并迭代指定数量的时期。在每个时期中,它通过调用vali
方法计算验证集上的损失。还有一个名为EarlyStopping
的回调,它会在验证集上的损失不再改善时停止训练。self.optimizer.zero_grad()
: 在每个批次的开始时,将优化器的梯度设置为零,以防止梯度在多个批次之间累积。self.model.train()
: 在每个批次的开始时,将模型设置为训练模式,以确保在训练时使用批量规范化等技术。self.optimizer.step()
: 在计算了梯度之后,调用此方法来执行优化步骤,即使用梯度更新模型参数。loss1.backward(retain_graph=True)
: 计算前向传递后的反向传递梯度,即计算相对于loss1
的梯度,同时保留计算图以进行后续计算。loss2.backward()
: 计算相对于loss2
的梯度,并使用梯度更新模型参数。early_stopping(vali_loss1, vali_loss2, self.model, path)
: 在每个时期结束时,将模型和路径传递给EarlyStopping
回调,以检查模型是否应该被早期停止。
这段代码中还有一些自定义函数,如 my_kl_loss
和 adjust_learning_rate
,这些函数在代码中没有给出。
test(self)
def test(self):
self.model.load_state_dict(
torch.load(
os.path.join(str(self.model_save_path), str(self.dataset) + '_checkpoint.pth')))
self.model.eval()
temperature = 50
print("======================TEST MODE======================")
criterion = nn.MSELoss(reduce=False)
# (1) stastic on the train set
attens_energy = []
for i, (input_data, labels) in enumerate(self.train_loader):
input = input_data.float().to(self.device)
output, series, prior, _ = self.model(input)
loss = torch.mean(criterion(input, output), dim=-1)
series_loss = 0.0
prior_loss = 0.0
for u in range(len(prior)):
if u == 0:
series_loss = my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach()) * temperature
prior_loss = my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach()) * temperature
else:
series_loss += my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach()) * temperature
prior_loss += my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach()) * temperature
metric = torch.softmax((-series_loss - prior_loss), dim=-1)
cri = metric * loss
cri = cri.detach().cpu().numpy()
attens_energy.append(cri)
attens_energy = np.concatenate(attens_energy, axis=0).reshape(-1)
train_energy = np.array(attens_energy)
# (2) find the threshold
attens_energy = []
for i, (input_data, labels) in enumerate(self.thre_loader):
input = input_data.float().to(self.device)
output, series, prior, _ = self.model(input)
loss = torch.mean(criterion(input, output), dim=-1)
series_loss = 0.0
prior_loss = 0.0
for u in range(len(prior)):
if u == 0:
series_loss = my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach()) * temperature
prior_loss = my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach()) * temperature
else:
series_loss += my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach()) * temperature
prior_loss += my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach()) * temperature
# Metric
metric = torch.softmax((-series_loss - prior_loss), dim=-1)
cri = metric * loss
cri = cri.detach().cpu().numpy()
attens_energy.append(cri)
attens_energy = np.concatenate(attens_energy, axis=0).reshape(-1)
test_energy = np.array(attens_energy)
combined_energy = np.concatenate([train_energy, test_energy], axis=0)
thresh = np.percentile(combined_energy, 100 - self.anormly_ratio)
print("Threshold :", thresh)
# (3) evaluation on the test set
test_labels = []
attens_energy = []
for i, (input_data, labels) in enumerate(self.thre_loader):
input = input_data.float().to(self.device)
output, series, prior, _ = self.model(input)
loss = torch.mean(criterion(input, output), dim=-1)
series_loss = 0.0
prior_loss = 0.0
for u in range(len(prior)):
if u == 0:
series_loss = my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach()) * temperature
prior_loss = my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach()) * temperature
else:
series_loss += my_kl_loss(series[u], (
prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)).detach()) * temperature
prior_loss += my_kl_loss(
(prior[u] / torch.unsqueeze(torch.sum(prior[u], dim=-1), dim=-1).repeat(1, 1, 1,
self.win_size)),
series[u].detach()) * temperature
metric = torch.softmax((-series_loss - prior_loss), dim=-1)
cri = metric * loss
cri = cri.detach().cpu().numpy()
attens_energy.append(cri)
test_labels.append(labels)
attens_energy = np.concatenate(attens_energy, axis=0).reshape(-1)
test_labels = np.concatenate(test_labels, axis=0).reshape(-1)
test_energy = np.array(attens_energy)
test_labels = np.array(test_labels)
pred = (test_energy > thresh).astype(int)
gt = test_labels.astype(int)
print("pred: ", pred.shape)
print("gt: ", gt.shape)
# detection adjustment
anomaly_state = False
for i in range(len(gt)):
if gt[i] == 1 and pred[i] == 1 and not anomaly_state:
anomaly_state = True
for j in range(i, 0, -1):
if gt[j] == 0:
break
else:
if pred[j] == 0:
pred[j] = 1
for j in range(i, len(gt)):
if gt[j] == 0:
break
else:
if pred[j] == 0:
pred[j] = 1
elif gt[i] == 0:
anomaly_state = False
if anomaly_state:
pred[i] = 1
pred = np.array(pred)
gt = np.array(gt)
print("pred: ", pred.shape)
print("gt: ", gt.shape)
from sklearn.metrics import precision_recall_fscore_support
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(gt, pred)
precision, recall, f_score, support = precision_recall_fscore_support(gt, pred,
average='binary')
print(
"Accuracy : {:0.4f}, Precision : {:0.4f}, Recall : {:0.4f}, F-score : {:0.4f} ".format(
accuracy, precision,
recall, f_score))
return accuracy, precision, recall, f_score
这段代码是用于在时序数据中检测异常的一个方法。主要分为三个步骤:
第一步是对训练集进行统计,获取每个时间序列点的异常得分(energy),这里使用了 MSELoss 作为损失函数,然后结合模型的输出结果计算每个点的异常得分。
第二步是根据训练集得到的异常得分统计出一个异常阈值。具体方法是将训练集和测试集的异常得分合并后,找到一个分位数,将高于这个分位数的点标记为异常。
第三步是在测试集上进行异常检测,得到每个点的异常得分,并将高于阈值的点标记为异常。
准确率(Accuracy):代表模型在所有样本中正确分类的比例,即正确预测的样本数与总样本数的比值。在这里,准确率为0.9587,表示模型正确预测了95.87%的样本。
精确率(Precision):代表模型在预测为异常的样本中,真正是异常的样本所占的比例。在这里,精确率为0.9216,表示模型将92.16%的预测为异常的样本正确地识别为异常。
召回率(Recall):代表模型正确预测为异常的样本数占所有真正异常的样本数的比例。在这里,召回率为0.9304,表示模型能够正确地识别出93.04%的真正异常样本。
F1值(F-score):综合了精确率和召回率两个指标,是一个综合评估模型性能的指标。在这里,F1值为0.9260,表示模型综合考虑了精确率和召回率两个指标,具有比较好的分类效果。
提高F1值的方法。
- 调整超参数:您可以通过调整学习率、正则化参数、批次大小等超参数来优化模型。您可以使用交叉验证技术来找到最佳的超参数组合。
- 数据增强:通过对数据集进行增强,例如旋转、翻转、缩放、裁剪等,可以增加数据集的多样性,有助于提高模型的泛化能力。
- 更换损失函数:您可以尝试使用不同的损失函数,例如二元交叉熵、Focal Loss、Dice Loss等,以获得更好的结果。
- 增加训练数据:使用更多的训练数据可以帮助模型更好地学习数据的分布,有助于提高模型的性能。
- 调整模型结构:通过修改模型的层数、宽度、卷积核大小等结构参数来优化模型。
- 使用预训练模型:使用预训练模型可以加速模型的收敛速度,并且通常可以提高模型的性能。
数据集
SMD
SMD数据集是指智能能源管理系统(Smart*)中用于异常检测的数据集,其中包含了来自20个不同楼宇的真实数据。这些数据记录了不同类型的传感器数据,包括温度、湿度、电力等,用于检测能源系统中的异常和故障。SMD数据集可以用于训练和测试各种异常检测算法,是一个常用的数据集之一。
SMAP
SMAP (Soil Moisture Active Passive) 数据集:是由NASA发起的一个卫星任务,旨在通过使用微波辐射计和微波干涉仪,测量全球土壤湿度和冻融状态。SMAP数据集包含来自2015年至今的土壤湿度数据、表面土壤温度数据和冻融状态数据,可用于研究土壤湿度、水文循环和气候变化等问题。
PSM
PSM (Power System Modeling) 数据集:是一个用于电力系统研究的数据集,包括了大量的电力系统运行数据,如电力负荷、发电机输出、线路电压等,可用于电力系统建模、优化和控制等研究领域。
MSL
PSM (Power System Modeling) 数据集:是一个用于电力系统研究的数据集,包括了大量的电力系统运行数据,如电力负荷、发电机输出、线路电压等,可用于电力系统建模、优化和控制等研究领域。