【KDD20】多变量时间序列异常检测算法之USAD:对抗性训练AE

简介: # 前言![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/931dfc0b-23ac-4f8e-9d5c-8d6986c90147.png)KDD20的paper- 链接:https://dl.acm.org/doi/pdf/10.1145/3394486.3403392- 代码链接:https://githu

前言

KDD20的paper

一、摘要

在摘要中主要指出了本文的难题引出本文的方法。

原因出自Orange公司的IT系统的自动监测。由于系统的整体的规模和复杂性,随着时间的推移,用于推断正常和异常行为的测量所需的传感器数量急剧增加,使传统的基于专家的监督方法变慢或容易出错。

所以本文提出了一个非监督方法,而前面所说的通过多传感器数据来推断系统正常或异常的问题其实就是多变量时间序列异常检测问题。

最后该方法在五种公共数据集上以及Orange公司提供的真实数据集上进行测试。

二、相关方法

这里文中给出了一些,再加上自己的一些积累,顺便简单总结一些。

  • 基于分布的方法:3-sigma,z-score
  • 基于聚类的方法:kmeans,GMM,DBSCAN等
  • 基于距离的方法:KNN等
  • 基于密度的方法:LOF等
  • 基于分类的方法:Oneclass-SVM等
  • 基于树的方法:iforest等
  • 基于降维的方法:PCA,AE等
  • 基于预测的方法:时序预测之后看差距(这里约等于时序预测的方法)
  • 基于深度学习的方法: DAGMM,LSTM-VAE,VAE等
  • 主要针对多变量时间序列的异常检测深度学习方法:MTAD_GAT,OmniAnomaly,GDN等

文中主要提到一个非常重要的一点,就是算法的性能(使用的硬件资源+运行速度)这个角度,很多算法都没有考虑和测试。当然这两个指标对于一个算法服务化以及响应性能也是十分重要的。

三、 问题定义

这里文章中写了一部分内容(主要涉及很多标准化的公式),这边我直接用简单直接的方式快速阐述一下

一个单变量时间序列肯定很多人都很熟悉了,一般来说是等间隔的序列,故每隔一定的时间有一个数据。

而多变量时间序列就是对应于每一个时刻变成多个维度的序列。

从维度(1, T)->(N, T),其中N代表N个时间序列。

而对于多变量时间序列异常检测,区别于多变量流的异常检测。

前者是 多变量的变化代表某一时刻的整体的异常:比如机器的很多指标出现一个整体或单一的变化代表着机器出现异常。

后者大多代表,时序有很多单一的线,比如有一万多时间序列,每一条时间序列都要检测出对应的异常情况,对于每一条时间序列本质还是单变量时间序列异常检测场景。

更多可以参考论文的描述,很清晰。

四、模型

4.1 基础模型

这里的基础模型就是AE,广泛应用于异常检测场景下,整理简略来说 降维和重构之后来比较和输入的差距,通过与阈值的大小比较来定义是否为异常状态。

训练过程也是计算两者的差距最为训练的目标,也就是loss,一般采用L2 loss也就是均方根loss

4.2 最终模型

4.2.1 训练方式


这里引入了,两个decoder,并且借鉴GAN的训练方式,这可能是本文比较大的创新。

这里图也画的也很直白与直观。

  1. 首先第一步:训练两个AE,使得拥有良好的数据重构能力。

  1. 其次第二步:借鉴GAN的训练模式思想,对抗的训练两个网络,即训练AE2能够很好的区别AE1属出的数据的真实与否,通俗点说,就是AE2更好的分辨哪些是真实数据,哪些是AE1重构的数据。用AE1迷惑AE2,所以AE1的目标是最小化AE2(AE1(W))与W的不同,相反AE2想要更好的区别两者,即最大化不同。

总体的loss是这样

分开即:

最终将两步骤loss合并,其中n为巡览epoch数:

总体的训练步骤如下:

4.2.2 前向推理

异常检测分数定义为:


其中两个参数和为1,来权衡false positive 和true positive,最终通过与阈值的比较,来判断是否为异常的实体。

五、实验

5.1 数据集

其中前五个为public数据集,最后一个为从Orange公司收集的实际数据集。

数据集详情可见:

5.2 评价指标

依然是采用precision,recall,f1的方式。

不过这里有个小不同,对于异常的定义有两种

  1. label是异常就是异常,针对某一点的实体的状态
  2. label针对于某一个window,如果在一个窗口下有异常出现,我们就认为这一个窗口里面的数据都为异常(这个我个人理解也是通过实际的观察而这么定义的,比如在机器异常中,异常有一定的可持续性,在一个窗口内,如果存在一些异常,有的时候也是异常的情况,而并非只有异常的时间点异常)


对于存在多个实体的数据集,也采用了平均的F1。

5.3 实验结果

5.3.1 实验

其中without就是前面说的第一种异常定义,with就是基于window的异常定义

5.3.2 参数实验

5.3.3 训练速度实验


都是在每一个数据集的一个epoch的平均训练时间,在显卡1080Ti上的实验。但是我个人觉得这里应该还可以做一下在大量变量下的前向推理速度的实验,这个响应速度也很关键,以及占用的内存的实验,所耗用的硬件资源也很重要。

5.3.4 真实数据实验

最后给的可视化的监测事件成功的例子还是蛮不错的,并且指出了在实际工作中用30min解决了人工检查需要24h才解决的异常问题。

六、代码解读

6.1 模型代码

整体来说代码比较简单,不赘述了

import torch
import torch.nn as nn

from utils import *
device = get_default_device()

class Encoder(nn.Module):
  def __init__(self, in_size, latent_size):
    super().__init__()
    self.linear1 = nn.Linear(in_size, int(in_size/2))
    self.linear2 = nn.Linear(int(in_size/2), int(in_size/4))
    self.linear3 = nn.Linear(int(in_size/4), latent_size)
    self.relu = nn.ReLU(True)
        
  def forward(self, w):
    out = self.linear1(w)
    out = self.relu(out)
    out = self.linear2(out)
    out = self.relu(out)
    out = self.linear3(out)
    z = self.relu(out)
    return z
    
class Decoder(nn.Module):
  def __init__(self, latent_size, out_size):
    super().__init__()
    self.linear1 = nn.Linear(latent_size, int(out_size/4))
    self.linear2 = nn.Linear(int(out_size/4), int(out_size/2))
    self.linear3 = nn.Linear(int(out_size/2), out_size)
    self.relu = nn.ReLU(True)
    self.sigmoid = nn.Sigmoid()
        
  def forward(self, z):
    out = self.linear1(z)
    out = self.relu(out)
    out = self.linear2(out)
    out = self.relu(out)
    out = self.linear3(out)
    w = self.sigmoid(out)
    return w
    
class UsadModel(nn.Module):
  def __init__(self, w_size, z_size):
    super().__init__()
    self.encoder = Encoder(w_size, z_size)
    self.decoder1 = Decoder(z_size, w_size)
    self.decoder2 = Decoder(z_size, w_size)
  
  def training_step(self, batch, n):
    z = self.encoder(batch)
    w1 = self.decoder1(z)
    w2 = self.decoder2(z)
    w3 = self.decoder2(self.encoder(w1))
    loss1 = 1/n*torch.mean((batch-w1)**2)+(1-1/n)*torch.mean((batch-w3)**2)
    loss2 = 1/n*torch.mean((batch-w2)**2)-(1-1/n)*torch.mean((batch-w3)**2)
    return loss1,loss2

  def validation_step(self, batch, n):
    z = self.encoder(batch)
    w1 = self.decoder1(z)
    w2 = self.decoder2(z)
    w3 = self.decoder2(self.encoder(w1))
    loss1 = 1/n*torch.mean((batch-w1)**2)+(1-1/n)*torch.mean((batch-w3)**2)
    loss2 = 1/n*torch.mean((batch-w2)**2)-(1-1/n)*torch.mean((batch-w3)**2)
    return {'val_loss1': loss1, 'val_loss2': loss2}
        
  def validation_epoch_end(self, outputs):
    batch_losses1 = [x['val_loss1'] for x in outputs]
    epoch_loss1 = torch.stack(batch_losses1).mean()
    batch_losses2 = [x['val_loss2'] for x in outputs]
    epoch_loss2 = torch.stack(batch_losses2).mean()
    return {'val_loss1': epoch_loss1.item(), 'val_loss2': epoch_loss2.item()}
    
  def epoch_end(self, epoch, result):
    print("Epoch [{}], val_loss1: {:.4f}, val_loss2: {:.4f}".format(epoch, result['val_loss1'], result['val_loss2']))
    
def evaluate(model, val_loader, n):
    outputs = [model.validation_step(to_device(batch,device), n) for [batch] in val_loader]
    return model.validation_epoch_end(outputs)

def training(epochs, model, train_loader, val_loader, opt_func=torch.optim.Adam):
    history = []
    optimizer1 = opt_func(list(model.encoder.parameters())+list(model.decoder1.parameters()))
    optimizer2 = opt_func(list(model.encoder.parameters())+list(model.decoder2.parameters()))
    for epoch in range(epochs):
        for [batch] in train_loader:
            batch=to_device(batch,device)
            
            #Train AE1
            loss1,loss2 = model.training_step(batch,epoch+1)
            loss1.backward()
            optimizer1.step()
            optimizer1.zero_grad()
            
            
            #Train AE2
            loss1,loss2 = model.training_step(batch,epoch+1)
            loss2.backward()
            optimizer2.step()
            optimizer2.zero_grad()
            
            
        result = evaluate(model, val_loader, epoch+1)
        model.epoch_end(epoch, result)
        history.append(result)
    return history
    
def testing(model, test_loader, alpha=.5, beta=.5):
    results=[]
    for [batch] in test_loader:
        batch=to_device(batch,device)
        w1=model.decoder1(model.encoder(batch))
        w2=model.decoder2(model.encoder(w1))
        results.append(alpha*torch.mean((batch-w1)**2,axis=1)+beta*torch.mean((batch-w2)**2,axis=1))
    return results

6.2 实战

这里有个jupyter notebook里面给予了数据集SWaT的链接,如果着急的话可以试一下

目录
相关文章
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于MSER和HOG特征提取的SVM交通标志检测和识别算法matlab仿真
### 算法简介 1. **算法运行效果图预览**:展示算法效果,完整程序运行后无水印。 2. **算法运行软件版本**:Matlab 2017b。 3. **部分核心程序**:完整版代码包含中文注释及操作步骤视频。 4. **算法理论概述**: - **MSER**:用于检测显著区域,提取图像中稳定区域,适用于光照变化下的交通标志检测。 - **HOG特征提取**:通过计算图像小区域的梯度直方图捕捉局部纹理信息,用于物体检测。 - **SVM**:寻找最大化间隔的超平面以分类样本。 整个算法流程图见下图。
|
1月前
|
存储 机器学习/深度学习 算法
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
蓝桥杯Python编程练习题的集合,涵盖了从基础到提高的多个算法题目及其解答。
63 3
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
|
24天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
1月前
|
机器学习/深度学习 算法 数据挖掘
基于GWO灰狼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了基于分组卷积神经网络(GroupCNN)和灰狼优化(GWO)的时间序列回归预测算法。算法运行效果良好,无水印展示。使用Matlab2022a开发,提供完整代码及详细中文注释。GroupCNN通过分组卷积减少计算成本,GWO则优化超参数,提高预测性能。项目包含操作步骤视频,方便用户快速上手。
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于WOA鲸鱼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了一种基于WOA优化的GroupCNN分组卷积网络时间序列预测算法。使用Matlab2022a开发,提供无水印运行效果预览及核心代码(含中文注释)。算法通过WOA优化网络结构与超参数,结合分组卷积技术,有效提升预测精度与效率。分组卷积减少了计算成本,而WOA则模拟鲸鱼捕食行为进行优化,适用于多种连续优化问题。
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA遗传优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
该算法结合了遗传算法(GA)与分组卷积神经网络(GroupCNN),利用GA优化GroupCNN的网络结构和超参数,提升时间序列预测精度与效率。遗传算法通过模拟自然选择过程中的选择、交叉和变异操作寻找最优解;分组卷积则有效减少了计算成本和参数数量。本项目使用MATLAB2022A实现,并提供完整代码及视频教程。注意:展示图含水印,完整程序运行无水印。
|
1月前
|
机器学习/深度学习 算法 决策智能
【机器学习】揭秘深度学习优化算法:加速训练与提升性能
【机器学习】揭秘深度学习优化算法:加速训练与提升性能
|
1月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
66 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
23天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA-PSO-SVM算法的混沌背景下微弱信号检测matlab仿真
本项目基于MATLAB 2022a,展示了SVM、PSO、GA-PSO-SVM在混沌背景下微弱信号检测中的性能对比。核心程序包含详细中文注释和操作步骤视频。GA-PSO-SVM算法通过遗传算法和粒子群优化算法优化SVM参数,提高信号检测的准确性和鲁棒性,尤其适用于低信噪比环境。
|
28天前
|
存储 JSON 算法
TDengine 检测数据最佳压缩算法工具,助你一键找出最优压缩方案
在使用 TDengine 存储时序数据时,压缩数据以节省磁盘空间是至关重要的。TDengine 支持用户根据自身数据特性灵活指定压缩算法,从而实现更高效的存储。然而,如何选择最合适的压缩算法,才能最大限度地降低存储开销?为了解决这一问题,我们特别推出了一个实用工具,帮助用户快速判断并选择最适合其数据特征的压缩算法。
35 0
下一篇
无影云桌面