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 不可用。

警告

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

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) 没有显示黑色的决策边界,因为它在用于异常检测时没有可应用于新数据的预测方法。

../_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\) 维空间中绘制的初始观测值分布的轮廓。然后,如果进一步的观测值位于边界限定的子空间内,则认为它们来自与初始观测值相同的总体。否则,如果它们位于边界之外,我们可以说它们是异常的,并且对我们的评估有一定的信心。

One-Class SVM 由 Schölkopf 等人提出,用于此目的,并在 支持向量机 模块的 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 的集合。遵循隔离森林的原始论文,每棵树的最大深度设置为 \(\lceil \log_2(n) \rceil\),其中 \(n\) 是用于构建树的样本数量(有关更多详细信息,请参见 (Liu 等人,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 和 Zhou, Zhi-Hua。 “隔离森林”。数据挖掘,2008 年。ICDM’08。第八届 IEEE 国际会议。

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 用于新颖性检测时,predictdecision_functionscore_samples 可用于新的未见数据,即当 novelty 参数设置为 True 时,但 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