4.5 异常检测分析
数据集中的异常数据通常被认为是异常点、离群点或孤立点,特点是这些数据的特征与大多数数据不一致,呈现出“异常”的特点,检测这些数据的方法称为异常检测。在大多数数据分析和挖掘工作中,异常值都会被当作“噪音”剔除。但在某些情况下,如果数据工作的目标就是围绕异常值展开,那么异常值会成为数据工作的焦点。
“噪音”的出现有多种原因,例如业务操作的影响(典型案例:网站广告费用增加10倍,导致流量激增)、数据采集问题(典型案例:数据缺失、不全、溢出、格式匹配等问题)、数据同步问题(异构数据库同步过程中的丢失、连接错误等导致的数据异常),在对离群点进行挖掘分析之前,需要从中区分出真正的“离群数据”,将“垃圾数据”去掉。
常用的异常检测方法分为基于统计的异常检测方法(如基于泊松分布、正态分布等分布规律找到异常分布点)、基于距离的异常检测方法(如基于K均值找到离所有分类最远的点)、基于密度的离群检测方法(LOF就是用于识别基于密度的局部异常值的算法)、基于偏移的异常点检测方法、基于时间序列的异常点监测方法等。
异常值检测常用于异常订单识别、风险客户预警、黄牛识别、贷款风险识别、欺诈检测、技术入侵等针对个体的分析场景。
4.5.1 异常检测中的“新奇检测”模式
异常检测根据原始数据集的不同可分为离群点检测和新奇检测两类模式。
1.离群点检测(Outlier Detection)
大多数场景下我们定义的异常数据都属于离群点数据,离群点检测的训练数据集包含“离群点”数据,对这些数据训练完成之后再在新的数据集中寻找异常数据。
2.新奇检测(Novelty Detection)
所谓新奇检测是识别新的或未知数据模式和规律的检测方法,这些规律和知识在已有机器学习系统的训练集中没有被发掘出来。新奇检测的前提是已知训练数据集是“纯净”的,未被真正的“噪音”数据或真实的“离群点”污染,然后针对这些数据训练完成之后再对新的数据做训练以寻找新奇数据的模式。
新奇检测主要应用于新的模式、主题、趋势的探索和识别,包括信号处理、计算机视觉、模式识别、智能机器人等技术方向,应用领域例如潜在疾病的探索、新物种的发现、新传播主题的获取等。
新奇检测和异常检测有关,一开始的新奇点往往都以一种离群的方式出现在数据中,这种离群方式一般会被认为是离群点,因此二者的检测和识别模式非常类似。但是,当经过一段时间之后,新奇数据一旦被证实为正常模式,例如将新的疾病识别为一种普通疾病,那么新奇模式将被合并到正常模式之中,就不再属于异常点的范畴。
4.5.2 将数据异常与业务异常相分离
数据异常反映的只是数据层面的离群分布,而这些离群分布未必都是业务意义上的“异常”,很多时候数据的异常是对业务特殊运营状态的反映,因此属于数据异常但业务正常的范畴。有关这部分内容已经在3.1.2节中介绍过,在此不再赘述。
4.5.3 面临维度灾难时,异常检测可能会失效
当原始数据集的维度非常多时,普通的异常检测方法可能会失效,原因是随着维度的增加,数据之间的相似程度将严重受到维度数量的影响,因此这种过程将很难奏效。
解决高维空间下的异常检测问题,通常有三种思路:
扩展现有的离群点检测模式。
发现子空间中的离群点。
对高维数据进行建模。
在异常检测面对高维数据集时,跟聚类遇到的问题非常类似,有关该话题的更多信息请查看4.1.4节。
4.5.4 异常检测的结果能说明异常吗
在做异常检测分析时,输出的结果是用户是否异常的标签,例如1、-1,这种标签只是客观上基于数据相似度或密度的识别结果。但是,即使在业务也没有任何特殊动作(即由异常业务引发的真实数据反应)导致的“假异常”的前提下,仍然无法判断结果是否真的异常。
在销售型公司中,黄牛常用来代表哪些通过一定的渠道和方法大量倒卖公司的商品,并从中获利的人。对黄牛的检测和识别属于销售公司的主要分析项目之一。通过多种算法做出的异常检测结果会给出属于黄牛的信息列表,但列表中的异常检测结果并一定就符合实际情况,还需要业务介入做进一步筛查和审核。
因此,在大多数场景下通过非监督式方法实现的异常检测的结果只是用来缩小排查范围,为业务的执行提供更加精准和高效的执行目标而已。
要实现异常检测不只有非监督式一种方法,如果有历史对于黄牛等异常信息的标记,还可以通过监督式(分类算法)进行预测性检测分析。
4.5.5 代码实操:Python异常检测分析
sklearn中提供了one-class SVM和EllipticEnvelope两种方法用于异常检测,前者基于libsvm实现的非监督式异常检测方法,可用于做高维度分布的评估;后者只能做基于高斯分布数据集的异常检测。
本节示例模拟的是针对一批没有任何标签的原始数据做异常检测模型训练,然后通过新的测试集来发现新数据集中的异常数据;本示例会使用svm来做异常检测分析,数据源文件outlier.txt位于“附件-chapter4”中,默认工作目录为“附件-chapter4”(如果不是,请cd切换到该目录下,否则会报“IOError:File outlier.txt does not exist”)。完整代码如下:
# 导入库 from sklearn.svm import OneClassSVM # 导入OneClassSVM import numpy as np # 导入numpy库 import matplotlib.pyplot as plt # 导入Matplotlib from mpl_toolkits.mplot3d import Axes3D # 导入3D样式库 # 数据准备 raw_data = np.loadtxt('outlier.txt', delimiter=' ') # 读取数据 train_set = raw_data[:900, :] # 训练集 test_set = raw_data[:100, :] # 测试集 # 异常数据检测 model_onecalsssvm = OneClassSVM(nu=0.1, kernel="rbf", random_state=0) # 创建异常检测算法模型对象 model_onecalsssvm.fit(train_set) # 训练模型 pre_test_outliers = model_onecalsssvm.predict(test_set) # 异常检测 # 异常结果统计 toal_test_data = np.hstack((test_set, pre_test_outliers.reshape(test_set.shape[0], 1))) # 将测试集和检测结果合并 normal_test_data = toal_test_data[toal_test_data[:, -1] == 1] # 获得异常检测结果中正常数据集 outlier_test_data = toal_test_data[toal_test_data[:, -1] == -1] # 获得异常检测结果中异常数据 n_test_outliers = outlier_test_data.shape[1] # 获得异常的结果数量 total_count_test = toal_test_data.shape[0] # 获得测试集样本量 print ('outliers: {0}/{1}'.format(n_test_outliers, total_count_test)) # 输出异常的结果数量 print ('{:*^60}'.format(' all result data (limit 5) ')) # 打印标题 print (toal_test_data[:5]) # 打印输出前5条合并后的数据集 # 异常检测结果展示 plt.style.use('ggplot') # 使用ggplot样式库 fig = plt.figure() # 创建画布对象 ax = Axes3D(fig) # 将画布转换为3D类型 s1 = ax.scatter(normal_test_data[:, 0], normal_test_data[:, 1], normal_test_data[:, 2], s=100, edgecolors='k', c='g', marker='o') # 画出正常样本点 s2 = ax.scatter(outlier_test_data[:, 0], outlier_test_data[:, 1], outlier_test_data[:, 2], s=100, edgecolors='k', c='r', marker='o') # 画出异常样本点 ax.w_xaxis.set_ticklabels([]) # 隐藏x轴标签,只保留刻度线 ax.w_yaxis.set_ticklabels([]) # 隐藏y轴标签,只保留刻度线 ax.w_zaxis.set_ticklabels([]) # 隐藏z轴标签,只保留刻度线 ax.legend([s1, s2], ['normal points', 'outliers'], loc=0) # 设置两类样本点的图例 plt.title('novelty detection') # 设置图像标题 plt.show() # 展示图像
上述代码以空行分为5个部分。
第一部分导入库。本示例使用OneClassSVM做异常检测,用numpy做数据导入和基本预处理,用Matplotlib做图像展示,用Axes3D将图形转换为3D类型。
第二部分数据准备。使用Numpy的loadtxt方法导入数据,数据集中的训练集和测试集直接切分为两部分。
第三部分异常数据监测。使用OneClassSVM做异常检测分析,先创建异常检测算法模型对象,然后使用fit方法对训练集做模型训练,最后应用测试集做新的异常数据的检测。
相关知识点:OneClassSVM
one-class SVM用于异常检测,它的基本原理是在给定的一组样本中,检测数据集的边界以便于区分新的数据点是否属于该类。它是基于密度检测方法的一种,属于无监督学习算法,拟合过程由于不存在数据标签列,因此只需要输入一个矩阵X即可。
one-class SVM属于SVM的一种,可用于高维数据的异常检测。one-class SVM提供了linear、poly、rbf、sigmoid和precomputed检测内核可供使用,甚至你可以自定义一个内核算法来调用。one-class SVM本质上还是一种分类,但这种分类与传统的“分类”意义不同,one-class SVM的分类是将数据分为“正常数据”和“异常数据”(用+1和-1表示),而传统的分类是将正常数据按照不同的特征分为几个不同的类别。
第四部分异常结果统计。本部分中先使用np.hastck将测试集的输入与预测的检测结果合并,从而得到一份完整的数据集;然后基于该数据集将数据划分为预测后的正常数据集和异常数据集,接着使用shape形状方法获得异常的结果数量和测试的总样本量,并打印输出异常数据的数量以及整体测试集的带有预测结果标签的前5条数据。这里使用了之前介绍过的str.format方法,得到如下结果:
outliers: 6/100 **************** all result data (limit 5) ***************** [[ 0.03685295 0.0343899 0.09197858 -0.01026255 -0.00814121 -1. ] [-0.0011522 0.02174971 -0.02040125 0.00986554 -0.03447136 1. ] [-0.01258645 0.04736393 0.01110832 -0.01156876 -0.02334062 1. ] [-0.02837847 0.04398011 0.00126378 0.02313849 0.00542565 1. ] [ 0.02222529 0.00715191 -0.03713534 -0.02938668 -0.09915368 -1. ]]
第五部分异常检测结果展示。在展示结果的部分使用了3D展示图形类型。
首先通过plt.style.use方法加载预定义的样式库ggplot。
相关知识点:matplotlib.pyplot的样式库
在使用Matplotlib画图时,可指定图像的样式。默认情况下,通过print plt.style.available方法可以得到可用的预定义样式库:
[u'seaborn-darkgrid', u'seaborn-notebook', u'classic', u'seaborn-ticks', u'grayscale', u'bmh', u'seaborn-talk', u'dark_background', u'ggplot', u'fivethirtyeight', u'seaborn-colorblind', u'seaborn-deep', u'seaborn-whitegrid', u'seaborn-bright', u'seaborn-poster', u'seaborn-muted', u'seaborn-paper', u'seaborn-white', u'seaborn-pastel', u'seaborn-dark', u'seaborn', u'seaborn-dark-palette']
列表中的不同的样式库(每个字符串代表一个样式库),可使用plt.style.use()方法指定来实现不同的样式风格。同时,也可以自己设计一个样式库,然后加载进来使用。
然后创建一个画布对象fig,并将该对象通过Axes3D方法转换为3D类型的对象ax。
接着在ax对象上通过scatter分别画出正常和异常的样本点,这里在画图的时候由于是三维图像,需要同时指定x、y和z轴的数据(对二维数据只需要指定x和y即可)。
使用w_xaxis.set_ticklabels、w_yaxis.set_ticklabels、w_zaxis.set_ticklabel方法隐藏x、y、z轴标签,只保留刻度线。
使用legend方法设置图例标签以及位置。图例位置自适应,图例标签值的设置可通过scatter中的legend参数设置。
使用title方法设置标题并通过show方法展示图像,结果如图4-10所示。
图4-10 异常检测3D图
图形中的空心圆色代表异常点,实心圆代表正常点。该图形可直接通过鼠标拖拽以显示不同3D视角下的数据分布,这在某些区域的点比较集中时非常有用。
上述过程中,主要需要考虑的关键点是:
注意区分one-class SVM和EllipticEnvelope的应用数据集的限制并根据不同实际情况选择算法;
如果要做新奇检测或要适应更多的数据集分布,那么使用one-class SVM,但需要注意新奇检测的训练数据必须是不包含噪音的干净数据,否则新奇点将可能无法监测出来。
代码实操小结:在本节的代码中,主要用了以下几个知识点:
通过Numpy的loadtxt读取数据文件;
对矩阵做切片操作;
使用sklearn.svm中的OneClassSVM方法实现异常检测分析,并使用fit方法针对训练集,使用predict方法针对测试集应用;
使用Numpy的hstack方法将矩阵按列合并,得到新的矩阵;
通过对矩阵中特定列的值进行判断,直接选择或切分数据集;
通过shape方法获得矩阵的形状;
通过print方法结合str.format做格式化的输出;
通过plt.style.use方法使用Matplotlib的预定义库样式;
通过mpl_toolkits.mplot3d的Axes3D方法做3D类型图像转换;
通过matplotlib.pyplot的scatter方法画散点图,并通过展示图形,设置不同的展示样式,包括颜色、样式、图例等,针对并隐藏坐标轴标签、设置图例和标签、设置标题等。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论