当前位置:   article > 正文

《异常检测——从经典算法到深度学习》18 USAD:多元时间序列的无监督异常检测_多元时间序列异常检测

多元时间序列异常检测

《异常检测——从经典算法到深度学习》

相关:

18. USAD:多元时间序列的无监督异常检测

论文下载地址:https://www.researchgate.net
Smileyan 翻译 https://smileyan.cn/#/ad/USAD
源码:https://github.com/manigalati/usad
另一个源码实现(非原论文源码)https://github.com/finloop/usad-torchlightning

18.1 论文概述

论文提出了一种对多维时间序列数据的无监督的、基于对抗训练的自编码器异常检测算法。并且论文是开源的,比较详细的介绍自己论文基于那些数据集,甚至提供了一些超参数的设置参考等。

论文的主要贡献包括:

  • 论文提出了一种结合自编码器与对抗性训练的算法;
  • 论文对公开的数据集进行了实验,对提出的算法进行鲁棒性、训练速度和性能方面的分析;
  • 使用 Orange 的专用数据进行可行性研究,以分析所提出的方法是否满足公司在可扩展性、稳定性、鲁棒性、训练速度和高性能方面的要求。

18.2 算法核心架构

关于自编码器的数学原理,对抗训练的数据原理 这里不介绍,推荐参考 1 | 2

跳过相关工作背景介绍,直接看它的图一的左边。

在这里插入图片描述

A E 1 ( W ) = D 1 ( E ( W ) ) ,    A E 2 ( W ) = D 2 ( E ( W ) ) (3) AE_1(W) = D_1(E(W)),\ \ AE_2(W)=D_2(E(W)) \tag{3} AE1(W)=D1(E(W)),  AE2(W)=D2(E(W))(3)

USAD 训练时分为两个过程,

  1. 对这两个 AE 网络进行训练,使得它们对正常数据具有很好地重构能力;
  2. 对这两个 AE 网络进行对抗性训练,其中 A E 1 AE_1 AE1 尝试去 “迷惑” A E 2 AE_2 AE2 A E 2 AE_2 AE2 努力不被迷惑,即识别哪些是真实数据,哪些是 A E 1 AE_1 AE1 生成的假数据。

明白这个整体过程,我们就看图说话吧,先看左边的训练部分的红色虚线框中间的内容,原始窗口数据输入的编码网络 E n c o d e r Encoder Encoder,得到隐变量 Z Z Z,接着到达 D e c o d e r 1 Decoder 1 Decoder1 然后有根绿色的实线,这是只根据 Decoder 的结果进行参数的调整,再次训练。同样那个浅蓝色框的内容也是如此训练。这个是第一阶段(AE1 minimizes the reconstruction error of W (phase
1))。

第二阶段就是 A E 1 AE_1 AE1 尝试去迷惑 A E 2 AE_2 AE2 的过程,这个迷惑的过程可以打个可能恰当可能不恰当但是忍不住一定想打的比方: A E 1 AE_1 AE1 是一个假珠宝商,在他被抓进局子之前他想去销售自己的假货, A E 2 AE_2 AE2 就是受害者群体。我们查看图片中右上角的 A E 2 ( A E 1 ( W ) ) AE_2(AE_1(W)) AE2(AE1(W)),这个单方面企图欺骗的过程就是这个表达式了。(minimizes the difference between W an the reconstructed output of AE2)

这个过程中可能会疑惑,那为什么要两个AE网络,对抗训练一个不就行了吗?那是因为作者后面还要用到。这个训练过程完成以后,可以肯定的是,这两个网络肯定不同了,已经分道扬镳,从此各干各的了。

记着看图1的右边部分,检测过程。这部分内容比较好理解。最终我们的计算结果是将两个网络的检测结果带参数相加即可。
在这里插入图片描述

18.3 USAD 训练算法

我对原论文的三线表进行翻译,整理后结果如下,总体过程上面已经讲过,这里并没有什么提别之处。
在这里插入图片描述

18.4 USAD 异常检测算法

这里最主要的难点可能是,你需要知道AE是如何用于异常检测的。这个参考6 前半截部分即可,比较容易这个地方真的不想重复了额额额。

在这里插入图片描述

18.5 动手实验

18.5.1 数据集下载

论文总共用到了5个公开数据集,但源码中只是针对于 SWaT dataset 数据集而展开的,可以前去SWaT官网申请访问权限,https://itrust.sutd.edu.sg/itrust-labs_datasets/dataset_info/#swat,基本上填写一些信息就可以获得。但是

我根据论文提到的数据集源码地址下载好了,并上传到 蓝奏云 中,以便于不能访问谷歌网盘的小伙伴们直接下载。

18.5.2 搭建环境

根据论文源码内容,需要安装 pytorch1.6,如果希望运行在 GPU 上根据torch官方文档安装相应版本即可。(好吧我承认我没有)。

!pip install torch==1.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 1

在这里插入图片描述

18.5.3 运行源码

前去下载源码,并上传到自己本地或云服务器端的 jupyter 中,因为提供的源码也是基于jupyter完成的。

把下载好的数据集解压,新建一个文件input,把解压后的两个 csv 文件放到 input 文件夹中。

在这里插入图片描述

打开 USAD.ipynb,需要选择性运行源码。

在这里插入图片描述
接着后面的内容就一步一步运行就可以了,一般不会出现什么错误。

18.6 源码分析

只摘取其中一部分内容进行解释,我们重点关注 usad.py 文件,这里包括整个网络的实现以及训练测试等。

18.6.1 Encoder 类

这个只是单纯的网络结构,稍微看一下就可以了。

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

18.6.2 Decoder 类

这个也是单纯的网络结构,稍微看一下就可以了。

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

18.6.3 UsadModel

这里应该结合源码中给定的公式,比如说两个网络的损失函数的计算方法。

 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']))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

18.7 总结

论文提到的算法外壳是一个对抗训练,对抗训练的核心是自编码器,并且在这个基础上进行优化,将普通的自编码器拆分成一个编码器和两个解码器,拿其中一个解码器生产假数据去对抗训练另外一个解码器,以提高它对假数据的识别能力。

Smileyan
2022.5.10 22:17

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/999165
推荐阅读
相关标签
  

闽ICP备14008679号