查看原文
其他

又一强大的异常检测开源工具库:PyOD

推荐关注↓

来源丨数据STUDIO

数据探索没有捷径可走。如果你跳过数据科学项目阶段,直接进入到构建模型的阶段,经过一段时间后,你会发现准确度会达到上限——模型的性能不会提高。

这是因为异常值(也称为“离散值”)这个经常被我们忽略的问题。诱因就在于根据给定的数据开始建立模型——但这实际上是有问题的。

数据探索包括许多因素,例如变量识别、缺失值处理、特征工程等。检测和处理异常值也是数据探索阶段的主要因素。输入的质量决定了输出的质量,所以我们应该首先消除数据中的杂质,以便构建更好的模型。

PyOD是一个用于检测数据中异常值的库,它能对20多种不同的算法进行访问,以检测异常值,并能够与Python 2和3兼容。

在本文中,将了解异常值以及如何在 Python 中使用 PyOD 检测它们。

1. 什么是异常值?

异常值任何与数据集中其余观察值不同的数据点。让我们看一些真实的例子来理解异常值检测:

  • 一名学生的平均成绩超过90%而班上其余学生的平均成绩达到70%时——这就是一个明确的异常值。
  • 在分析某个客户的购买模式时,突然出现了非常高的值。虽然他/她的大部分交易金额都低于10,000卢比,但是这个交易的金额是100,000卢比。这可能是购买了一个电子产品——无论什么原因,它是整体数据的异常值。
  • 当你考虑到大多数运动员的成绩时,那些破纪录的短跑绝对是异常值。

存在异常值的原因有很多。也许是分析师在数据输入中出错,或者机器在测量中引起错误,或者是故意出现异常值。有些人不想透露他们的信息,因此在表格中输入虚假信息

异常值有两种类型:单变量和多变量。单变量异常值是仅由一个变量中的极值组成的数据点,而多变量异常值是至少两个变量的组合异常分数

假设有三个不同的变量——X,Y,Z。如果在三维空间中绘制这些变量的图形,它们应该形成一种云。位于此云之外的所有数据点都将是多变量异常值。

2. 为什么需要检测异常值?

异常值可以极大地影响分析和统计建模的结果。下面的图像,将出现异常值时模型发生的情况与处理异常值时的情况可视化出来:

但这里有一点需要注意——离散值并不总是坏事。了解这一点非常重要,简单地从数据中删除异常值而不考虑它们如何影响结果是一种灾难。

异常值不一定是坏事。这些离散值只是与其他模式不一致。但事实上异常值非常有趣。例如,如果在生物实验中,一只老鼠安然无恙而其他老鼠都死了,找到其中的原因会非常有趣。这可能会带来新的科学发现。因此,检测异常值非常重要。

——麦考斯,作家和统计学家

我们倾向于使用简单的方法,如箱形图、直方图和散点图来检测异常值。但是,专用异常值检测算法在处理大量数据并需要在较大数据集中执行模式识别的领域中非常有价值。

金融中的欺诈检测和网络安全中的入侵检测等应用需要密集而准确的技术来检测离散值。你能想象如果发现一个异常值并且结果是真的,那会有多尴尬吗?

而PyOD库可以弥补这一差距。

3. 为什么使用PyOD检测异常值?

许多异常检测包存在于各种编程语言中。这些语言在R中尤其有用,但是当切换到Python时,显然缺少异常值检测库。这是为什么呢?

PyNomaly这样已经存在的应用并不是专门为异常值检测而设计的(虽然它仍然值得一试)。为填补这一空白,Yue Zhao,Zain Nasrullah和Zheng Li设计并实施了PyOD库。

PyOD是一个可扩展的Python工具包,用于检测多变量数据中的异常值。它可以在记录良好的API下访问大约20个异常值检测算法

4. PyOD库的特征

PyOD有很多优点和有用的功能,以下是其中的一部分:

  • 开放源代码,包含各种算法的详细文档和示例
  • 支持高级模型,包括神经网络,深度学习和异常集合
  • 使用Numba和joblib优化JIT(即时)和并行化的性能
  • 与Python 2和3都兼容

5. 使用Python安装PyOD

首先在机器上安装PyOD:

pip install pyod

pip install --upgrade pyod # to make sure that the latest version is installed!

请注意,PyOD还包含一些基于神经网络的模型,这些模型在Keras中实现。PyOD不会自动安装Keras或TensorFlow,如果要使用基于神经网络的模型,则需要手动安装Keras和其他库。

6. PyOD中的异常检测算法

在本节中使用一个术语Outlying score。这意味着每个模型,在某种程度上,对一个数据点评分,而不是使用阈值来确定该点是否为异常值。

1) 基于角度的异常值检测(ABOD)

它考虑到每个点与其相邻点之间的关系没有考虑这些邻居之间的关系。其加权余弦分数与所有相邻点的方差可视为偏离分数

ABOD在多维数据上表现良好。

PyOD提供两种不同版本的ABOD:快速ABOD:根据 k 最邻近的值进行估计;原始ABOD:考虑所有具有高时间复杂性的训练点。

2) k-Nearest Neighbors检测器

对于任何数据点,与其第 k 个最近邻居的距离可视为远离分数

PyOD支持三个kNN检测器最大:以第k个邻近点的距离作为离群值得分;均值:以所有k个邻近点平均值作为离群值得分;中位数:以与邻近点的距离的中位数作为离群值得分。

3) 孤立森林

它在内部使用scikit-learn库。在此方法中,使用一组树完成数据分区。孤立森林提供了一个异常分数,用于查看结构中点的孤立程度。然后使用异常分数来识别来自正常观察的异常值。

孤立森林在多维数据上表现良好。

4) 基于直方图的异常值检测

这是一种有效的无监督方法,它假设特征独立并通过构建直方图来计算异常值。

它比多变量方法得多,但代价是精度较低

5) 局部相关积分(LOCI)

LOCI对于检测异常值和异常值组非常有效。它为每个点提供LOCI图,总结了该点周围区域内数据的大量信息确定了簇、微簇、它们的直径以及簇间距离

现有的异常检测方法都不能与此功能匹配,因为它们对每个点只输出一个数字

6) Feature bagging

Feature bagging检测器可在数据集的各个子样本上安装多个基本检测器。它使用平均或其他组合方法来提高预测精度。

默认情况下,LOF用作基本估算器。但是,任何估计器都可以用作基本估计器,例如kNN和ABOD。

Feature bagging首先通过随机选择特征子集来构建n个子样本。这使得基本估计更加多样化。最后,通过平均或取所有基本检测器的最大值来生成预测分数

7) 基于聚类的局部异常因素

将数据分为小型集群和大型集群。然后根据点所属的簇的大小以及到最近的大簇的距离来计算异常分数。

7. PyOD提供的额外工具

函数generate_data可用于生成具有异常值的随机数据内部数据多元高斯分布生成,异常值均匀分布生成。

我们可以提供自己的异常值分数值和在数据集中想要的样本总数。将使用此实用程序函数在实现部分中创建数据。

8. PyOD在Python中的运用

本文中,我们将使用两种不同的方法来演示PyOD:

  • 使用模拟数据集
  • 使用真实世界的数据集——大市场销售挑战

1) 模拟数据集中的PyOD

首先,让我们来输入必要的库:

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib.font_manager

现在,我们将导入想要用来检测数据集中异常值的模型。使用ABOD和KNN:

from pyod.models.abod import ABOD
from pyod.models.knn import KNN

现在,我们将创建一个带有异常值的随机数据集并绘制它。

from pyod.utils.data import generate_data, get_outliers_inliers

#generate random data with two features
X_train, Y_train = generate_data(n_train=200,train_only=True, n_features=2)

# by default the outlier fraction is 0.1 in generate data function 
outlier_fraction = 0.1

# store outliers and inliers in different numpy arrays
x_outliers, x_inliers = get_outliers_inliers(X_train,Y_train)

n_inliers = len(x_inliers)
n_outliers = len(x_outliers)

#separate the two features and use it to plot the data 
F1 = X_train[:,[0]].reshape(-1,1)
F2 = X_train[:,[1]].reshape(-1,1)

# create a meshgrid 
xx , yy = np.meshgrid(np.linspace(-10, 10, 200), np.linspace(-10, 10, 200))

# scatter plot 
plt.scatter(F1,F2)
plt.xlabel('F1')
plt.ylabel('F2')

创建一个dictionary并添加要用于检测异常值的所有模型:

classifiers = {
     'Angle-based Outlier Detector (ABOD)'   : ABOD(contamination=outlier_fraction),
     'K Nearest Neighbors (KNN)' :  KNN(contamination=outlier_fraction)
}

将数据拟合到我们在dictionary中添加的每个模型,然后,查看每个模型如何检测异常值:

#set the figure size
plt.figure(figsize=(10, 10))

for i, (clf_name,clf) in enumerate(classifiers.items()) :
    # fit the dataset to the model
    clf.fit(X_train)

    # predict raw anomaly score
    scores_pred = clf.decision_function(X_train)*-1

    # prediction of a datapoint category outlier or inlier
    y_pred = clf.predict(X_train)

    # no of errors in prediction
    n_errors = (y_pred != Y_train).sum()
    print('No of Errors : ',clf_name, n_errors)

    # rest of the code is to create the visualization

    # threshold value to consider a datapoint inlier or outlier
    threshold = stats.scoreatpercentile(scores_pred,100 *outlier_fraction)

    # decision function calculates the raw anomaly score for every point
    Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
    Z = Z.reshape(xx.shape)

    subplot = plt.subplot(1, 2, i + 1)

    # fill blue colormap from minimum anomaly score to threshold value
    subplot.contourf(xx, yy, Z, levels = np.linspace(Z.min(), threshold, 10),cmap=plt.cm.Blues_r)

    # draw red contour line where anomaly score is equal to threshold
    a = subplot.contour(xx, yy, Z, levels=[threshold],linewidths=2, colors='red')

    # fill orange contour lines where range of anomaly score is from threshold to maximum anomaly score
    subplot.contourf(xx, yy, Z, levels=[threshold, Z.max()],colors='orange')

    # scatter plot of inliers with white dots
    b = subplot.scatter(X_train[:-n_outliers, 0], X_train[:-n_outliers, 1], c='white',s=20, edgecolor='k'
    # scatter plot of outliers with black dots
    c = subplot.scatter(X_train[-n_outliers:, 0], X_train[-n_outliers:, 1], c='black',s=20, edgecolor='k')
    subplot.axis('tight')

    subplot.legend(
        [a.collections[0], b, c],
        ['learned decision function''true inliers''true outliers'],
        prop=matplotlib.font_manager.FontProperties(size=10),
        loc='lower right')

    subplot.set_title(clf_name)
    subplot.set_xlim((-10, 10))
    subplot.set_ylim((-10, 10))
plt.show()

看起来很不错!

2) 关于大市场销售问题的PyOD

现在,让我们看看PyOD是如何在著名的大型超市销售问题上进行运用的。

从导入所需的库并加载数据开始:

import pandas as pd
import numpy as np

# Import models
from pyod.models.abod import ABOD
from pyod.models.cblof import CBLOF
from pyod.models.feature_bagging import FeatureBagging
from pyod.models.hbos import HBOS
from pyod.models.iforest import IForest
from pyod.models.knn import KNN
from pyod.models.lof import LOF
# reading the big mart sales training data
df = pd.read_csv("train.csv")
# 数据获取:公众号:数据STUDIO 后台回复 data

让我们绘制Item MRP vs Item Outlet Sales以了解数据:

df.plot.scatter('Item_MRP','Item_Outlet_Sales')

Item Outlet Sales的范围是0到12000Item MRP0到250。我们将这两个功能缩小到0到1之间的范围。这是创建可解释的可视化所必需的(否则,它将变得太紧张)。对于此数据,使用相同的方法将花费更多时间来创建可视化。

注意:如果你不想可视化,则可以使用相同的比例来预测某个点是否为异常值。

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler(feature_range=(0, 1))
df[['Item_MRP','Item_Outlet_Sales']] = scaler.fit_transform(df[['Item_MRP','Item_Outlet_Sales']])
df[['Item_MRP','Item_Outlet_Sales']].head()

将这些值存储在NumPy数组中,以便以后在模型中使用:

X1 = df['Item_MRP'].reshape(-1,1)
X2 = df['Item_Outlet_Sales'].reshape(-1,1)
X = np.concatenate((X1,X2),axis=1)

我们要再次创建一个dictionary。但这一次,我们将添加更多模型,并了解每个模型如何预测异常值。

你可以根据问题和对数据的理解来设置异常值分数的值。在示例中,如果我们想检测5%与其他数据不相似的观察结果,那么需要将异常分数的值设置为0.05。

random_state = np.random.RandomState(42)
outliers_fraction = 0.05
# Define seven outlier detection tools to be compared
classifiers = {
        'Angle-based Outlier Detector (ABOD)': ABOD(contamination=outliers_fraction),
        'Cluster-based Local Outlier Factor (CBLOF)':CBLOF(contamination=outliers_fraction,check_estimator=False, random_state=random_state),
        'Feature Bagging':FeatureBagging(LOF(n_neighbors=35),contamination=outliers_fraction,check_estimator=False,random_state=random_state),
        'Histogram-base Outlier Detection (HBOS)': HBOS(contamination=outliers_fraction),
        'Isolation Forest': IForest(contamination=outliers_fraction,random_state=random_state),
        'K Nearest Neighbors (KNN)': KNN(contamination=outliers_fraction),
        'Average KNN': KNN(method='mean',contamination=outliers_fraction)
}

现在,我们将逐个拟合每个模型的数据,看看每个模型预测异常值的方式有多么不同。

xx , yy = np.meshgrid(np.linspace(0,1 , 200), np.linspace(0, 1, 200))

for i, (clf_name, clf) in enumerate(classifiers.items()):
    clf.fit(X)
    # predict raw anomaly score
    scores_pred = clf.decision_function(X) * -1
        
    # prediction of a datapoint category outlier or inlier
    y_pred = clf.predict(X)
    n_inliers = len(y_pred) - np.count_nonzero(y_pred)
    n_outliers = np.count_nonzero(y_pred == 1)
    plt.figure(figsize=(10, 10))
    
    # copy of dataframe
    dfx = df
    dfx['outlier'] = y_pred.tolist()
    
    # IX1 - inlier feature 1,  IX2 - inlier feature 2
    IX1 =  np.array(dfx['Item_MRP'][dfx['outlier'] == 0]).reshape(-1,1)
    IX2 =  np.array(dfx['Item_Outlet_Sales'][dfx['outlier'] == 0]).reshape(-1,1)
    
    # OX1 - outlier feature 1, OX2 - outlier feature 2
    OX1 =  dfx['Item_MRP'][dfx['outlier'] == 1].values.reshape(-1,1)
    OX2 =  dfx['Item_Outlet_Sales'][dfx['outlier'] == 1].values.reshape(-1,1)
         
    print('OUTLIERS : ',n_outliers,'INLIERS : ',n_inliers, clf_name)
        
    # threshold value to consider a datapoint inlier or outlier
    threshold = stats.scoreatpercentile(scores_pred,100 * outliers_fraction)
        
    # decision function calculates the raw anomaly score for every point
    Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
    Z = Z.reshape(xx.shape)
          
    # fill blue map colormap from minimum anomaly score to threshold value
    plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), threshold, 7),cmap=plt.cm.Blues_r)
        
    # draw red contour line where anomaly score is equal to thresold
    a = plt.contour(xx, yy, Z, levels=[threshold],linewidths=2, colors='red')
        
    # fill orange contour lines where range of anomaly score is from threshold to maximum anomaly score
    plt.contourf(xx, yy, Z, levels=[threshold, Z.max()],colors='orange')
        
    b = plt.scatter(IX1,IX2, c='white',s=20, edgecolor='k')
    
    c = plt.scatter(OX1,OX2, c='black',s=20, edgecolor='k')
       
    plt.axis('tight')  
    
    # loc=2 is used for the top left corner 
    plt.legend(
        [a.collections[0], b,c],
        ['learned decision function''inliers','outliers'],
        prop=matplotlib.font_manager.FontProperties(size=20),
        loc=2)
      
    plt.xlim((0, 1))
    plt.ylim((0, 1))
    plt.title(clf_name)
    plt.show()

输出结果:

OUTLIERS : 447 INLIERS : 8076 Angle-based Outlier Detector (ABOD)

OUTLIERS : 427 INLIERS : 8096 Cluster-based Local Outlier Factor (CBLOF)

OUTLIERS : 386 INLIERS : 8137 Feature Bagging

OUTLIERS : 501 INLIERS : 8022 Histogram-base Outlier Detection (HBOS)

OUTLIERS : 427 INLIERS : 8096 Isolation Forest

OUTLIERS : 311 INLIERS : 8212 K Nearest Neighbors (KNN)

OUTLIERS : 176 INLIERS : 8347 Average KNN

在上图中,白点是由红线包围的内点黑点是蓝色区域中的异常值


- EOF -


加主页君微信,不仅Python技能+1

主页君日常还会在个人微信分享Python相关工具资源精选技术文章,不定期分享一些有意思的活动岗位内推以及如何用技术做业余项目

加个微信,打开一扇窗



推荐阅读  点击标题可跳转

1、AI 作画神器 Midjourney 又更新了

2、ChatGPT 和 InstructGPT 技术解析

3、异常检测从入门到应用


觉得本文对你有帮助?请分享给更多人

推荐关注「Python开发者」,提升Python技能

点赞和在看就是最大的支持❤️

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存