一、概述
基于深度学习的时间序列异常检测算法,主要可以分为以下这么几种:
- 针对正常数据进行训练建模,然后通过高重构误差来识别异常点,即生成式(Generative)的算法,往往是无监督的,如自编码器(Auto Encoder)类 或者回声状态网络(Echo State Networks)。
- 对数据的概率分布进行建模,然后根据样本点与极低概率的关联性来识别异常点,如DAGMM
- 通过标注数据,告诉模型正常数据点长什么样,异常数据点长什么样,然后通过有监督算法训练分类模型,也称判别式(Discriminative)算法。
在判别式里面,包括时间序列的特征工程和各种有监督算法,还有端到端的深度学习方法。在端到端的深度学习方法里面,包括前馈神经网络,卷积神经网络,或者其余混合模型等常见算法。借用张图大致做一个总结:
二、几种深度学习方法实例
1、基于AutoEncoder的异常检测算法(无监督)
2、Deep SVDD的异常检测方法(无监督)
来自论文Deep One-Class Classification
一句话概括它的思想:使用一个神经网络将样本从输入空间 χ ⊆ R d \chi\subseteq R^{d} χ⊆Rd 映射到输出空间
F
⊆
R
p
F \subseteq R^{p}
F⊆Rp 使得在隐空间上, 正常样本聚集类中心c附近。在测试时, 如果一个样本距离正常类中心c太远, 就认定为异常。如下图所示:
Deep SVDD这个方法是无监督的异常检测方法,充分利用标签信息之后, 我就可以让网络更加明确地区分正常样本和异常样本。理想的情况是想将正常样本和异常样本在输出空间聚成两簇, 如下图:
构建一个简单的神经网络:
encoder = nn.Sequential(
nn.Linear(input_size, 128),
nn.ReLU(inplace=True),
nn.Linear(128, 64),
nn.ReLU(inplace=True),
nn.Linear(64, output_size))
在这个网络中, 设置output_size = 3。这样的设定,一定程度上是为了方便在3维空间中将这些点画出来。所有的正常样本记为Xnor,所有的异常样本Xano,使用上述神经网络 ϕ ( . ; ω ) \phi(.;\omega) ϕ(.;ω) 分别将他们映射到输出空间:
X n o r = ϕ ( X n o r ; ω ) Xnor = \phi(Xnor;\omega) Xnor=ϕ(Xnor;ω)
X a n o = ϕ ( X a n o ; ω ) Xano = \phi(Xano;\omega) Xano=ϕ(Xano;ω)
为了实现上图聚成两簇的效果,设计损失函数如下:
def loss(z_nor, z_ano):
# 计算两个类簇的中心
nor_center = torch.mean(z_nor, dim=0)
ano_center = torch.mean(z_ano, dim=0)
# 两类中心远离,最大化dist
dist = torch.sum((nor_center - ano_center) ** 2)
# 类内聚集,最小化nor_dis + ano_dis
nor_dis = torch.sum((nor_center - z_nor) ** 2)
ano_dis = torch.sum((ano_center - z_ano) ** 2)
# 综合以上两点,整体的loss
loss = (nor_dis + ano_dis) - dist
如果只是按照以上的loss训练,会发现类间距离dist会不断增大,而忽略了nordis和anodis的最小化。为了解决上述问题,引入一个超参Lambda, 调节网络的优化方向:
Lambda = 0.05
if dist.item() > max_dis:
Lambda = 0.9
loss = Lambda * (nor_dis + ano_dis) - (1 - Lambda) * dist
刚开始的时候,Lambda=0.05,网络的优化方向更侧重于将dist变大,将两个类簇分开。当dist大到预设的最大值maxdis, 就调整Lambda=0.9,让网络的优化方向更侧重于减小(nordis+ano_dis), 即让每个点往它的类簇中心靠拢。在训练过程中,观察到一些很有意思的现象:刚开始的时候,所有的样本都聚集在(0,0,0)点附近,两类中心的距离dist自然也是非常接近于0,经过多轮训练之后,网络才开始很明显地将两簇的中心分开。在其中几条kpi中,正常样本和异常样本比较相似,甚至要经过1000轮以上训练之后才能将两类分开。
实际结果与设想的结果不尽相同,但是对异常检测效果影响不大。实际训练和检测的结果如下图所示
- trick1调整不同的滑动窗口大小和选取标签的位置
在实际应用中,异常检测要求及时性。当异常发生时,越快检测出异常越好。所以一般在滑动窗口采样时,都是取窗口的最后一个点的标签作为这个窗口的标签。这样做的话,在检测一个点是否为异常时, 只能根据它本身以及过去的数据来检测。然而在本次比赛中,并不要求及时响应。所以这里我为了提高检检测效果,“不择手段”地引入了未来的数据。根据不同的KPI, 选择不同的滑动窗口大小和标签在窗口里的位置。 - trick2 判断异常的方式
对于测试集的每一个样本,直接根据它到正常簇和异常簇的哪一簇中心的距离更近,它到哪一簇的中心更近,就把它判定为哪一类。后来发现这种判断方式不够用:对于一些训练集里没有出现过的异常样本, 虽然它距离正常簇中心更近,却比大多数正常样本更加远离正常簇中心。基于以上观察, 判断异常的方式修改为:计算每个测试样本到正常簇中心的距离d,确定一个阈值a 当d>a就判定该样本为异常。
这里可以用一种相邻异常点的判断方法,如果异常检测算法在该连续异常区间开始后的 不晚于T 个时间点内检测到了该连续异常区间,就认为此异常检测算法成功地检测到了整段连续异常区间,因此该异常区间内的每一个异常点都算作一次true positive(TP);否则,该连续异常区间内的每一个异常点都算作一次false negative(FN)。就举下图为例,第一行是真实标签。如果模型输出score>=0.5判定为异常, 那么得到的结果是point-wise alert这一行的结果。采用以上的评分方法, 经过调整之后的结果为adjusted alert这一行的结果:
这样的评分方式是有实际意义的。在实际的运维过程中, 一旦异常检测算法检测出异常并产生警报, 这时就有人工介入, 那么后续发生的异常也就有人工干预了,那就相当于这一段连续的异常,都有人工介入处理。
3、基于Transformer掩码重建的时序数据异常检测算法(pyTorch)
3.1 如何重建
对于大部分重建的时间步而言,模型事实上是从整段时间序列中取一段长度来重建下一步(LSTM信息传播的步数是有限的)。重建序列是将一步步的预测拼接而成一段完整的序列,如下图所示。也就是说,这一过程是单向的,仅有重建时间步之前的数据影响了重建的这一时间步。
而 Transformer 与传统的基于 RNN 的模型不同。一次性读取整段时序数据。在 NLP 任务中,模型可以从一个单词的左右文字学习这个单词的作用。因此设想了一种新的数据输入输出的方式,即图 3(b)。获取一段完整序列并分割成小序列时,不是重建这一段小序列的最后一节数据,而是预测这一段小序列的中间一节数据。这样就可以使用重建数据的左右两边的信息来重建。
3.2 模型结构
受 Mask Language Modelling 启发,有人提出Mask Time Series Modeling ,一种基于时序数据的重建。 模型结构如下,掩码时序建模:
对于输入序列X = [ 1, 2,…, ],选择其中将要重建的时间步如 4用[mask]标志代替,从 接 下 来 的 模 型 中 屏 蔽 掉。经 过 一 层 position encoding 进入如上的 Transformer encoder 得到编码后的序列 encoded sequence 也就是上文提到的memory(但与一般的 memory 不同,此时的 memory不含要重建的 4).再经过一个简单的 decoder 之后获取序列。损失函数定义为被屏蔽的 4预测值与真实值之间。换言之, mask time series modeling 是从被 mask 的时间步两边的数据或者说上下文数据重建时间步。这充分利用了 Transformer 无向性的特点。同时,综合利用上下文信息,也可提高重建的精度。
3.3 异常检测效果
时序数据异常检测数据集的标注需要昂贵的专家成本,尤其是详细标注了异常发生的开始和结束时刻的数据集。这类数据集规模通常较小,因此检验算法效果存在一定偶然性。
基于重建误差的异常检测,要求模型在输入正常数据时重建误差较小,输入异常数据时重建误差较大;下面我们可以看一下孟恒宇同学在他的论文里对不同工况的存储数据集进行重建效果的对比(四个工况子集上传感器的数据的Transformer重建结果对比,绿色为预测波形):
可以看出优化后的数据比未优化的数据更平稳波动更小,而异常数据比跟正常数据多了一小段激剧上下波动的异常。不同工况下LSTM与Transformer的绝对误差对比:
上表为四个子集上的重建结果的绝对误差对比。可以看到 Transformer 方法在有异常的数据集上重建的绝对误差更大 ,这 说 明 面 对 异 常 发 生 时Transformer 的表现更鲁棒。优化工况的周期性更明显,本文方法效果显著提高。
不同工况下LSTM与Transformer的训练耗时对比:
可以看出,由于 Transformer 放弃了 LSTM 的序列式推进,其 self-attention 模块完全可以并行,因此用时普遍较少,最多可以节约 80.7%的时间,同时异常检测 F1 score 达到0.78。
需要强调一下Multi-head 机制在这个时序数据任务中几乎没有发挥作用,可能是因为数据维度过少,只有 25 或 55维,而一般的 NLP 任务的词一般会嵌入 512 维度的词向量空间。