内容大致分为两大部分,第一部分是异常值挖掘方法概述,简略介绍异常值挖掘方法的分类及其优缺点。第二部分介绍孤立森林算法(iForest),Isolation Forest 简称 iForest,该算法是周志华在2010年提出的一个异常值检测算法,在工业界很实用,算法效果好,时间效率高。第二部分包括对iForest算法思想、原理、流程的介绍,以及来自sklearn官网上的一个iForest例子实战讲解,并附上代码与注释。
先对变量做一个描述性统计,进而查看哪些数据是不合理的。如箱型图分析,平均值,最大最小值分析,统计学上的3σ法则等。 假设原数据服从某个分布(如高斯分布),然后计算 μ \mu μ 和 σ \sigma σ再计算 ( μ − 3 σ , μ + 3 σ ) \ (\mu-3\sigma,\mu+3\sigma) (μ−3σ,μ+3σ)的区间,最后落在区间之外的数据点就被认为是异常值。
优点:
1.比较直观,方法简单
2.建立在标准的统计学理论之上,当存在充分数据以及选对检验方法时,效果非常好。
缺点:
适合一元场合情况之下,对于高维数据,检验可能性较差。
通常可以在对象之间定义邻近性度量,并且许多移仓检测方法都基于邻近度。异常对象是那些远离大部分其他对象的对象,这一邻域的许多技术都基于距离,称作基于距离的离群点检测技术,代表算法:基于KNN的密度检测算法。
优点:
原理简单,比统计量检验法应用范围更广。
缺点:
1.基于邻近度的方法一般需要O(m^2)时间(其中m是对象个数),这对于大型数据集可能代价过高。
2.该方法对参数的选择是敏感的。
3.不能处理具有不同密度区域的数据集,不能考虑这种密度的变化。
离群点是在低密度区域中的对象。基于密度的离群点检测与基于邻近度的离群点检测密切相关,因为密度通常用邻近度定义。一种常用的定义密度的方法是,定义密度为到k个最近邻的平均距离的倒数。如果该距离小,则密度高,反之亦然。
优点:
1.给出了对象离群程度的定量度量。
2.数据具有不同密度的区域也能够很好地处理。
缺点:
1.有O(m^2)时间复杂度(其中m是对象个数)
2.参数选择困难,评估指标缺乏参照标准。
一种利用聚类检测离群点的方法是丢弃原理其他簇的小簇。这种方法可以与任何聚类技术一起使用,但是需要最小簇大小和小簇与其他簇之间距离的阈值,通常,该过程可以简化为丢弃小于某个最小尺寸的所有簇。
优点
缺点
产生的离群点集和它们的得分可能非常依赖所用的簇的个数和数据总离群点的存在性。聚类算法产生的簇的 质量对该算法产生的离群点的质量影响非常大。
对于如何查找哪些点是否容易被孤立,iForest使用了一套非常高效的策略。假设我们用一个随机超平面来切割数据空间, 切一次可以生成两个子空间,再继续用一个随机超平面来切割每个子空间,循环下去,直到每子空间里面只有一个数据点为止。那些密度很高的簇是可以被切很多次才会停止切割,但是那些密度很低的点很容易很早的就停到一个子空间了。由于切割是随机的,所以需要用集成(ensemble)的方法来得到一个收敛值,即反复从头开始切,然后平均每次切的结果,且随着森林中树木棵树增多,收敛越快。孤立森林为给定的数据集构建了一组iTree,较少的异常实例会导致树结构中的较短路径,而且具有可区分的属性值的实例更可能在早期分区。 因此,当一棵由随机树木组成的森林为某些特定点共同产生较短的路径长度时,则它们很可能是异常的。通过集成算法求出平均路径长度之后,异常是那些在iTree上具有较短平均路径长度的实例。
如图2.1所示,异常更容易被隔离,因此路径长度较短。正常点xi需要隔离十二个随机分区,xo异常仅需要隔离四个分区,显然异常点更早被区分出来。
递归分区可以用树结构表示,所以隔离一个点所需的分区数等于从根节点到终止节点的路径长度。
孤立森林算法总共分两步:
训练 iForest:从训练集中进行采样,构建孤立树,对森林中的每棵孤立树进行测试,记录路径长度;
2.计算异常分数:根据异常分数计算公式,计算每个样本点的 anomaly score。
获得t个iTree之后,iForest 训练就结束,然后就可以用生成的iForest来评估测试数据。对于一个训练数据x,我们令其遍历每一棵iTree,然后计算x最终落在每个树第几层(x在树的高度)。然后我们可以得出x在每棵树的高度平均值,即 平均路径长度(the average path length over t iTrees),通过平均路径长度可以得到每个实例的异常分数。
如图4.2所示,a,b,c,d四个实例中,d的平均路径最短,最早被区分出来,可能是异常值点。
平均路径长度:
c ( n ) = 2 H ( n − 1 ) − ( 2 ( n − 1 ) / n ) (1) c(n)=2H(n-1)-(2(n-1)/n)\tag{1} c(n)=2H(n−1)−(2(n−1)/n)(1)
其中, n n n是样本量, c ( n ) c(n) c(n)是该样本量下的平均路径长度, H ( i ) H(i) H(i)是谐波数,可以被$y ln(i) + 0.5772156649 $ (Euler
常数)估计出来。
异常分数计算公式如下:
s ( x , n ) = 2 ( − E ( h ( x ) ) c ( n ) ) (2) s(x,n)=2^(\frac{-E(h(x))}{c(n)})\tag{2} s(x,n)=2(c(n)−E(h(x)))(2)
其中, h ( x ) h(x) h(x)为点 x x x从itree的根节点到叶子节点所经过的路径长度, E ( h ( x ) ) E(h(x)) E(h(x))是整片森林里点x的平均路径长度 。最终异常分数值取值范围在0到1之间,被判为异常值的实例异常分数接近于1。
class sklearn.ensemble.IsolationForest(*, n_estimators=100, max_samples='auto', contamination='auto', max_features=1.0, bootstrap=False, n_jobs=None, behaviour='deprecated', random_state=None, verbose=0, warm_start=False)
参数 | 数据类型 | 释意 |
---|---|---|
n_estimators | int | 孤立树的数量,默认值是100 |
max_samples | int or float | 训练每颗孤立树时的采样数,可以是整数值,也可以是小数比例值,默认值是256 |
contamination | float in (0.,0.5) | 数据集中异常值的比例,用于定义决策函数的阀值,默认值是0.1 |
max_features | int or float | 用于训练每个孤立树的特征数,默认值是1 |
bootstrap | boolean | 取值为True:每颗孤立树都在放回条件下进行随机采样,False:不放回采样,默认值是False |
behaviour | str(new/old) | 决策函数版本,默认值是old |
random_state | int | RandomState instance or None 如果是int, random_state是随机数生成器使用的种子;如果是RandomState instance,random_state是随机数生成器;如果None,随机数生成器是np.random使用的RandomState实例。默认值是None |
verbose | int, optional | 控制树构建过程的详细程度,默认值是None |
1 .decision_function(self, X)
得出输入样本的异常分数, (n_samples,)(到底是一个平均值,还是所有树的异常值,尚且不清楚)由所有基分类器的异常分数取平均值而得。X是样本矩阵(shape=(n_samples,n_features)),输出值越低,表明异常程度越大。分数为负时,代表离群值。
2 .fit(self, X, y=None, sample_weight=None)
训练数据。X是样本矩阵,sample_weight是长度为n_samples的一维数组,为每条用于训练的数据分配一个明确的权重。有时异常值在样本中数量太少了,赋予较大的权数可以平衡两部分数据,让异常值不容易被淹没。
3 .fit_predict(self, X, y=None)
基于用X训练出的异常值估计器,预测出X的标签(是否异常,标签为-1的样本为异常值 )
4 .get_params(self, deep=True)
得出训练出的估计器的参数和作为估计器的子对象。
5 .predict(self, X)
判断一个输入样本是否是异常值,返回-1,说明该实例是异常个体。
6. score_samples(self, X)
计算异常得分
该实例思路:
自创5个不同分布的数据集,每种数据集有不同的密度,有的是均匀散布,有的呈单峰或双峰高斯分布。选取四种常用的异常值检验模型,分别是:Robust covariance、One-Class SVM、Isolation Forest、Local Outlier Factor(LOF),用不同类型数据集对比这四种算法处理多模式数据的能力。
对于每个数据集,将生成15%的样本作为随机均匀噪声。可视化结果中,正常值和异常值之间的决策边界以黑色显示,但局部离群值因子(LOF)除外,因为当用于离群值检测时,它没有适用于新数据的预测方法。
该实例代码:
import time
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_moons, make_blobs
from sklearn.covariance import EllipticEnvelope
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
print(__doc__)
#自定义可视化线形效果
matplotlib.rcParams['contour.negative_linestyle'] = 'solid'
#设定样本,总量为300,其中15%是异常值
n_samples = 300
outliers_fraction = 0.15
n_outliers = int(outliers_fraction * n_samples)
n_inliers = n_samples - n_outliers
# 使用四种异常值检验方法(分别是:Robust covariance、One-Class SVM、Isolation Forest、Local Outlier Factor)
#把四种方法的模型放在一个列表里面,之后用for循环遍历这四种方法来构建异常检验器。
anomaly_algorithms = [
("Robust covariance", EllipticEnvelope(contamination=outliers_fraction)),
("One-Class SVM", svm.OneClassSVM(nu=outliers_fraction, kernel="rbf",
gamma=0.1)),
("Isolation Forest", IsolationForest(contamination=outliers_fraction,
random_state=42)),
("Local Outlier Factor", LocalOutlierFactor(
n_neighbors=35, contamination=outliers_fraction))]
# 创造数据集,一共构造五种分布情况不同数据集,比较四种方法在不同数据集上的效果。(运行结果中每一行都是一种数据集)
blobs_params = dict(random_state=0, n_samples=n_inliers, n_features=2)
datasets = [
make_blobs(centers=[[0, 0], [0, 0]], cluster_std=0.5,
**blobs_params)[0],
make_blobs(centers=[[2, 2], [-2, -2]], cluster_std=[0.5, 0.5],
**blobs_params)[0],
make_blobs(centers=[[2, 2], [-2, -2]], cluster_std=[1.5, .3],
**blobs_params)[0],
4. * (make_moons(n_samples=n_samples, noise=.05, random_state=0)[0] -
np.array([0.5, 0.25])),
14. * (np.random.RandomState(42).rand(n_samples, 2) - 0.5)]
# 将数据网格化,便与画等高线图
xx, yy = np.meshgrid(np.linspace(-7, 7, 150),
np.linspace(-7, 7, 150))
#设定画布大小以及子图尺寸
plt.figure(figsize=(len(anomaly_algorithms) * 2 + 3, 12.5))
plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05,
hspace=.01)
plot_num = 1
rng = np.random.RandomState(42)
#下面是一个嵌套循环,对每一个数据集,用四种方法异常检验,训练数据,并且标记异常值
for i_dataset, X in enumerate(datasets):
#往数据集中加入异常值
X = np.concatenate([X, rng.uniform(low=-6, high=6,
size=(n_outliers, 2))], axis=0)#合并数据
#遍历四种方法对X进行异常检验
for name, algorithm in anomaly_algorithms:
t0 = time.time()
algorithm.fit(X)
t1 = time.time()
plt.subplot(len(datasets), len(anomaly_algorithms), plot_num)
#在每一列第一幅子图上面显示对应的算法名称
if i_dataset == 0:
plt.title(name, size=18)
# 异常值标记,.fit_predict()方法,显示个体是否是异常个体,返回-1,即异常个体
#分成两部分进行训练以及预测,LOF的方法和其它三种略有不同。
if name == "Local Outlier Factor":
y_pred = algorithm.fit_predict(X)
else:
y_pred = algorithm.fit(X).predict(X)
# plot the levels lines and the points
if name != "Local Outlier Factor": # LOF没有实现预测
#.ravel()方法把多维数据变成一维数据
#np.c[]左右连接两个矩阵
Z = algorithm.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
#画等高线图,Z是整张网格数据(2维)每个点对应的预测值,等高线画出异常值和正常值的分界线
plt.contour(xx, yy, Z, levels=[0], linewidths=2, colors='black')
#设定散点图点的颜色
colors = np.array(['#377eb8', '#ff7f00'])
#画散点图,异常值是蓝色点
plt.scatter(X[:, 0], X[:, 1], s=10, color=colors[(y_pred + 1) // 2])
#设定子图尺寸
plt.xlim(-7, 7)
plt.ylim(-7, 7)
plt.xticks(())
plt.yticks(())
plt.text(.99, .01, ('%.2fs' % (t1 - t0)).lstrip('0'),
transform=plt.gca().transAxes, size=15,
horizontalalignment='right')
plot_num += 1
plt.savefig('./iforest.jpg')
plt.show()
结果分析:
Robust covariance:
基于方差的鲁棒的异常检测模型,在sklearn中的模块是covariance.ELLipticEnvelope。该模型假设正常样本都服从高斯分布。它假定数据呈高斯分布,根据数据做一个鲁棒(足够稳定,绝不过拟合)的协方差估计,然后学习到一个包围中心样本点并忽视离群点的椭圆(由上图第一列可以看出)。当数据不是单峰时,它会降低性能。
OneClassSVM
SVM通过把原始空间通过核方法(kernel method)映射到一个特征空间中,进而使不同类别的样本在特征空间中很容易分开。One-class SVM也是一样的,先用一个核方法,比如高斯核(RBF kernel)将原始空间映射到特征空间,然后在特征空间中画一个圈,圈内是正常值,圈外是异常值。在高维数据上,或者没有对基础数据进行任何分布假设的情况下,用这种方法风险比较大。由上图第二列可见,One-class SVM对于均匀散布的第五种数据集比较容易过拟合。
LocalOutlierFactor
LOF在sklearn里面的模块是sklearn.neighbors.LocalOutlierFactor。LOF通过观察数据分布的密度来检测异常,密度小的地方往往是异常值。LOF算法从两个维度判断异常点:1.目标样本点的可达密度小2.目标样本的K近邻的可达密度都较大。它的优势是能解决密度分布不同情况下的异常值检验。第三个数据集也显示了这一优势。
Isolation Forest
孤立森林在5种数据集上的表现都不错,也能很好的处理数据分布密度不同的情况。
大。由上图第二列可见,One-class SVM对于均匀散布的第五种数据集比较容易过拟合。
LocalOutlierFactor
LOF在sklearn里面的模块是sklearn.neighbors.LocalOutlierFactor。LOF通过观察数据分布的密度来检测异常,密度小的地方往往是异常值。LOF算法从两个维度判断异常点:1.目标样本点的可达密度小2.目标样本的K近邻的可达密度都较大。它的优势是能解决密度分布不同情况下的异常值检验。第三个数据集也显示了这一优势。
Isolation Forest
孤立森林在5种数据集上的表现都不错,也能很好的处理数据分布密度不同的情况。