2.7. 新奇性与异常值检测#
许多应用需要能够判断一个新的观测值是否属于与现有观测值相同的分布(它是一个正常值),或者是否应被视为不同(它是一个异常值)。通常,这种能力被用于清理真实数据集。必须区分两个重要的概念
- 异常值检测:
训练数据包含异常值,这些异常值被定义为远离其他观测值的观测值。因此,异常值检测估计器试图拟合训练数据最集中的区域,忽略那些偏离的观测值。
- 新奇性检测:
训练数据未被异常值污染,我们感兴趣的是检测一个新的观测值是否为异常值。在这种情况下,异常值也称为新奇点(novelty)。
异常值检测和新奇性检测都用于异常检测,即对检测不正常或不寻常的观测值感兴趣。异常值检测也称为无监督异常检测,新奇性检测也称为半监督异常检测。在异常值检测的背景下,异常值/异常点不能形成一个密集的聚类,因为可用的估计器假设异常值/异常点位于低密度区域。相反,在新奇性检测的背景下,新奇点/异常点可以形成一个密集的聚类,只要它们位于训练数据(在这种情况下被认为是正常的)的低密度区域即可。
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 默认不支持 predict、decision_function 和 score_samples 方法,而只支持 fit_predict 方法,因为此估计器最初旨在用于异常值检测。训练样本的异常分数可通过 negative_outlier_factor_ 属性访问。
如果您确实想将 neighbors.LocalOutlierFactor 用于新奇性检测,即预测标签或计算新未见数据的异常分数,您可以在拟合估计器之前将 novelty 参数设置为 True 来实例化估计器。在这种情况下,fit_predict 不可用。
警告
使用 Local Outlier Factor 进行新奇性检测
当 novelty 设置为 True 时,请注意您必须只对新的未见数据使用 predict、decision_function 和 score_samples,而不是对训练样本使用,因为这会导致错误的结果。即 predict 的结果将与 fit_predict 的结果不同。训练样本的异常分数始终可通过 negative_outlier_factor_ 属性访问。
下表总结了 neighbors.LocalOutlierFactor 的行为。
方法 |
异常值检测 |
新奇性检测 |
|---|---|---|
|
可用 |
不可用 |
|
不可用 |
仅对新数据使用 |
|
不可用 |
仅对新数据使用 |
|
使用 |
仅对新数据使用 |
|
可用 |
可用 |
2.7.1. 异常值检测方法概述#
scikit-learn 中异常值检测算法的比较。局部异常因子(LOF)未显示黑色的决策边界,因为它在用于异常值检测时没有应用于新数据的 predict 方法。
ensemble.IsolationForest 和 neighbors.LocalOutlierFactor 在此处考虑的数据集上表现相当好。svm.OneClassSVM 众所周知对异常值敏感,因此在异常值检测方面的表现不佳。话虽如此,在高维或对正常数据分布没有任何假设的情况下进行异常值检测非常具有挑战性。svm.OneClassSVM 仍然可用于异常值检测,但需要对其超参数 nu 进行微调,以处理异常值并防止过拟合。linear_model.SGDOneClassSVM 提供了线性 One-Class SVM 的实现,其复杂度与样本数量呈线性关系。此处使用此实现结合核近似技术来获得与 svm.OneClassSVM 相似的结果,后者默认使用高斯核。最后,covariance.EllipticEnvelope 假设数据是高斯分布的并学习一个椭圆。有关不同估计器的更多详细信息,请参阅示例 比较玩具数据集上的异常值检测算法 和以下部分。
示例
请参阅 比较玩具数据集上的异常值检测算法 以比较
svm.OneClassSVM、ensemble.IsolationForest、neighbors.LocalOutlierFactor和covariance.EllipticEnvelope。请参阅 评估异常值检测估计器 以获取使用
metrics.RocCurveDisplay中的 ROC 曲线评估异常值检测估计器(neighbors.LocalOutlierFactor和ensemble.IsolationForest)的示例。
2.7.2. 新奇性检测#
考虑一个具有 \(p\) 个特征的、来自相同分布的 \(n\) 个观测值的数据集。现在考虑向该数据集添加一个观测值。这个新的观测值与其他观测值如此不同,以至于我们可以怀疑它是否是正常的?(即它是否来自相同的分布?)或者相反,它与其他观测值如此相似,以至于我们无法将其与原始观测值区分开来?这是新奇性检测工具和方法要解决的问题。
一般来说,它旨在学习一个粗略的、紧密的边界,划定初始观测值分布的轮廓,绘制在嵌入的 \(p\) 维空间中。然后,如果后续观测值位于边界划定的子空间内,则认为它们来自与初始观测值相同的群体。否则,如果它们位于边界之外,我们可以说它们是异常的,并且对我们的评估有给定的置信度。
One-Class SVM 由 Schölkopf 等人提出用于此目的,并在 支持向量机 模块中的 svm.OneClassSVM 对象中实现。它需要选择一个核和一个标量参数来定义边界。通常选择 RBF 核,尽管没有精确的公式或算法来设置其带宽参数。这是 scikit-learn 实现中的默认设置。参数 nu,也称为 One-Class SVM 的裕度,对应于在边界之外找到一个新的但正常的观测值的概率。
References
Estimating the support of a high-dimensional distribution Schölkopf, Bernhard, et al. Neural computation 13.7 (2001): 1443-1471.
示例
请参阅 使用非线性核(RBF)的单类 SVM 以可视化
svm.OneClassSVM对象围绕某些数据学习到的边界。
2.7.2.1. 扩展 One-Class SVM#
One-Class SVM 的在线线性版本在 linear_model.SGDOneClassSVM 中实现。此实现与样本数量呈线性关系扩展,可与核近似结合使用,以近似核化的 svm.OneClassSVM 的解,后者的复杂度最好为样本数量的平方。有关更多详细信息,请参阅 在线 One-Class SVM 部分。
示例
请参阅 One-Class SVM 与使用随机梯度下降的 One-Class SVM 以说明核化的 One-Class SVM 与
linear_model.SGDOneClassSVM结合核近似的近似。
2.7.3. 异常值检测#
异常值检测与新奇性检测类似,目标是将核心的正常观测值与一些被污染的观测值(称为异常值)分开。然而,在异常值检测的情况下,我们没有一个干净的数据集来表示可用于训练任何工具的正常观测值群体。
2.7.3.1. 拟合椭圆包络#
执行异常值检测的一种常见方法是假设正常数据来自已知分布(例如数据呈高斯分布)。基于这个假设,我们通常尝试定义数据的“形状”,并可以将远离拟合形状的观测值定义为异常值。
scikit-learn 提供了一个对象 covariance.EllipticEnvelope,它将鲁棒的协方差估计拟合到数据中,从而将椭圆拟合到中心数据点,忽略中心模式之外的点。
例如,假设正常数据是高斯分布的,它将以鲁棒的方式(即不受异常值影响)估计正常值的位置和协方差。从这个估计获得的马氏距离用于导出异常程度的度量。此策略如下所示。
示例
请参阅 鲁棒协方差估计和马氏距离相关性 以说明使用标准(
covariance.EmpiricalCovariance)或鲁棒估计(covariance.MinCovDet)的位置和协方差来评估观测值的异常程度之间的差异。请参阅 真实数据集上的异常值检测 以获取真实数据集上鲁棒协方差估计的示例。
References
Rousseeuw, P.J., Van Driessen, K. “A fast algorithm for the minimum covariance determinant estimator” Technometrics 41(3), 212 (1999)
2.7.3.2. Isolation Forest#
在处理高维数据集中的异常值检测时,一种有效的方法是使用随机森林。ensemble.IsolationForest 通过随机选择一个特征,然后在所选特征的最大值和最小值之间随机选择一个分割值来“隔离”观测值。
由于递归分区可以用树结构表示,隔离样本所需的分裂次数等同于从根节点到终止节点的路径长度。
这个路径长度,在由这样的随机树组成的森林中取平均值,是正常性的度量,也是我们的决策函数。
随机分区会为异常点生成明显更短的路径。因此,当一个随机树森林集体地为特定样本生成更短的路径长度时,它们极有可能是异常点。
ensemble.IsolationForest 的实现基于 tree.ExtraTreeRegressor 的集合。根据 Isolation Forest 的原始论文,每棵树的最大深度设置为 \(\lceil \log_2(n) \rceil\),其中 \(n\) 是用于构建树的样本数量(有关更多详细信息,请参阅 [1])。
此算法如下所示。
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
示例
请参阅 IsolationForest 示例 以说明 IsolationForest 的用法。
请参阅 比较玩具数据集上的异常值检测算法 以比较
ensemble.IsolationForest与neighbors.LocalOutlierFactor、svm.OneClassSVM(调整为执行异常值检测方法)、linear_model.SGDOneClassSVM和基于协方差的异常值检测covariance.EllipticEnvelope。
References
2.7.3.3. Local Outlier Factor#
在适度高维数据集上执行异常值检测的另一种有效方法是使用 Local Outlier Factor (LOF) 算法。
neighbors.LocalOutlierFactor (LOF) 算法计算一个分数(称为局部异常因子)来反映观测值的异常程度。它测量给定数据点相对于其邻居的局部密度偏差。其思想是检测那些密度明显低于其邻居的样本。
在实践中,局部密度是从 k-近邻获得的。观测值的 LOF 分数等于其 k-近邻的平均局部密度与其自身局部密度的比率:正常实例的局部密度预计与其邻居相似,而异常数据的局部密度预计会小得多。
所考虑的邻居数量 k(即参数 n_neighbors)通常选择为 1) 大于一个聚类必须包含的最小对象数量,以便其他对象可以相对于此聚类成为局部异常值,以及 2) 小于可能成为局部异常值的附近对象的最大数量。在实践中,此类信息通常不可用,将 n_neighbors=20 设置为通常效果良好。当异常值的比例很高时(例如大于 10 %,如以下示例所示),n_neighbors 应更大(例如在以下示例中为 n_neighbors=35)。
LOF 算法的优势在于它同时考虑了数据集的局部和全局属性:即使在异常样本具有不同底层密度的数据集中,它也能表现良好。问题不在于样本有多孤立,而在于它相对于周围邻域有多孤立。
当将 LOF 用于异常值检测时,没有 predict、decision_function 和 score_samples 方法,而只有一个 fit_predict 方法。训练样本的异常分数可通过 negative_outlier_factor_ 属性访问。请注意,当 LOF 用于新奇性检测时(即当 novelty 参数设置为 True 时),predict、decision_function 和 score_samples 可用于新的未见数据,但 predict 的结果可能与 fit_predict 的结果不同。请参阅 使用 Local Outlier Factor 进行新奇性检测。
此策略如下所示。
示例
请参阅 使用 Local Outlier Factor (LOF) 进行异常值检测 以说明
neighbors.LocalOutlierFactor的用法。请参阅 比较玩具数据集上的异常值检测算法 以与其他异常检测方法进行比较。
References
Breunig, Kriegel, Ng, and Sander (2000) LOF: identifying density-based local outliers. Proc. ACM SIGMOD
2.7.4. 使用 Local Outlier Factor 进行新奇性检测#
要使用 neighbors.LocalOutlierFactor 进行新奇性检测,即预测标签或计算新未见数据的异常分数,您需要在拟合估计器之前将 novelty 参数设置为 True 来实例化估计器
lof = LocalOutlierFactor(novelty=True)
lof.fit(X_train)
请注意,在这种情况下,fit_predict 不可用,以避免不一致。
警告
使用 Local Outlier Factor 进行新奇性检测
当 novelty 设置为 True 时,请注意您必须只对新的未见数据使用 predict、decision_function 和 score_samples,而不是对训练样本使用,因为这会导致错误的结果。即 predict 的结果将与 fit_predict 的结果不同。训练样本的异常分数始终可通过 negative_outlier_factor_ 属性访问。
使用 neighbors.LocalOutlierFactor 进行新奇性检测如下所示(请参阅 使用 Local Outlier Factor (LOF) 进行新奇性检测)。