1. 什么是异常检测
异常检测(Outlier Detection),顾名思义,就是识别与正常数据不同,与预期行为差异大的数据。一般情况下,可以把异常检测看成是数据不平衡下的分类问题。
1.1 异常的类别
**点异常:**指的是少数个体实例是异常的,大多数个体实例是正常的,例如正常人与病人的健康指标;
**上下文异常:**又称上下文异常,指的是在特定情境下个体实例是异常的,在其他情境下都是正常的,例如在特定时间下的温度突然上升或下降,在特定场景中的快速信用卡交易;
**群体异常:**指的是在群体集合中的个体实例出现异常的情况,而该个体实例自身可能不是异常,例如社交网络中虚假账号形成的集合作为群体异常子集,但子集中的个体节点可能与真实账号一样正常。
1.2 异常检测任务分类
有监督:训练集的正例和反例均有标签
无监督:训练集无标签
半监督:在训练集中只有单一类别(正常实例)的实例,没有异常实例参与训练
1.3 异常检测场景
- 故障检测
- 物联网异常检测
- 欺诈检测
- 工业异常检测
- 时间序列异常检测
- 视频异常检测
- 日志异常检测
- 医疗日常检测
- 网络入侵检测
2. 异常检测方法
2.1 统计方法
2.1.1 基于统计学的方法
**统计与概率模型:**基于统计学的方法主要是对数据的分布做出假设,并找出在此假设下所定义的“异常”,因此往往会使用极值分析或者假设检验。如假设一维数据服从高斯分布,将距离均值特定范围以外的数据当做异常点处理。推广到高温哦数据后,假设数据各个维度相互独立,并将各个维度上的异常度相加来找出异常点。如果考虑特征间的相关性,也可以用马氏距离(mahalanobis distance)来衡量数据的异常度。可以看出。这类方法最大的优点在于速度快,点由于存在比较强的“假设”效果不一定好。
**异常检测的统计学方法的一般思想:**学习一个拟合给定数据集的生成模型,然后识别该模型低概率区域中的对象,把它们作为异常点。即利用统计学方法建立一个模型,然后考虑对象有多大可能符合该模型。
2.1.2 线性模型
假设数据在低维空间上有嵌入,那么无法、或者在低维空间投射后表现不好的数据可以认为是离群点。典型的如PCA方法,Principle Component Analysis是主成分分析,简称PCA。它的应用场景是对数据集进行降维。降维后的数据能够最大程度地保留原始数据的特征(以数据协方差为衡量标准)。 PCA的原理是通过构造一个新的特征空间,把原数据映射到这个新的低维空间里。PCA可以提高数据的计算性能,并且缓解"高维灾难"。
2.1.3 基于相似度的方法
异常点因为和正常点的分布不同,因此相似度较低,这类算法适用于数据点的聚集程度高、离群点较少的情况。同时,因为相似度算法通常需要对每一个数据分别进行相应计算,所以这类算法通常计算量大,不太适用于数据量大、维度高的数据。
基于相似度的检测方法大致可以分为三类:
- 基于集群(簇)的检测,如DBSCAN等聚类算法。
聚类算法是将数据点划分为一个个相对密集的“簇”,而那些不能被归为某个簇的点,则被视作离群点。这类算法对簇个数的选择高度敏感,数量选择不当可能造成较多正常值被划为离群点或成小簇的离群点被归为正常。因此对于每一个数据集需要设置特定的参数,才可以保证聚类的效果,在数据集之间的通用性较差。聚类的主要目的通常是为了寻找成簇的数据,而将异常值和噪声一同作为无价值的数据而忽略或丢弃,在专门的异常点检测中使用较少。
聚类算法的优缺点:
(1)能够较好发现小簇的异常;
(2)通常用于簇的发现,而对异常值采取丢弃处理,对异常值的处理不够友好;
(3)产生的离群点集和它们的得分可能非常依赖所用的簇的个数和数据中离群点的存在性;
(4)聚类算法产生的簇的质量对该算法产生的离群点的质量影响非常大。 - 基于距离的度量,如k近邻算法。
k近邻算法的基本思路是对每一个点,计算其与最近k个相邻点的距离,通过距离的大小来判断它是否为离群点。在这里,离群距离大小对k的取值高度敏感。如果k太小(例如1),则少量的邻近离群点可能导致较低的离群点得分;如果k太大,则点数少于k的簇中所有的对象可能都成了离群点。为了使模型更加稳定,距离值的计算通常使用k个最近邻的平均距离。
k近邻算法的优缺点:
(1)简单;
(2)基于邻近度的方法需要O(m^2)时间,大数据集不适用;
(3)对参数的选择敏感;
(4)不能处理具有不同密度区域的数据集,因为它使用全局阈值,不能考虑这种密度的变化。 - 基于密度的度量,如LOF(局部离群因子)算法。
局部离群因子(LOF)算法与k近邻类似,不同的是它以相对于其邻居的局部密度偏差而不是距离来进行度量。它将相邻点之间的距离进一步转化为“邻域”,从而得到邻域中点的数量(即密度),认为密度远低于其邻居的样本为异常值。
LOF(局部离群因子)算法的优缺点:
(1)给出了对离群度的定量度量;
(2)能够很好地处理不同密度区域的数据;
(3)对参数的选择敏感。
2.2 集成方法
集成是提高数据挖掘算法精度的常用方法。集成方法将多个算法或多个基检测器的输出结合起来。其基本思想是一些算法在某些子集上表现很好,一些算法在其他子集上表现很好,然后集成起来使得输出更加鲁棒。集成方法与基于子空间方法有着天然的相似性,子空间与不同的点集相关,而集成方法使用基检测器来探索不同维度的子集,将这些基学习器集合起来。
常用的集成方法有Feature bagging,孤立森林等。
2.2.1 孤立森林
孤立森林假设我们用一个随机超平面来切割数据空间,切一次可以生成两个子空间。然后我们继续用随机超平面来切割每个子空间并循环,直到每个子空间只有一个数据点为止。直观上来讲,那些具有高密度的簇需要被切很多次才会将其分离,而那些低密度的点很快就被单独分配到一个子空间了。孤立森林认为这些很快被孤立的点就是异常点。
用四个样本做简单直观的理解,d是最早被孤立出来的,所以d最有可能是异常。
2.3 机器学习
在有标签的情况下,可以使用树模型(gbdt,xgboost等)进行分类,缺点是异常检测场景下数据标签是不均衡的,但是利用机器学习算法的好处是可以构造不同特征。
3. 异常检测常用开源库
Scikit-learn:
Scikit-learn是一个Python语言的开源机器学习库。它具有各种分类,回归和聚类算法。也包含了一些异常检测算法,例如LOF和孤立森林。
官网:https://scikit-learn.org/stable/
PyOD:
**Python Outlier Detection(PyOD)**是当下最流行的Python异常检测工具库,其主要亮点包括:
- 包括近20种常见的异常检测算法,比如经典的LOF/LOCI/ABOD以及最新的深度学习如对抗生成模型(GAN)和集成异常检测(outlier ensemble)
- 支持不同版本的Python:包括2.7和3.5+;支持多种操作系统:windows,macOS和Linux
- 简单易用且一致的API,只需要几行代码就可以完成异常检测,方便评估大量算法
- 使用JIT和并行化(parallelization)进行优化,加速算法运行及扩展性(scalability),可以处理大量数据
——https://zhuanlan.zhihu.com/p/58313521
4. 练习
4.1 Scikit-learn(以孤立森林为例)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import IsolationForest
生成随机数据集
# 获得伪随机数生成器
rng = np.random.RandomState(42)
# 以给定的形状创建一个数组,数组元素来符合标准正态分布N(0,1)
X = 0.3 * rng.randn(100, 2)
# np.r是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等
X_train = np.r_[X + 1, X - 3, X - 5, X + 6]
print('X_train',X_train)
# 再生成一组有规律的数据
X = 0.3 * rng.randn(50, 2)
X_test = np.r_[X + 1, X - 3, X - 5, X + 6]
print('X_test',X_test)
# 生成一组异常数据
# 随机生成-8-8之间(20,2)的出界数组
X_outliers = rng.uniform(low=-8, high=8, size=(20, 2))
print('X_outliers',X_outliers)
生成模型
# 生成模型
clf = IsolationForest(max_samples=100)
clf.fit(X_train)
# 生成训练数据预测值
y_pred_train = clf.predict(X_train)
print('y_pred_train',y_pred_train)
# 生成测试数据预测值
y_pred_test = clf.predict(X_test)
print('y_pred_test',y_pred_test)
# 生成出界数据预测值
y_pred_outliers = clf.predict(X_outliers)
print('y_pred_outliers',y_pred_outliers)
输出结果为:
画图
# 画图
xx, yy = np.meshgrid(np.linspace(-8, 8, 50), np.linspace(-8, 8, 50))
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.title("IsolationForest")
plt.contourf(xx, yy, Z, cmap=plt.cm.Blues_r)
b1 = plt.scatter(X_train[:, 0], X_train[:, 1], c='white')
b2 = plt.scatter(X_test[:, 0], X_test[:, 1], c='green')
c = plt.scatter(X_outliers[:, 0], X_outliers[:, 1], c='red')
plt.axis('tight')
plt.xlim((-8, 8))
plt.ylim((-8, 8))
plt.legend([b1, b2, c],
["training observations",
"new regular observations", "new abnormal observations"],
loc="upper left")
plt.show()
4.2 PyOD(以孤立森林为例)
from __future__ import division
from __future__ import print_function
import os
import sys
from pyod.models.iforest import IForest
from pyod.utils.data import generate_data
from pyod.utils.data import evaluate_print
from pyod.utils.example import visualize
if __name__ == "__main__":
contamination = 0.1 # 离群值百分比
n_train = 200 # 训练点数
n_test = 100 # 测试点数
# 生成样本数据
X_train, y_train, X_test, y_test = \
generate_data(n_train=n_train,
n_test=n_test,
n_features=2,
contamination=contamination,
random_state=42)
# 训练
clf = IForest()
clf.fit(X_train)
# 得到训练数据的预测标签和离群值
y_train_pred = clf.labels_ # 二元标签(0: inliers, 1: outliers)
y_train_scores = clf.decision_scores_
# 获取测试数据的预测值
y_test_pred = clf.predict(X_test) # outlier labels (0 or 1)
y_test_scores = clf.decision_function(X_test) # 异常分数
# 评估并输出结果
print("\nOn Training Data:")
evaluate_print(clf_name, y_train, y_train_scores)
print("\nOn Test Data:")
evaluate_print(clf_name, y_test, y_test_scores)
# 可视化结果
visualize(clf_name, X_train, y_train, X_test, y_test, y_train_pred,
y_test_pred, show_figure=True, save_figure=False)