计算机系统应用教程网站

网站首页 > 技术文章 正文

使用Extended Isolation Forest的异常检测

btikc 2024-10-11 11:21:09 技术文章 3 ℃ 0 评论

在本文中,我简要介绍了最初的IF算法,描述了它的潜在问题以及EIF如何处理它。最后,我将给出一个Python示例,说明如何使用这两种算法并比较它们的性能。

1.隔离森林

隔离森林算法利用了异常观测很少且与“正常”观测有显著差异的事实。森林是建立在决策树的基础上的,每棵树都可以访问训练数据的子样本。为了在树中创建分支,首先选择一个随机特征。然后,为该特征选择一个随机拆分值(在最小值和最大值之间)。如果给定的观察值具有较低的该特征值,则所选的该特征值为左分支,否则为右分支。这个过程将继续进行,直到达到一个孤立的点或指定的最大深度。

原则上,离群值比常规观测值出现的频率要低,并且在数值上与常规观测值存在差异(在特征空间中离常规观测值较远)。这就是为什么通过使用这样的random partitioning,它们应该更接近树的根(更短的平均路径长度,即观察必须在树中从根节点到终端节点传递的边的数量更少),需要的拆分次数更少。

离群分数是根据森林中的所有树以及该点在这些树中的深度创建的。

2.隔离森林的问题

理解这个问题的最好方法是看一个例子。

在左图中,我们可以看到从多元正态分布中取样的数据。直观地说,我们假设分配给观测值的异常分数从分布中心点[0,0]呈放射状增加。然而,显然不是这样,如右图所示。此外,还有分数较低的矩形伪影,如X轴0点和1点之间的垂直伪影。

让我们继续第二个例子。在这里,我们看到两个以点[0,10]和[10,0]为中心的小块。通过检查右图,我们不仅可以看到之前存在的伪像,还可以看到两个 ghost clusters(大约在[0,0]和[10,10]处)。

造成这种特殊行为的原因是隔离林的决策边界要么是垂直的,要么是水平的(随机特征的随机值),如下图所示,其中作者绘制了训练阶段IF生成的branch cuts。我们看到,这些分支倾向于聚集在大多数点所在的位置。但由于直线只能平行于坐标轴,所以有些区域包含许多branch cuts,只有少量或单个观测值,这就导致了一些观测值的异常分数不正确。一个例子可能是[3,0](许多branch cuts和[3,3](很少cuts)附近的点。

3.扩展隔离林(Extended Isolation Forest)

对隔离林的缺点进行分析后,得出的结论是,这个问题仅仅是由水平/垂直的branch cuts引起的。扩展随机森林通过以稍微不同的方式来解决这个问题。它选择的数是据范围内的随机值,而不是选择随机特征:

  • branch cut的随机斜率
  • 从训练数据的可用值范围中选择随机截距

这些是您最有可能从简单线性回归(y = ax + b)中回忆的术语(斜率/截距)。让我们看一个视觉示例!从下图中我们可以看到与原始IF算法的主要区别 - >与轴不平行的cuts 。

扩展随机森林可以很好地推广到高维空间,在那里我们处理的不是直线而是超平面。

让我们通过观察IF / EIF生成的离群分数图的差异来结束理论解释。在下面的图像中,我们看到离群分数从数据聚类中径向扩散,并且没有可见的伪影/ghost clusters。

EIF捕获的额外特征是直接位于两个聚类之间的较高离群得分区域(它们的链接类型)。考虑到两个聚类的接近程度,该区域可以被认为接近“正常”,但由于远离集中群体,因此得分较高。

4. Python中的示例

数据

对于这个简短的练习,我使用从这里(http://odds.cs.stonybrook.edu/forestcovercovertype-dataset/)下载的Forest Cover数据集。该机器学习数据集包含286048个观测值和10个特征。观察结果被标记,因此我们事先知道哪些是离群的(0.9%的观察结果是离群的)。

比较的另一个方面可以是速度,看看扩展隔离森林中的速度是否会显著降低。

UCI机器学习库中原始的ForestCover/Covertype数据集是一个多类分类数据集。它仅用于从制图变量(没有遥感数据)预测森林覆盖类型。该研究区域包括位于科罗拉多州北部罗斯福国家森林的四个荒野地区。这些地区的森林很少受到人为干扰,因此现有的森林覆盖类型更多地是生态过程的结果,而不是森林管理的做法。该机器学习数据集有54个属性(10个定量变量,4个二元荒野区和40个二元土壤类型变量)。这里,离群点检测数据集仅使用10个定量属性创建。类2中的实例被视为正常点,类4中的实例被视为异常。离群率为0.9%。其他类的实例将被省略。

导入Python库

import matplotlib.pyplot as plt
import seaborn as sns
import scipy.io
import pandas as pd
import numpy as np
from sklearn import metrics
from scipy.stats import multivariate_normal
from sklearn.ensemble import IsolationForest
from sklearn.metrics.pairwise import euclidean_distances
import eif as iso
# default plot settings
%matplotlib inline

UDF

def pretty_cm(y_pred, y_truth, labels):
 '''
 'Pretty' implementation of a confusion matrix with some evaluation statistics.
 
 Input:
 y_pred - object with class predictions from the model
 y_truth - object with actual classes
 labels - list containing label names
 '''
 
 cm = metrics.confusion_matrix(y_truth, y_pred)
 ax= plt.subplot()
 sns.heatmap(cm, annot=True, fmt="d", linewidths=.5, square = True, cmap = 'BuGn_r')
 ax.set_xlabel('Predicted label')
 ax.set_ylabel('Actual label')
 ax.set_title('Confusion Matrix', size = 15) 
 ax.xaxis.set_ticklabels(labels)
 ax.yaxis.set_ticklabels(labels)
 
 print('#######################')
 print('Evaluation metrics ####')
 print('#######################')
 print('Accuracy: {:.4f}'.format(metrics.accuracy_score(y_truth, y_pred)))
 print('Precision: {:.4f}'.format(metrics.precision_score(y_truth, y_pred)))
 print('Recall: {:.4f}'.format(metrics.recall_score(y_truth, y_pred)))
 print('F1: {:.4f}'.format(metrics.f1_score(y_truth, y_pred)))

加载机器学习数据集

mat = scipy.io.loadmat('cover.mat')
X = pd.DataFrame(mat['X'])
y = pd.Series([x[0] for x in mat['y']])
X.describe()

隔离森林

在所有模型中,我将尝试使用相同的设置,这意味着:

  • 森林中的树数量= 100
  • 为估计每棵树而绘制的最大样本数= 256
  • 我事先知道数据集中存在0.9%的异常,我将使用此百分比来选择最高的离群分数

Scikit-Learn

# define % of anomalies
anomalies_ratio = 0.009
if_sk = IsolationForest(n_estimators = 100, 
 max_samples = 256,
 contamination = anomalies_ratio, 
 behaviour= " new", 
 random_state = np.random.RandomState(42))
if_sk.fit(X)
y_pred = if_sk.predict(X)
y_pred = [1 if x == -1 else 0 for x in y_pred]
pretty_cm(y_pred, y, [0, 1])

Forest能够正确识别机器学习数据集中8.7%的异常。

eif

通过设置ExtensionLevel为0,我估计一个常规的隔离森林。首先,截至目前,无法为模型设置随机状态,因此多次运行可能会产生不同的结果。此外,eif实现没有那么多要配置的参数。

另一件事是该模型预测离群分数,但不会自动识别哪些观察结果被视为异常。为了识别异常,我对异常分数进行排序并检索0.9%具有最高分数的观察指数。

if_eif = iso.iForest(X.values, 
 ntrees = 100, 
 sample_size = 256, 
 ExtensionLevel = 0)
# calculate anomaly scores
anomaly_scores = if_eif.compute_paths(X_in = X.values)
# sort the scores
anomaly_scores_sorted = np.argsort(anomaly_scores)
# retrieve indices of anomalous observations
indices_with_preds = anomaly_scores_sorted[-int(np.ceil(anomalies_ratio * X.shape[0])):]
# create predictions 
y_pred = np.zeros_like(y)
y_pred[indices_with_preds] = 1
pretty_cm(y_pred, y, [0, 1])

隔离森林的这种实现比sklearn更差。它仅捕获机器学习数据集中6.3%的异常。更重要的是,由于每次运行此模型时缺乏随机状态,性能都会发生变化。

Extended Isolation Forest

这部分非常类似于普通的隔离森林案例(eif实现),不同之处在于ExtensionLevel。要在完全扩展的level上工作,我将级别level 为9(维度数- 1)。

eif = iso.iForest(X.values, 
 ntrees = 100, 
 sample_size = 256, 
 ExtensionLevel = X.shape[1] - 1)
anomaly_scores = eif.compute_paths(X_in = X.values)
anomaly_scores_sorted = np.argsort(anomaly_scores)
indices_with_preds = anomaly_scores_sorted[-int(np.ceil(anomalies_ratio * X.shape[0])):]
y_pred = np.zeros_like(y)
y_pred[indices_with_preds] = 1
pretty_cm(y_pred, y, [0, 1])

从结果中可以看出,模型无法识别数据集中的单个异常。

结论

扩展隔离森林算法当然很有趣,值得进一步探索。它很容易克服原始模型在一组人工实例上的局限性,然而,在实际数据集上实现它时似乎存在某种问题。此外,当前的eif实现几乎与sklearn实现一样快。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表