异常检测系列:Histogram-based Outlier Score_HBOS算法

img
考虑多维数据,就像Excel电子表格中的数据框一样。列是维度或变量,行是观察结果。一个观察结果有多个值。变量的计数统计称为直方图。如果有N个变量,就会有N个直方图。如果一个观察结果的值落在直方图的尾部,那么这个值就是异常值。如果一个观察结果的许多值都是异常值,那么这个观察结果很可能是异常值。

列也被称为变量。通过所有的观察结果,我们可以为每个变量计算一个称为直方图的计数统计。如果一个观察结果的值落在直方图的尾部,那么这个值就是异常值。通常情况下,某些观察结果的值在相应的变量方面是异常值,但是某些值是正常的。如果一个观察结果的许多值都是异常值,那么这个观察结果很可能是异常值。

基于这种直觉,可以使用变量的直方图来定义变量的单变量异常值分数。一个观察结果应该有N个单变量异常值分数。该技术假设变量之间是独立的,以推导直方图和单变量异常值分数。一个观察结果的N个单变量异常值分数可以加总成为基于直方图的异常值分数(HBOS)。尽管这个假设听起来很强,但HBOS在实际情况中证明了其有效性。

(A) HBOS是如何工作的?

HBOS独立地为所有的N个变量构建直方图。箱子的高度用于衡量“异常性”。大多数观察结果属于高频箱子,而异常值属于低频箱子。单变量异常值分数被定义为箱子高度的倒数。

HBOS被正式定义为N个变量的对数单变量异常值分数的总和:
img
在上述方程中,hist_i§是变量i所属的柱状图的高度,其中观测值p属于,而*1/hist§*是单变量异常值分数。这个定义将为异常值分配一个较大的值。

如果一个变量是分类变量,那么直方图就是按类别计数。如果一个变量是数值型的,它应该首先被离散化成等宽的箱子,以得到计数统计量。每个直方图的最大高度被归一化为1.0。这样可以确保所有单变量分数可以等量地求和得到HBOS。

(B) 基于分布的算法可以很快

在前面的章节中,我提到异常检测算法可以是基于接近度、基于分布或基于集成的方法。基于分布的方法使用概率分布拟合数据来获取异常值分数。基于分布的方法的计算时间通常比基于接近度或基于集成的方法的时间短。基于接近度的方法可能耗时,因为它需要计算任意两个数据点之间的距离。因此,它是一个适合数据科学项目的建模候选方法。

© 建模过程

在本书中,我应用图©中的过程来帮助您开发模型,评估模型性能并展示模型结果。它们是(1) 模型开发,(2) 阈值确定,和(3) 对正常和异常组进行分析。
img
图(C):建模过程(作者提供的图像)

在大多数情况下,我们没有已验证的异常值来进行监督学习建模。由于我们没有已知的异常值,我们甚至不知道人群中异常值的百分比。好消息是异常值分数已经衡量了观测值与正常数据之间的偏差。如果我们为异常值分数派生直方图,我们可以发现这些观测值并确定异常值的百分比。因此,在第一步中,我们开发模型并分配异常值分数。在第二步中,我们将异常值分数绘制在直方图中,然后选择一个值,称为阈值,将正常观测值与异常观测值分开。阈值还确定了异常组的大小。

我们如何评估无监督模型的可靠性?如果一个模型能够有效地识别训练数据中的异常值,那么这些异常值应该表现出异常性的特征。它们在这些变量方面应该与正常数据非常不同。在第三步中,我们将对正常组和异常组进行描述性统计,以证明模型的可靠性。两组之间的变量的描述性统计(如均值和标准差)证明了模型的可预测性。

描述性统计表是评估模型是否与任何先前知识一致的合理指标。如果一个变量在异常组中预期较高或较低,但结果与直觉相悖,您应该调查、修改或删除该变量,并重新进行建模。最终版本的模型应该提供与任何先前知识一致的描述性统计表。

(C.1) 第一步 - 构建模型

与之前一样,我将使用PyOD的实用函数generate_data()生成百分之十的异常值。为了使案例更有趣,数据生成过程(DGP)将创建六个变量。虽然这个模拟数据集有目标变量Y,但无监督模型只使用X变量。Y变量仅用于验证。我将异常值的百分比设置为5%,即“contamination=0.05”。

前五条记录如下所示:
img
让我在散点图中绘制前两个变量。黄色点是异常值,紫色点是正常数据点。
img
让我们构建我们的第一个HBOS模型。HBOS的一个重要超参数是箱子的数量。HBOS对直方图的箱宽敏感,我们将在后面讨论这个超参数。该模型假设箱子的数量为50。在这个模型中,我们还假设污染率为5%,因为我们在数据中生成了5%的异常值。如果我们不指定它,那么默认值为10%。

一旦训练完成,该模型还会为训练数据中的观测值计算异常值分数。如果我们按升序对观测值进行排序,那些高于阈值的观测值就是异常值。这个阈值是由参数contamination=0.05确定的,意味着5%的数据将是异常值。模型计算训练数据的阈值并存储在HBOS.threshold_中。

我们将使用PyOD的decision_function()函数为训练和测试数据生成异常值分数。我们还将使用predict()函数来预测观测值是否为异常值。predict()函数将异常值分数与阈值hbos.threshold_进行比较。如果异常值分数高于阈值,函数将为观测值分配“1”,否则为“0”。我编写了一个简短的函数count_stat()来显示预测的“1”和“0”值的计数。

from pyod.models.hbos import HBOS
n_bins = 50
hbos = HBOS(n_bins=n_bins,contamination=0.05)
hbos.fit(X_train)

# Training data
y_train_scores = hbos.decision_function(X_train)
y_train_pred = hbos.predict(X_train)

# Test data
y_test_scores = hbos.decision_function(X_test)
y_test_pred = hbos.predict(X_test) # outlier labels (0 or 1)

# Threshold for the defined comtanimation rate
print("The threshold for the defined comtanimation rate:" , hbos.threshold_)

def count_stat(vector):
    # Because it is '0' and '1', we can run a count statistic. 
    unique, counts = np.unique(vector, return_counts=True)
    return dict(zip(unique, counts))

print("The training data:", count_stat(y_train_pred))
print("The training data:", count_stat(y_test_pred))

img
(C.2) 第二步 — 确定一个合理的阈值

在大多数情况下,我们不知道异常值的百分比。我们如何在无监督学习模型中识别异常值?因为异常值分数已经衡量了数据点与其他数据点的偏差,我们可以按照它们的异常值分数对数据进行排序。那些具有较高异常值分数的数据点可能是异常值。我们可以根据异常值分数确定一个阈值,将异常值与正常数据分开。

图(C.2)展示了异常值分数的直方图。我们可以选择一种更保守的方法,通过选择一个较高的阈值,这将导致异常值组中的异常值更少,但希望更精细。直方图建议选择5.5作为阈值。阈值的选择还决定了人群中异常值的百分比。

import matplotlib.pyplot as plt
plt.hist(y_test_scores, bins='auto')  # arguments are passed to np.histogram
plt.title("Histogram with 'auto' bins")
plt.xlabel('HBOS')
plt.show()

img
图(C.2)

(C.3) 第三步 — 展示正常组和异常组的汇总统计

这一步描述了正常组和异常组的特征。异常值的特征应该与正常数据非常不同。描述性统计表成为证明模型的可靠性的一个好指标。

在表(A)中,我展示了正常组和异常组的计数和计数百分比。在我们的案例中,有两个被标记为“特征0”和“特征1”的特征。 “异常分数”是平均异常分数。提醒您在有效的展示中使用特征名称标记特征。该表告诉我们几个重要的结果:

  • 异常组的大小: 异常组约占总体的8.4%。请记住,异常组的大小由阈值决定。如果您选择更高的阈值,大小将会缩小。

  • 平均异常分数: 异常组的平均HBO分数远高于正常组的分数(5.836 > -0.215)。这个证据只是验证了异常组中的数据是异常值。此时,您不需要对分数进行过多的解释。

  • 每个组中的特征统计: 表(A)显示,异常组的特征‘0’和特征‘1’的值比正常组的值要小。在业务应用中,您可能期望异常组的特征值要么高于正常组,要么低于正常组。因此,特征统计有助于理解模型结果。

threshold = hbos.threshold_ # Or other value from the above histogram

def descriptive_stat_threshold(df,pred_score, threshold):
    # Let's see how many '0's and '1's.
    df = pd.DataFrame(df)
    df['Anomaly_Score'] = pred_score
    df['Group'] = np.where(df['Anomaly_Score']< threshold, 'Normal', 'Outlier')

    # Now let's show the summary statistics:
    cnt = df.groupby('Group')['Anomaly_Score'].count().reset_index().rename(columns={'Anomaly_Score':'Count'})
    cnt['Count %'] = (cnt['Count'] / cnt['Count'].sum()) * 100 # The count and count %
    stat = df.groupby('Group').mean().round(2).reset_index() # The avg.
    stat = cnt.merge(stat, left_on='Group',right_on='Group') # Put the count and the avg. together
    return (stat)

descriptive_stat_threshold(X_train,y_train_scores, threshold)

img
由于我们在数据生成过程中有真实值y_test,我们可以生成一个混淆矩阵来了解模型的性能。数据生成过程产生了50个异常值(500 x 10% = 50)。该模型在阈值为4.0时识别出了50个中的42个。


Actual_pred = pd.DataFrame({'Actual': y_test, 'Anomaly_Score': y_test_scores})
Actual_pred['Pred'] = np.where(Actual_pred['Anomaly_Score']< threshold,0,1)
pd.crosstab(Actual_pred['Actual'],Actual_pred['Pred'])

img


descriptive_stat_threshold(X_test,y_test_scores, threshold)

img

def confusion_matrix(actual,score, threshold):
    Actual_pred = pd.DataFrame({'Actual': actual, 'Pred': score})
    Actual_pred['Pred'] = np.where(Actual_pred['Pred']<=threshold,0,1)
    cm = pd.crosstab(Actual_pred['Actual'],Actual_pred['Pred'])
    return (cm)
confusion_matrix(y_train,y_train_scores,threshold)

img
(D) 通过聚合多个模型实现模型稳定性

HBOS 对直方图的箱宽非常敏感。如果箱宽太窄,正常数据点会被识别为异常值并落入这些箱中。如果箱宽太宽,异常值会落入正常数据的箱中而被忽略。为了得到一个稳定的模型结果,策略是使用一系列不同箱宽的 HBOS 模型来生成多个分数,然后将这些分数进行聚合。这种方法可以减少过拟合的机会并提高预测准确性。PyOD 模块提供了四种方法来聚合结果。您只需要使用一种方法来生成聚合结果。

  1. 平均值

  2. 最大最大值 (MOM)

  3. 最大平均值 (AOM)

  4. 平均最大值 (MOA)

我将为一系列箱宽 [5,10, 15, 20, 35, 30, 50, 60, 75, 100] 生成 10 个 HBOS 模型。这些模型的平均预测值将作为最终模型的预测结果。

# 导入所需的库
from pyod.models.combination import aom, moa, average, maximization
from pyod.utils.utility import standardizer
from pyod.models.hbos import HBOS

# 对数据进行标准化处理
X_train_norm, X_test_norm = standardizer(X_train, X_test)

# 测试一系列分箱数
k_list = [5, 10, 15, 20, 25, 30, 50, 60, 75, 100]
n_clf = len(k_list)
# 准备数据框以存储模型结果
train_scores = np.zeros([X_train.shape[0], n_clf])
test_scores = np.zeros([X_test.shape[0], n_clf])

# 建模
for i in range(n_clf):
    k = k_list[i]
    hbos = HBOS(n_bins=k)
    hbos.fit(X_train_norm)
    # 将结果存储在每一列中:
    train_scores[:, i] = hbos.decision_function(X_train_norm) 
    test_scores[:, i] = hbos.decision_function(X_test_norm) 
# 在组合之前,需要对决策分数进行标准化处理
train_scores_norm, test_scores_norm = standardizer(train_scores,test_scores)

以上的“train_scores”中存储了十个异常值分数。为了正确汇总这十个分数,需要进行归一化步骤。下面的“y_by_average”是这10个分数的平均预测值。我在图(D)中创建了它的直方图。


# Combination by average
# The test_scores_norm is 500 x 10. The "average" function will take the average of the 10 columns. The result "y_by_average" is a single column: 
y_train_by_average = average(train_scores_norm)
y_test_by_average = average(test_scores_norm)
import matplotlib.pyplot as plt
plt.hist(y_train_by_average, bins='auto') # arguments are passed to np.histogram
plt.title("Combination by average")
plt.show()

img
图(D):训练数据的平均预测直方图

图(D)中的直方图建议使用阈值1.4。使用该阈值的描述性统计数据显示在表(D)中。它将25个数据点标识为异常值。读者应该对表(C.3)应用类似的解释。

descriptive_stat_threshold(X_train,y_train_by_average, 1.4)

img
表格(D)

(E) HBOS算法摘要

  • 如果一个观测值在几乎所有变量上都是异常值,那么这个观测值很可能是异常值。

  • HBOS根据直方图定义每个变量的异常值得分。

  • 所有变量的异常值得分可以相加,得到观测值的多变量异常值得分。

  • 由于直方图易于构建,HBOS是一种高效的无监督方法来检测异常值。

参考文献

  • [1] Goldstein, M., & Dengel, A. (2012). 基于直方图的异常值得分(HBOS):一种快速无监督异常检测算法。KI-2012:海报和演示轨道9

异常检测系列文章导航

异常检测系列:异常检测基本介绍

异常检测系列:Histogram-based Outlier Score_HBOS算法

异常检测系列:基于经验累积分布的异常检测(ECOD)

异常检测系列:孤立森林(Isolation Forest)模型介绍

异常检测系列:主成分分析PCA异常值分数检测

异常检测系列:支持向量机(SVM)异常检测介绍

异常检测系列:单类支持向量机(OCSVM)

异常检测系列:高斯混合模型(GMM)

异常检测系列:局部异常因子(LOF)

异常检测系列:K最近邻算法KNN

异常检测系列:基于聚类的局部异常因子(CBLOF)

异常检测系列:基于极限梯度提升的异常检测(XGBOD)

异常检测系列:AutoEncoder模型介绍

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值