异常检测分为离群点检测(outlier detection) 以及奇异值检测(novelty detection) 两种.
- 离群点检测:适用于训练数据中包含异常值的情况,例如上述所提及的情况。离群点检测模型会尝试拟合训练数据最集中的区域,而忽略异常数据。
- 奇异值检测:适用于训练数据不受异常值的污染,目标是去检测新样本是否是异常值。 在这种情况下,异常值也被称为奇异点。
基于时间序列分析 同比环比
-
基于同比和环比
-
适合数据呈周期性规律的场景中。例如:
- 监控APP的DAU的环比和同比,及时发现DAU上涨或者下跌
- 监控实时广告点击、消耗的环比和同比,及时发现变化
-
https://www.cnblogs.com/zhengzhicong/p/12916915.html)
基于统计 单特征且符合正态分布
- 在正态分布中,μ-3σ<=x<=μ+3σ的区域包含了绝大部分数据,可以以此为参考,调整ε的值:
- 值得一提的是,如果有些特征不符合高斯分布,可以通过一些函数变换(Z-score、Box-Cox),使其符合正态分布,再使用基于统计的方法。
基于统计 箱线图
- 箱线图算法不需要数据服从特定分布。该方法需要先计算第一四分位数Q1(25%)和第三四分位数Q3(75%)。
- 令IQR=Q3-Q1,然后算出异常值边界点Q3+λIQR和Q1- λIQR,通常λ取1.5
基于聚类 DBSCAN
-
以每个点为中心,设定邻域及邻域内需要有多少个点,如果样本点大于指定要求,则认为该点与邻域内的点属于同一类,如果小于指定值,若该点位于其它点的邻域内,则属于边界点。
-
设定两个参数,eps表示聚类点为中心划定邻域,min_samples表示每个邻域内需要多少个样本点。
-
def filter_data(data0, params): from sklearn.cluster import DBSCAN from sklearn import metrics from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaler.fit(data0) data = scaler.transform(data0) #eps:半径,表示以给定点P为中心的圆形邻域的范围 #min_samples:以点P为中心的邻域内最少点的数量 #如果满足,以点P为中心,半径为EPS的邻域内点的个数不少于MinPts,则称点P为核心点 eps, min_samples = params # eps为领域的大小,min_samples为领域内最小点的个数 model = DBSCAN(eps=eps, min_samples=min_samples) # 构造分类器 model.fit(data) # 拟合 labels = model.labels_ # 获取类别标签,-1表示未分类 # 获取其中的core points core_sample_indices_ core_indices = np.zeros_like(labels, dtype=bool) # 生成数据类型和数据shape和指定array一致的变量 core_indices[model.core_sample_indices_] = True # model.core_sample_indices_ border point位于labels中的下标 core_point = data[core_indices] # 获取非异常点 两个类似 normal_point = data0[labels>=0] return normal_point
基于树模型 孤立森林
-
孤立森林是一个基于Ensemble的快速离群点检测方法,适用于连续数据的异常检测,通过对样本点的孤立来检测异常值。具体来说,该算法利用孤立树(iTree)的二叉搜索树结构来孤立样本。由于异常值的数量较少且与大部分样本的疏离性,因此,异常值会被更早的孤立出来,也即异常值会距离iTree的根节点更近,而正常值则会距离根节点有更远的距离。此外,相较于LOF,K-means等传统算法,孤立森林算法对高纬数据有较好的鲁棒性。
-
iForest 由 t 个 iTree 组成,每个 iTree 是一个二叉树结构。该算法大致可以分为两个阶段,第一个阶段我们需要训练出 t 颗孤立树,组成孤立森林。随后我们将每个样本点带入森林中的每棵孤立树,计算平均高度,之后再计算每个样本点的异常值分数
-
第一阶段
- 从训练数据中随机选择Ψ个点样本点作为样本子集,放入树的根节点。
- 随机指定一个维度(特征),在当前节点数据中随机产生一个切割点 p(切割点产生于当前节点数据中指定维度的最大值和最小值之间)。
- 以此切割点生成了一个超平面,然后将当前节点数据空间划分为2个子空间:把指定维度里小于 p 的数据放在当前节点的左子节点,把大于等于 p 的数据放在当前节点的右子节点。
- 在子节点中递归步骤,不断构造新的孩子节点,直到子节点中只有一个数据(无法再继续切割)或子节点已到达限定高度。
- 循环(1)至(4),直至生成 t 个孤立树iTree。
-
第二阶段:
- 获得t个iTree之后,iForest 训练就结束,然后我们可以用生成的iForest来评估测试数据了。对于每一个数据点 xi,令其遍历每一颗孤立树(iTree),计算点 xi 在森林中的平均高度好h(xi),对所有点的平均高度做归一化处理
-
from sklearn.ensemble import IsolationForest X = [[-1.1], [0.3], [0.5], [100]] lf = IsolationForest(random_state=0).fit(X) clf.predict([[0.1], [0], [90]]) array([ 1, 1, -1])
-
-
iForest具有线性时间复杂度
-
iForest不适用于特别高维的数据。由于每次切数据空间都是随机选取一个维度,建完树后仍然有大量的维度信息没有被使用,导致算法可靠性降低。高维空间还可能存在大量噪音维度或无关维度(irrelevant attributes)
-
iForest仅对Global Anomaly敏感,即全局稀疏点敏感,不擅长处理局部的相对稀疏点 (Local Anomaly)。
-
# 参考https://blog.csdn.net/ye1215172385/article/details/79762317 # 官方例子https://scikit-learn.org/stable/auto_examples/ensemble/plot_isolation_forest.html#sphx-glr-auto-examples-ensemble-plot-isolation-forest-py import numpy as np import matplotlib.pyplot as plt from sklearn.ensemble import IsolationForest # 设置随机种子 rng = np.random.RandomState(42) # 构造训练样本 n_samples = 200 #样本总数 outliers_fraction = 0.25 #异常样本比例 n_inliers = int((1. - outliers_fraction) * n_samples) n_outliers = int(outliers_fraction * n_samples) X = 0.3 * rng.randn(n_inliers // 2, 2) X_train = np.r_[X + 2, X - 2] #正常样本 X_train = np.r_[X_train, np.random.uniform(low=-6, high=6, size=(n_outliers, 2))] #正常样本加上异常样本 # 构造模型并拟合 # max_samples 构造一棵树使用的样本数,输入大于1的整数则使用该数字作为构造的最大样本数目, # contamination 多少比例的样本可以作为异常值 clf = IsolationForest(max_samples=n_samples, random_state=rng, contamination=outliers_fraction) clf.fit(X_train) # 计算得分并设置阈值 scores_pred = clf.decision_function(X_train) threshold = np.percentile(scores_pred, 100 * outliers_fraction) #根据训练样本中异常样本比例,得到阈值,用于绘图 X_train_predict1 = X_train[clf.predict(X_train)==1] X_train_predict2 = X_train[scores_pred>=threshold,:]
基于线性模型 OneClassSVM
from sklearn import svm
X_train = X_train_demo.values
# 构造分类器
# nu :错误和支持度的下界 0.5默认
# kernal:内核类型 rbf默认
clf = svm.OneClassSVM(nu=0.4, kernel="rbf", )
clf.fit(X_train)
# 预测,结果为-1或者1
labels = clf.predict(X_train)
# 分类分数
score = clf.decision_function(X_train) # 获取置信度
# 获取正常点
X_train_normal = X_train[labels>0]
基于密度 LOF
-
一个样本点周围的样本点所处位置的平均密度比上该样本点所在位置的密度。比值越大于1,则该点所在位置的密度越小于其周围样本所在位置的密度。
-
from sklearn.neighbors import LocalOutlierFactor # 构造分类器 ## 25个样本点为一组,异常值点比例为0.2 clf = LocalOutlierFactor(n_neighbors=25, contamination=0.2) # 预测,结果为-1或者1 labels = clf.fit_predict(X_train) # 获取正常点 X_train_normal = X_train[labels>0]