异常的分类
时间序列的异常检测问题通常表示为相对于某些标准信号或常见信号的离群点。虽然有很多的异常类型,但是我们只关注业务角度中最重要的类型,比如意外的峰值、下降、趋势变化以及等级转换(level shifts)。
常见的异常有如下几种:
革新性异常:innovational outlier (IO),造成离群点的干扰不仅作用于$X_T$,而且影响T时刻以后序列的所有观察值。
附加性异常:additive outlier (AO),造成这种离群点的干扰,只影响该干扰发生的那一个时刻T上的序列值,而不影响该时刻以后的序列值。
水平移位异常:level shift (LS),造成这种离群点的干扰是在某一时刻T,系统的结构发生了变化,并持续影响T时刻以后的所有行为,在数列上往往表现出T时刻前后的序列均值发生水平位移。
暂时变更异常temporary change (TC):造成这种离群点的干扰是在T时刻干扰发生时具有一定初始效应,以后随时间根据衰减因子的大小呈指数衰减。
上面的解释可能不太容易理解,我们结合图片来看一下:
通常,异常检测算法应该将每个时间点标记为异常/非异常,或者预测某个点的信号,并衡量这个点的真实值与预测值的差值是否足够大,从而将其视为异常。使用后面的方法,你将能够得到一个可视化的置信区间,这有助于理解为什么会出现异常并进行验证。
常见异常检测方法
从分类看,当前发展阶段的时序异常检测算法和模型可以分为一下几类:
统计模型:优点是复杂度低,计算速度快,泛化能力强悍。因为没有训练过程,即使没有前期的数据积累,也可以快速的投入生产使用。缺点是准确率一般。但是这个其实是看场景的,并且也有简单的方法来提高业务层面的准确率。这个后面会提到。
机器学习模型:鲁棒性较好,准确率较高。需要训练模型,泛化能力一般。
深度学习模型:普遍需要喂大量的数据,计算复杂度高。整体看,准确性高,尤其是近段时间,强化学习的引入,进一步巩固其准确性方面的领先优势。
3-Sigma
3-Sigma原则又称为拉依达准则,该准则定义如下:假设一组检测数据只含有随机误差,对原始数据进行计算处理得到标准差,然后按一定的概率确定一个区间,认为误差超过这个区间的就属于异常值。
使用3-Sigma的前提是数据服从正态分布,满足这个条件之后,在3-Sigma范围$(\mu – 3\sigma ,\mu + 3\sigma)$内99.73%的为正常数据,其中$\sigma$代表标准差,$\mu$代表均值,$x=\mu$为图形的对称轴。下面是3-Sigma的Python实现: import numpy as np
def three_sigma(df_col):
'''
df_col:DataFrame数据的某一列
'''
rule = (df_col.mean() - 3 * df_col.std() > df_col) | (df_col.mean() + 3 * df_col.std() < df_col)
index = np.arange(df_col.shape[0])[rule]
out_range = df_col.iloc[index]
return out_range
对于异常值检测出来的结果,有多种处理方式,如果是时间序列中的值,那么我们可以认为这个时刻的操作属于异常的;如果是将异常值检测用于数据预处理阶段,处理方法有以下四种:
删除带有异常值的数据;
将异常值视为缺失值,交给缺失值处理方法来处理;
用平均值进行修正;
当然我们也可以选择不处理。
Grubbs测试
Grubbs’Test为一种假设检验的方法,常被用来检验服从正太分布的单变量数据集(univariate data set)Y 中的单个异常值。若有异常值,则其必为数据集中的最大值或最小值。原假设与备择假设如下:
$H_0$: 数据集中没有异常值
$H_1$: 数据集中有一个异常值
Grubbs’ Test检验假设的所用到的检验统计量(test statistic)为:
$$G = \frac{\max |Y_i – \overline{Y}|}{s}$$
其中,$\overline{Y}$为均值,s为标准差。原假设$H_0$被拒绝,当检验统计量满足以下条件:
$$G > \frac{(N-1)}{\sqrt{N}}\sqrt{\frac{ (t_{\alpha/(2N), N-2})^2}{N-2 + (t_{\alpha/(2N), N-2})^2}}$$
其中,$N$为数据集的样本数,$t_{\alpha/(2N), N-2}$为显著度(significance level)等于$\alpha/(2N)$、自由度(degrees of freedom)等于$N-2$的t分布临界值。实际上,Grubbs’ Test可理解为检验最大值、最小值偏离均值的程度是否为异常。
使用Grubbs测试需要总体是正态分布的。算法流程:
样本从小到大排序
求样本的mean和dev
计算min/max与mean的差距,更大的那个为可疑值
求可疑值的z-score (standard score),如果大于Grubbs临界值,那么就是outlier
Grubbs临界值可以查表得到,它由两个值决定:检出水平$\alpha$(越严格越小),样本数量n,排除outlier,对剩余序列循环做 1-4 步骤。由于这里需要的是异常判定,只需要判断tail_avg是否outlier即可。
使用示例: from outliers import smirnov_grubbs as grubbs
print(grubbs.test([8, 9, 10, 1, 9], alpha=0.05))
print(grubbs.min_test_outliers([8, 9, 10, 1, 9], alpha=0.05))
print(grubbs.max_test_outliers([8, 9, 10, 1, 9], alpha=0.05))
print(grubbs.max_test_indices([8, 9, 10, 50, 9], alpha=0.05))
ESD(generalized ESD test)
在现实数据集中,异常值往往是多个而非单个。为了将Grubbs’ Test扩展到k个异常值检测,则需要在数据集中逐步删除与均值偏离最大的值(为最大值或最小值),同步更新对应的t分布临界值,检验原假设是否成立。基于此,Rosner提出了Grubbs’ Test的泛化版ESD(Extreme Studentized Deviate test)。算法流程如下:
计算与均值偏离最远的残差,注意计算均值时的数据序列应是删除上一轮最大残差样本数据后。$R_j = \frac{\max_i |Y_i – \overline{Y’}|}{s}, 1 \leq j \leq k$
计算临界值(critical value).$\lambda_j = \frac{(n-j) * t_{p,n-j-1}}{\sqrt{(n-j-1+t_{p,n-j-1}^2)(n-j+1)}}, 1 \leq j \leq k$
检验原假设,比较检验统计量与临界值;若$R_i > \lambda_j$,则原假设$H_0$不成立,该样本点为异常点
重复以上步骤k次至算法结束。
使用方法: import numpy as np
import matplotlib.pylab as plt
from PyAstronomy import pyasl
# Convert data given at:
# http://www.itl.nist.gov/div898/handbook/eda/section3/eda35h3.htm
# to array.
x = np.array([float(x) for x in "-0.25 0.68 0.94 1.15 1.20 1.26 1.26 1.34 1.38 1.43 1.49 1.49 \
1.55 1.56 1.58 1.65 1.69 1.70 1.76 1.77 1.81 1.91 1.94 1.96 \
1.99 2.06 2.09 2.10 2.14 2.15 2.