Anomly-transformer

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 的神经网络模型,用于检测异常点。它主要涉及以下知识点:

  1. Transformer 模型:Anomaly Transformer 是在 Transformer 模型的基础上进行扩展的。因此,理解 Transformer 模型的原理和机制对于理解 Anomaly Transformer 的实现非常重要。Transformer 模型包括自注意力机制和前馈神经网络等组件,可以实现输入序列的编码和解码。
  2. 自注意力机制:自注意力机制是 Transformer 模型的核心组件之一,用于计算输入序列中每个 token 与其他 token 之间的相关性。Anomaly Transformer 在自注意力机制的基础上增加了一个异常检测头部,用于将模型的输出转换成一个异常得分。
  3. 异常检测:Anomaly Transformer 的主要目的是检测异常点。因此,需要理解常见的异常检测方法,例如基于统计学的方法和基于机器学习的方法等。同时,还需要了解异常点的定义和特征。
  4. 神经网络训练和优化:Anomaly Transformer 是一个基于神经网络的模型,需要使用反向传播算法和优化器来更新模型的参数。因此,需要了解神经网络的训练和优化过程,包括损失函数的设计、学习率的调整和梯度下降等。
  5. 数据预处理:在训练 Anomaly Transformer 模型之前,需要对数据进行预处理,将原始数据转换为适合输入到模型的形式。这通常涉及到将数据分成多个 token,并将每个 token 表示为向量。
  6. 模型评估和调优:Anomaly Transformer 的性能需要通过评估指标来衡量,例如准确率、召回率和 F1 值等。同时,还需要对模型进行调优,以进一步提高其性能。这涉及到选择合适的训练集和测试集、调整模型参数、采用正则化技术等。
  7. 模型部署和维护:最后,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_1loss_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_1loss_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_lossadjust_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值的方法。

  1. 调整超参数:您可以通过调整学习率、正则化参数、批次大小等超参数来优化模型。您可以使用交叉验证技术来找到最佳的超参数组合。
  2. 数据增强:通过对数据集进行增强,例如旋转、翻转、缩放、裁剪等,可以增加数据集的多样性,有助于提高模型的泛化能力。
  3. 更换损失函数:您可以尝试使用不同的损失函数,例如二元交叉熵、Focal Loss、Dice Loss等,以获得更好的结果。
  4. 增加训练数据:使用更多的训练数据可以帮助模型更好地学习数据的分布,有助于提高模型的性能。
  5. 调整模型结构:通过修改模型的层数、宽度、卷积核大小等结构参数来优化模型。
  6. 使用预训练模型:使用预训练模型可以加速模型的收敛速度,并且通常可以提高模型的性能。

数据集

SMD

SMD数据集是指智能能源管理系统(Smart*)中用于异常检测的数据集,其中包含了来自20个不同楼宇的真实数据。这些数据记录了不同类型的传感器数据,包括温度、湿度、电力等,用于检测能源系统中的异常和故障。SMD数据集可以用于训练和测试各种异常检测算法,是一个常用的数据集之一。

SMAP

SMAP (Soil Moisture Active Passive) 数据集:是由NASA发起的一个卫星任务,旨在通过使用微波辐射计和微波干涉仪,测量全球土壤湿度和冻融状态。SMAP数据集包含来自2015年至今的土壤湿度数据、表面土壤温度数据和冻融状态数据,可用于研究土壤湿度、水文循环和气候变化等问题。

PSM

PSM (Power System Modeling) 数据集:是一个用于电力系统研究的数据集,包括了大量的电力系统运行数据,如电力负荷、发电机输出、线路电压等,可用于电力系统建模、优化和控制等研究领域。

MSL

PSM (Power System Modeling) 数据集:是一个用于电力系统研究的数据集,包括了大量的电力系统运行数据,如电力负荷、发电机输出、线路电压等,可用于电力系统建模、优化和控制等研究领域。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇