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 参数,也称为单类 SVM 的间隔,对应于在边界之外找到一个新的但规则的观测值的概率。

参考文献

示例

../_images/sphx_glr_plot_oneclass_001.png

2.7.2.1. 扩展单类支持向量机#

单类 SVM 的在线线性版本已在 linear_model.SGDOneClassSVM 中实现。此实现的复杂度与样本数量呈线性关系,并且可以与核近似结合使用,以近似核化 svm.OneClassSVM 的解,后者的复杂度在样本数量上至少是二次的。有关更多详细信息,请参见 在线单类 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 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 和 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