2.7. 新颖性检测和异常值检测#

许多应用需要能够判断新的观测值是否属于与现有观测值相同的分布(它是内点),或者是否应该被认为是不同的(它是异常值)。通常,此功能用于清理真实数据集。必须区分两个重要的方面:

异常值检测:

训练数据包含异常值,这些异常值被定义为远离其他观测值的观测值。因此,异常值检测估计器尝试拟合训练数据最集中的区域,忽略异常观测值。

新颖性检测:

训练数据未被异常值污染,我们感兴趣的是检测新的观测值是否是异常值。在这种情况下,异常值也称为新颖性。

异常值检测和新颖性检测都用于异常检测,其中人们感兴趣的是检测异常或不寻常的观测值。异常值检测也称为无监督异常检测,新颖性检测称为半监督异常检测。在异常值检测的上下文中,异常值/异常点不能形成密集的集群,因为可用的估计器假设异常值/异常点位于低密度区域。相反,在新颖性检测的上下文中,新颖性/异常点可以形成密集的集群,只要它们位于训练数据的低密度区域,在这种情况下被认为是正常的。

scikit-learn 项目提供了一套机器学习工具,可用于新颖性检测或异常值检测。这种策略通过以无监督的方式从数据中学习的对象来实现。

estimator.fit(X_train)

新的观测值可以通过 predict 方法分类为内点或异常值。

estimator.predict(X_test)

内点标记为 1,而异常值标记为 -1。predict 方法使用估计器计算的原始评分函数的阈值。此评分函数可以通过 score_samples 方法访问,而阈值可以通过 contamination 参数控制。

decision_function 方法也根据评分函数定义,负值表示异常值,非负值表示内点。

estimator.decision_function(X_test)

请注意,neighbors.LocalOutlierFactor 默认情况下不支持 predictdecision_functionscore_samples 方法,而只支持 fit_predict 方法,因为该估计器最初旨在用于异常值检测。训练样本的异常分数可以通过 negative_outlier_factor_ 属性访问。

如果您确实想将neighbors.LocalOutlierFactor用于新奇检测,即预测新未见数据的标签或计算异常分数,您可以在拟合估计器之前,将估计器实例化,并将novelty参数设置为True。在这种情况下,fit_predict不可用。

警告

局部异常因子 (Local Outlier Factor) 的新奇检测

novelty设置为True时,请注意,您只能对新的未见数据使用predictdecision_functionscore_samples,而不能用于训练样本,因为这会导致错误的结果。predict的结果将与fit_predict的结果不同。训练样本的异常分数始终可以通过negative_outlier_factor_属性访问。

neighbors.LocalOutlierFactor的行为总结在下表中。

方法

异常值检测

新奇检测

fit_predict

可用

不可用

predict

不可用

仅用于新数据

decision_function

不可用

仅用于新数据

score_samples

使用negative_outlier_factor_

仅用于新数据

negative_outlier_factor_

可用

可用

2.7.1. 异常检测方法概述#

scikit-learn 中异常检测算法的比较。局部异常因子 (LOF) 没有显示黑色的决策边界,因为它在用于异常值检测时没有可应用于新数据的 predict 方法。

../_images/sphx_glr_plot_anomaly_comparison_001.png

ensemble.IsolationForestneighbors.LocalOutlierFactor在本文考虑的数据集上表现良好。svm.OneClassSVM已知对异常值敏感,因此在异常值检测方面表现不佳。也就是说,高维的异常值检测,或者在对内在数据分布没有任何假设的情况下,是非常具有挑战性的。svm.OneClassSVM仍然可以用于异常值检测,但是需要微调其超参数nu来处理异常值并防止过拟合。linear_model.SGDOneClassSVM提供了一种线性One-Class SVM的实现,其在样本数量上的复杂度为线性。此实现在此处与内核逼近技术一起使用,以获得与svm.OneClassSVM(默认使用高斯内核)类似的结果。最后,covariance.EllipticEnvelope假设数据服从高斯分布并学习一个椭圆。有关不同估计器的更多详细信息,请参阅示例比较用于玩具数据集异常检测的异常检测算法和以下章节。

示例

2.7.2. 新奇检测#

考虑一个数据集,其中包含来自由\(p\)个特征描述的相同分布的\(n\)个观测值。现在考虑我们将另一个观测值添加到该数据集中。新的观测值与其他观测值如此不同,以至于我们可以怀疑它是否正常吗?(即,它是否来自相同的分布?)或者相反,它与其他观测值如此相似,以至于我们无法将其与原始观测值区分开来?这就是新奇检测工具和方法所解决的问题。

总的来说,该方法旨在学习一个粗略的、封闭的边界,以界定初始观测值分布的轮廓,该轮廓绘制在嵌入的\(p\)维空间中。然后,如果进一步的观测值位于边界限定的子空间内,则认为它们与初始观测值来自同一总体。否则,如果它们位于边界之外,我们可以说它们是不正常的,并且对我们的评估有一定的置信度。

Schölkopf等人为了这个目的引入了One-Class SVM,并在支持向量机模块中的svm.OneClassSVM对象中实现了它。它需要选择一个核函数和一个标量参数来定义边界。通常选择RBF核,尽管不存在设置其带宽参数的精确公式或算法。这是scikit-learn实现中的默认设置。nu参数,也称为One-Class SVM的边缘,对应于在边界之外找到新的但正常的观测值的概率。

参考文献

示例

../_images/sphx_glr_plot_oneclass_001.png

2.7.2.1. One-Class SVM的扩展#

One-Class SVM的在线线性版本在linear_model.SGDOneClassSVM中实现。此实现与样本数量线性缩放,并且可以与核近似一起使用,以近似具有核的svm.OneClassSVM的解,该解的复杂度在最佳情况下为样本数量的二次方。有关更多详细信息,请参见第在线One-Class SVM节。

示例

2.7.3. 异常值检测#

异常值检测与新奇性检测类似,其目标是将正常观测值的核心与一些污染性观测值(称为*异常值*)分开。然而,在异常值检测的情况下,我们没有一个干净的数据集来表示可以用来训练任何工具的正常观测值总体。

2.7.3.1. 拟合椭圆包络#

执行异常值检测的一种常见方法是假设正常数据来自已知分布(例如,数据服从高斯分布)。根据这个假设,我们通常试图定义数据的“形状”,并将远离拟合形状的观测值定义为异常值。

scikit-learn提供了一个对象covariance.EllipticEnvelope,它将稳健的协方差估计拟合到数据,从而将椭圆拟合到中心数据点,忽略中心模式之外的点。

例如,假设内点数据服从高斯分布,它将以稳健的方式估计内点位置和协方差(即不受异常值的影响)。由此估计获得的马氏距离用于推导出异常值的度量。此策略如下所示。

../_images/sphx_glr_plot_mahalanobis_distances_001.png

示例

参考文献

  • Rousseeuw, P.J., Van Driessen, K. “A fast algorithm for the minimum covariance determinant estimator” Technometrics 41(3), 212 (1999)

2.7.3.2. 隔离森林#

在高维数据集中执行异常值检测的一种有效方法是使用随机森林。ensemble.IsolationForest通过随机选择一个特征,然后随机选择所选特征的最大值和最小值之间的分割值来“隔离”观测值。

由于递归划分可以用树结构表示,因此隔离样本所需的分割次数等同于从根节点到终止节点的路径长度。

此路径长度在这样的随机树的森林中取平均值,是正常性和我们的决策函数的度量。

随机划分会为异常值产生明显更短的路径。因此,当随机树的森林共同为特定样本产生更短的路径长度时,它们很可能就是异常值。

ensemble.IsolationForest的实现基于tree.ExtraTreeRegressor的集成。根据Isolation Forest的原始论文,每棵树的最大深度设置为\(\lceil \log_2(n) \rceil\),其中\(n\)是用于构建树的样本数量(有关详细信息,请参见 (Liu et al., 2008))。

此算法如下所示。

../_images/sphx_glr_plot_isolation_forest_003.png

ensemble.IsolationForest支持warm_start=True,允许您向已拟合模型添加更多树。

>>> from sklearn.ensemble import IsolationForest
>>> import numpy as np
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [0, 0], [-20, 50], [3, 5]])
>>> clf = IsolationForest(n_estimators=10, warm_start=True)
>>> clf.fit(X)  # fit 10 trees  
>>> clf.set_params(n_estimators=20)  # add 10 more trees  
>>> clf.fit(X)  # fit the added trees  

示例

参考文献

  • Liu, Fei Tony, Ting, Kai Ming and Zhou, Zhi-Hua. “Isolation forest.” Data Mining, 2008. ICDM’08. Eighth IEEE International Conference on.

2.7.3.3. 局部异常因子#

另一种在中等高维数据集上进行异常值检测的有效方法是使用局部异常因子 (LOF) 算法。

neighbors.LocalOutlierFactor (LOF) 算法计算一个反映观测异常程度的分数(称为局部异常因子)。它衡量给定数据点与其邻居相比的局部密度偏差。其思想是检测局部密度比其邻居低得多的样本。

实际上,局部密度是从 k 近邻获得的。观测的 LOF 分数等于其 k 近邻的平均局部密度与其自身局部密度的比率:正常实例的局部密度与其邻居的局部密度相似,而异常数据的局部密度要小得多。

考虑的邻居数量 k(别名参数 n_neighbors)通常选择为:1)大于集群必须包含的最小对象数,以便其他对象可以相对于此集群成为局部异常值;2)小于可能成为局部异常值的附近对象的最大数量。实际上,此类信息通常不可用,而将 n_neighbors=20 设置为一般情况下的工作效果很好。当异常值比例较高(即大于 10%,如下例所示)时,n_neighbors 应更大(如下例中的 n_neighbors=35)。

LOF 算法的优势在于它同时考虑了数据集的局部和全局属性:即使在异常样本具有不同潜在密度的数据集中,它也能表现良好。问题不在于样本有多孤立,而在于它相对于周围邻域有多孤立。

当应用 LOF 进行异常值检测时,没有 predictdecision_functionscore_samples 方法,而只有一个 fit_predict 方法。训练样本的异常分数可以通过 negative_outlier_factor_ 属性访问。请注意,当 LOF 用于新奇性检测时,即当 novelty 参数设置为 True 时,可以在新的未见数据上使用 predictdecision_functionscore_samples,但 predict 的结果可能与 fit_predict 的结果不同。参见 使用局部异常因子进行新奇性检测

下面的示例说明了这种策略。

../_images/sphx_glr_plot_lof_outlier_detection_001.png

示例

参考文献

2.7.4. 使用局部异常因子进行新奇性检测#

要使用 neighbors.LocalOutlierFactor 进行新奇性检测,即预测新未见数据的标签或计算异常分数,需要在拟合估计器之前将 novelty 参数设置为 True 来实例化估计器。

lof = LocalOutlierFactor(novelty=True)
lof.fit(X_train)

请注意,在这种情况下,fit_predict 不可用,以避免不一致。

警告

使用局部异常因子进行新奇性检测

novelty设置为True时,请注意,您只能对新的未见数据使用predictdecision_functionscore_samples,而不能用于训练样本,因为这会导致错误的结果。predict的结果将与fit_predict的结果不同。训练样本的异常分数始终可以通过negative_outlier_factor_属性访问。

下面说明了使用局部异常因子进行新奇性检测。

../_images/sphx_glr_plot_lof_novelty_detection_001.png