在上一篇BLOG中,我们一同学习了异常检测算法的原理和实现,在这篇BLOG中,就让我们来看看更多在开发异常检测系统中的实现细节吧!
评估系统
在开发机器学习系统的时候,我们往往需要一个评估系统来评价我们的系统优劣。所以在系统开发的第一部分,我们将重点关注如何评价一个异常检测算法。
在之前的BLOG中,我们已经提到了使用评估系统的重要性,这样做的想法是当我们在用某个学习算法来开发一个具体的 机器学习应用时,我们常常需要做出很多决定比如说选择用什么样的特征等等,而如果我们能找到某种评价算法的方式直接返回一个数字来告诉我们算法的好坏,那么我们做这些决定就显得更容易了。每次更改算法后,我们的评估算法都会返回某个返回的数字来告诉我们这个更改到底是让算法表现变好了还是变差了。
为了能评价一个异常检测系统,我们先假定已有了一些带标签的数据。以飞机发动机的为例,现在假如我们有了一些带标签数据也就是有异常的飞机引擎的样本是否有问题,我们用 y = 0 来表示那些完全正常样本,用 y = 1 来代表那些异常样本。那么异常检测算法的推导和评价方法如下所示。
我们先考虑训练样本交叉验证和测试集如何分配。对于训练集,我们使用的是一些无异常的样本并且将其看成无标签的 训练集。接下来我们再将剩下的样本平均分配到交叉验证集 和测试集当中,并通过这两个集合我们将得到异常检测算法:
让我们通过具体的例子来深入了解一下。假如说我们总的数据 是有10000个正常的引擎和 20个有问题的引,对于异常检测的典型应用来说异常样本的个数也就是 y = 1的样本基本上很多都是20到50个。并且通常我们的正常样本的数量要比不正常的样本数量多得多。有了这组数据,我们就可以把数据分为训练集、交叉验证集和测试集。一种典型的分法如下:我们把这10000个正常的引擎放6000个到无标签的训练集中作为训练集来拟合p(x);然后我们取2000个好的飞机引擎样本到交叉验证集中再放剩下的2000个完好的飞机引擎到测试集中。此时正好6000+2000+2000=10000。同时我们还有20个异常的发动机样本也进行一个分割,我们放10个到验证集中剩下10个放入测试集中:
上面介绍的是一种比较推荐的方法来划分带标签和无标签的数据的比例,即 6:2:2 的比例来将好的引擎样本分配刀训练集,交叉验证集和测试集上;而坏的引擎样本我们一半只把它们平均分到交叉验证集和测试集中。顺便说一下如果你看到别人应用异常检测的算法时把10000个好的引擎分出6000个放到训练集中,然后把剩下的4000个样本既用作交叉验证集也用作测试集,这是不太推荐的。因为通常来说我们要交叉验证集和测试集当作是完全互不相同的两个数据组,合在一起显然不是一个好的尝试,非常不推荐。
话说回来给出之前分好的训练集、交叉验证集和测试集后,异常检测算法的推导和评估方法如下。首先我们先使用训练样本来拟合模型 p(x) ,接着我们在交叉验证集中选择出最适合的ε,最后我们对于测试集中的每一个测试样本 x 进行预测:若 p(x)<ε ,我们预测预测为 y = 1,而p(x)≥ε时 我们则预测 y = 0。这样多多少少让人感到和监督学习有点类似不是吗?我们有带标签的测试集而我们的算法就是对这些标签作出预测。最后我们可以通过对标签预测正确的次数来对我们算法的准确性进行评价。
当然这些标签会比较偏斜,因为 y = 0也就是正常的样本肯定是比出现 y = 1 也就是异常样本的情况多得多,这跟我们在监督学习中用到的评价度量方法非常接近。那么用什么评价度量好呢?因为数据是非常偏斜的,我们在之前的BLOG中也讲过,如果我们有一个比较偏斜的数据集,那么总是预测y = 0 它的分类准确度自然会很高。所以取而代之的我们应该算出真阳性、假阳性、假阴性和真阴性的比率来作为评价度量值,我们也可以算出查准率和召回率或者算出 F1-积分之类的,对这些方法不清楚的同学可以点击这里。
最后我们可以通过一个很简单的数字来总结出查准和召回的大小并通过这些方法来最终评价你的异常检测算法在交叉验证和测试集样本中的表现。
最后一点之前在异常检测算法中我们有一个参数 ε 对吧?这个 ε 是我们用来决定什么时候把一个样本当作是异常样本的一个阈值。如果我们有一组交叉验证集样本,一种常用的选择参数ε的方法就是你可以试一试多个不同的 ε 的取值,然后选出一个使得F1-积分的值最大的那个 ε ,也就是在交叉验证集中表现最好的那个 ε 。更一般来说我们使用训练集、测试集和交叉验证集的方法是,当我们需要作出决定时,比如要包括哪些特征或者说要确定参数 ε 取多大合适,我们就可以不断地用交叉验证集来评价这个算法然后决定我们应该用哪些特征和怎样选择 ε。当我们使用交叉验证集找到了能符合我们要求的 ε 的值后,我们就能用测试集来评价这个最终的模型的表现。
这就是评估系统的全部了,希望你有所领悟。
监督学习?
在之前的部分你也许会发现,异常或者正常的样本我们是使用 y = 1 或 y = 0 来表示,这就引出了这样一个问题:我们有了这些带标签的数据,即对于数据集其中一些我们知道是异常的,另外一些是正常的,我们为什么我们不直接用监督学习的方法呢?为什么不直接用逻辑回归或者神经网络的方法来直接学习这些带标签的数据从而给出预测 y = 1 或 y = 0 呢?在这一部分我就将跟你分享一些可供参考的方法来区分关于什么时候应该用异常检测算法什么时候该用监督学习算法才是更有成效的。
一种判断方法是观察样本数量的比例。如果我们的学习问题正样本的数量很小,那么我们也许应该考虑使用异常检测算法,比如正常的飞机引擎样本,此时异常的正样本很少,我们就可以用这些大量的负样本来拟合 p(x) 的模型。有一种观点就是在对 p(x) 进行估计并且拟合那些高斯参数的过程中,我们只需要负样本,所以就算我们有只有很少的正样本,但有大量的负样本,我们依然可以很好地拟合 p(x)。反过来对监督学习,一般来讲我们的正负样本数量都应该比较大,要不然我们的决策边界就可能不太清晰。这是一种可以从要解决的问题中看出应该使用异常检测还是监督学习算法的方法。
另一个判断的方法是看看异常的种类。比如飞机引擎的例子,我们都重点引起飞机引擎不工作的原因有很多对吧,很多部件坏了都可能导致引擎故障。因此如果我们只有很少的 正样本,那么一个监督学习算法要从你这么少的正样本中学习出这个异常是长什么样的是比较困难的。具体来说未来的异常可能跟你已经见过的完全不同,所以就算在你的正样本的集合里已经有了5种 或者10种 20种不同的引擎故障的原因,仍然可能明天你就需要检测一种全新的异常种类,一种全新的你从来都没见过的引擎故障原因。如果是这样的话就更加说明我们应该对负样本进行建模,我们应该通过负样本建立这个高斯模型 p(x) ,而不是很费力地对正样本进行建模。因为你知道 (即便你建了模型) 明天你也许就会遇到你完全没有见过的异常类型。
相对而言在其他一些问题中假如我们拥有足够多的正样本能让算法感觉到正样本是什么样的,比如你确信未来的正样本 跟训练集中的很相似的话 ,用监督学习算法似乎更加合理。所以当遇到具体的问题时应该使用异常检测算法还是监督学习算法关键的区别就是正负样本的比例,在监督学习算法中我们只有一小撮正样本,因此学习算法不可能从这些正样本中学出太多东西;因此取而代之的是我们使用一组大量的负样本让异常检测去学习,这样就能能学到更多或者说能从大量的负样本比如大量的正常引擎样本中学出 p(x) 模型。另外我们也预留一小部分正样本来评价我们的算法,既用于交叉验证集也用于测试集。
另外再额外说一点关于这些不同类型的异常情况。在前面的BLOG中,我们也提到了垃圾邮件的例子,在那些例子中垃圾邮件的类型其实也有很多种,有的是想卖东西给你,有的是想钓出你的密码,这种就叫钓鱼邮件还有其他一些类型的垃圾邮件看起来应该使用监督学习。但对于垃圾邮件的问题来说我们通常有足够多的垃圾邮件的样本让我们能得到绝大多数不同类型的垃圾邮件,这也正是为什么我们通常把垃圾邮件问题看作是监督学习问题的原因。
总而言之,是使用异常检测还是监督学习,关键在于正负样本的比例以及种类的多少,如果正负样本数量相当且很大的时候,就放心使用监督学习吧!
选择特征变量
在之前的部分,我们已经深入探讨了异常检测算法的一些部分。事实上当我们应用异常检测时,对它的效率影响最大的因素之一是特征变量的选择。所以这一部分,就让我们一起来看看如何设计或选择异常检测算法的特征变量吧!
在我们的异常检测算法中,我们做的事情之一就是使用这种高斯分布来对特征向量建模。那么我常做的一件事就是画出这些数据或者用直方图表示数据以确保这些数据在应用异常检测算法前看起来像高斯分布:
当然即使我们的数据并不是高斯分布,我们的异常检测算法也基本上可以良好地运行,但是我们的数据如果能近似像一个高斯分布,那么不论是效率还是最终的效果都会更加理想。
所以如果我的特征变量是上图那样的,那么我可以很高兴地把它送入我的学习算法了。但如果我画出来的直方图是下图这样的话:
好吧这个图形就看起来完全不像钟形曲线,分布很不对称,峰值非常偏向一边。如果我的数据是这样的话通常我们要做的事情就是对数据进行一些不同的转换来确保这些数据看起来更像高斯分布。
举个具体的例子,对于上面那个奇奇怪怪的直方图,如果我们用 log(x) 来替换掉 x 的话,就会变成下面这样:
显然经过替换后图像看起来更像高斯分布了。除了取对数变换之外还有别的一些方法也可以用,比如用 log(x + c) 来取代 x (c是常数)或者用 x^c 来取代 x (c是常数)。所有这些常数 c 我们都可以进行调整,其目的只有一个就是让数据看起来更像高斯分布。
下面就通过例子来演示一下如何对这些参数进行调整来让我的数据看起来更像高斯分布。我们先有1000个样本:
现在看起来显然还不够"高斯" 所以下面我们来调整一下参数。首先试试用 x^0.5 来替代 x,得到下图:
现在看起来有那么一点像高斯分布了但还是不够好。我们再调整一下我们用 x^0.2 来替代 x:
好像又更像高斯分布了一点,我们再减小一点,试试用 x^0.1 来替代 x:
好极了,我们再试试更小的用 x^0.05 来替代 x:
这样看起来更像高斯分布了,因此我们可以定义一个新的特征变量 xNew = x ^0.05,现在我的新特征变量 xNew 比原来的特征变量看起来更具像高斯分布,因此我就可以用这个新的特征变量来输入到我的异常检测算法中进行运算。
当然实现这一功能的方法不唯一你也可以用log(x)来代替x, 这也能让我们的数据看起来更像高斯分布:
所以我们也可以让 xNew = log(x),这是另一种可以选用的很好的特征变量。
所以如果我们画出数据的直方图,并且发现图形看起来非常不像正态分布,那么应该进行一些不同的转换就像上面这些,通过一些方法来让我们的数据看起来更具有高斯分布的特点后再把数据输入到学习算法。
接下来我们来探讨一下如何得到异常检测算法的特征变量。我通常用的办法是通过一个误差分析步骤先完整地训练出一个学习算法,然后在一组交叉验证集上运行算法后找出那些预测出错的样本,然后再看看我们能否找到一些其他的特征变量来帮助学习算法让它在那些交叉验证时判断出错的样本中表现更好。
让我们来用一个例子详细解释一下刚才说的这一过程。在异常检测中我们希望 p(x) 的值对正常样本来说是比较大的而对异常样本来说值是很小的。因 一个很常见的问题是 p(x) 是具有可比性的。也让我们来看一个具体点的例子,假如说下图我的无标签数据,我们只有一个特征变量 x1,我们要用一个高斯分布来拟合它,假如我有一个异常样本(绿叉)淹没在一堆正常样本中:
而我们的算法在只有一个特征变量的情况下显然是没有能力把这个样本判断为异常的。那么这时我会做的是看看我的训练样本然后看看到底是哪一个具体的飞机引擎出错了,看看通过这个样本能不能启发我想出一个新的特征 x2 来帮助算法区别出不好的样本和我剩下的正确的样本。如果我这样做的话创建出了一个新的特征 x2 ,当我重新画数据时,就会发现我们的异常样本就是绿叉所有正常的训练样本就是红叉非常远,所以这个新特征变量 x2 的值会看起来是异常的:
现在如果我再来给数据建模就会发现我们的异常检测算法会在中间区域给出一个较高的概率然后越到外层越小,到了那个绿色的样本我的异常检测算法会给出非常小的概率值以至于判断这个点为异常。
所以这个过程实际上就是看看哪里出了错,看看那些算法没能正确标记的异常点,看看你能不能得到启发来创造新的特征变量。这就是误差分析的过程以及如何为异常检查算法建立新的特征变量。
最后我想与你分享一些特别的选择技巧。通常来说我选择特征变量的方法是去选那些取值既不会特别特别大 不会特别特别小的那些特征变量。比如说我们还是用这个数据中心中监控计算机的例子,比如在一个数据中心中我们有上万台电脑 ,我们想要知道的是是不是有哪一台机器运作不正常了。这里给出了几种可选的特征变量包括占用内存、磁盘每秒访问次数 、CPU负载和网络流量。现在假如说我们怀疑某个出错的情况,比如正常来说我们电脑的CPU负载和网络流量应该互为正相关的线性关系,可能我运行了一组网络服务器发现其中一个服务器在对许多用户服务但网络流量非常小,那么就有可能是我的计算机在执行一个任务时进入了一个死循环因此被卡住了。在这种情况下要检测出异常,我可以新建一个特征 x5 = CPU负载/网络流量,因此 x5 的正常值将会变得不寻常地大,因此这将成为一个很好的特征能帮助我们检测出某种类型的异常情况。用同样的方法得到更多其他的特征比如说我可 建立一个特征 x6 = CPU负载^2/网络流量,这就像是特征 x5 的一个变体实际上它捕捉的异常仍然是你的机器是否具有一个比较高的 CPU 负载但没有一个 同样很大的网络流量。通过这样的方法建立新的特征变量,我们就可以通过不同特征变量的组合来轻松捕捉到对应的不寻常现象。
结语
通过这篇BLOG,相信你已经掌握了异常检测系统的诸多细节,快去实现看看吧!最后希望你喜欢这篇BLOG!