斯坦福机器学习课程 第九周 (2)构建一个异常检测系统

异常检测系统的开发与评估

视频地址

在上一节中,我们推导了异常检测算法。在本节,我想介绍一下如何开发一个异常检测的应用来解决一个实际问题。

在之前,我们已经提到了使用实数评估法的重要性。这样做的想法是,当你在用某个学习算法来开发一个具体的机器学习应用时,你常常需要做出很多决定,比如说选择用什么样的特征等等。而如果你找到某种评估算法的方式,比如直接返回一个数字,来告诉你算法的好坏,那么你做这些决定就显得更容易了。有了这样的数字,你就可以更好的确定某些特征是否需要在构建算法的时候考虑进来了,因为你可以通过对比算法在有这个特征和没这个特征的情况下,算法的具体表现,来决定是否要加入这个特征。

所以对于异常检测系统的评价方式很重要。

为了做到能评价一个异常检测系统,我们先假定已有了一些带标签的数据。

我们要考虑的异常检测问题是一个非监督问题,使用的是无标签数据。但如果你有一些带标签的数据,能够指明哪些是异常样本,哪些是非异常样本,那么这就是我们要找的能够评价异常检测算法的标准方法。

还是以飞机发动机的为例,现在假如你有了一些带标签数据,我们用$y=0$表示完全正常的样本,用$y=1$表示有异常的样本。

那么异常检测算法的推导和评价方法如下所示:

数据集划分

  • 我们先考虑训练样本。对于训练集,我们还是需要把数据看成是无标签的,通常来讲我们把这些样本都看成正常的,但可能有一些异常的也被分到你的训练集里,这也没关系(毕竟异常的是少数):

$$
x^{(1)},x^{(2)},…,x^{(m)}
$$

  • 接下来我们要定义交叉验证集和测试集。通过这两个集合我们将得到异常检测算法。

$$
交叉验证集:(x^{(1)}_{cv},y^{(1)}_{cv}),…,(x^{(m)}_{cv},y^{(m)}_{cv})
$$

$$
测试集:(x^{(1)}_{test},y^{(1)}_{test}),…,(x^{(m)}_{test},y^{(m)}_{test})
$$

继续回到我们的例子中:

假如说我们有10000制造的引擎作为样本。就我们所知,这些样本都是正常没有问题的飞机引擎。同样地,如果有一小部分有问题的引擎也被混入了这10000个样本,别担心,没有关系,我们假设这10000个样本中大多数都是没有问题的引擎。而且实际上从过去的经验来看,无论是制造了多少年引擎的工厂,在10000个引擎中都会得到大概20个有问题的引擎。对于异常检测的典型应用来说,异常样本的个数(也就是$y=1$的样本),基本上很多都是20到50个,并且通常我们的正常样本的数量要大得多。

有了这组数据,把数据分为训练集、交叉验证集和测试集的一种典型的分法如下:

我们把这10000个正常的引擎放6000个到无标签的训练集中,我叫它“无标签训练集”,但其实所有这些样本都对应$y=0$的情况。

我们要用这6000个训练样本来拟合$p(x)$。

$$
p(x)=p(x_1;μ_1, σ_1^2)…p(x_n;μ_n, σ_n^2)
$$

因此我们就是要用这6000个样本来计算参数$μ_1,σ_1^2…μ_n,σ_n^2$。

然后我们将剩余的4000个样本一半放入交叉验证集,另一半放入测试集中。同时我们还有20个异常的发动机样本,同样也把它们进行一个分割:放10个到验证集中,剩下10个放入测试集中。

注意:不要把交叉验证集和测试集混在一起使用,这样效果并不好。

异常检测算法的推导和评估方法

有了训练集、交叉验证集和测试集,异常检测算法的推导和评估方法如下:

首先我们使用训练样本来拟合模型$p(x)$。

然后我们预设一个比较小的$ε$,对于$p(x)<ε$的样本视为异常样本,然后分别在测试集合交叉验证集上进行测试和验证。我们知道在测试集合交叉验证集上是存在$y=1$的异常样本的,只不过量比较少而已。

这里其实我们可以把异常检测算法想象成是对交叉验证集和测试集中的$y$进行预测,这与监督学习有些类似,因为我们在对有标签的数据进行预测。所以我们可以通过对标签预测正确的次数来评价算法的好坏。

当然这些标签会比较偏斜,因为$y=0$(也就是正常的样本)肯定是比出现$y=1$(也就是异常样本)的情况更多。这跟我们在监督学习中用到的评价度量方法非常接近。

那么用什么评价度量好呢?

因为数据是非常偏斜的,所以通过分类准确度来衡量算法并不是一个好的度量法。我们之前的课程中也有提到过,如果你有一个比较偏斜的数据集,那么总是预测$y=0$它的分类准确度自然会很高。

因此我们应该算出以下数据来更科学的衡量算法的好坏:

  • 我们应该算出真阳性假阳性假阴性真阴性的比率来作为评价度量值
  • 我们也可以算出查准率召回率
  • 计算出$F_1-score$,通过一个很简单的数字来总结出查准和召回的大小。

通过这些方法,你就可以评价你的异常检测算法在交叉验证和测试集样本中的表现。

ε是怎么得到的呢?

现在还有一个问题没有说明,那就是参数$p(x)<ε$中的$ε$是如何求得的?

如果你有一组交叉验证集样本,一种选择参数$ε$的方法就是通过尝试多个不同的$ε$,然后选出一个使得$F_1-score$最大的$ε$,这个$ε$就是在交叉验证集上表现最好的。

更一般来说,我们使用训练集、测试集和交叉验证集的方法是当我们需要作出决定时,比如要包括哪些特征或者说要确定参数$ε$取多大合适,我们就可以不断地用交叉验证集来评价这个算法,然后决定我们应该用哪些特征,以及选择哪一个$ε$。

所以就是在交叉验证集中评价算法,然后选出一组特征,或者找到能符合我们要求的ε的值后,我们就能用测试集来最终评价算法的表现了。

异常检测 VS 监督学习

视频地址

在上一节,我们谈到如何评价一个异常检测算法。我们先是用了一些带标签的数据,以及一些我们知道是异常或者正常的样本(用$y=1$或$y=0$来表示)。

这就引出了这样一个问题:既然我们有了带标签的数据,那么为什么我们不直接用监督学习的方法(比如逻辑回归或者神经网络)呢?

这一节,就来介绍一下什么时候应该用异常检测算法,什么时候用监督学习算法是更有成效的。

下面这张表格对比了什么时候应该用什么算法:

异常检测 监督学习
负样本量很少(通常是在0到20个之间),正样本很多的时候 正负样本都很多的时候
有多种不同的异常类型时(因为对任何算法来说,从大量正样本中去学习到异常具体是什么,都是困难的);未知的异常与我们已知的异常完全不一样时。 有足够多的正样本来让你的算法学习到对应的特征,并且在未知的正样本中,也和已知的样本是类似的。

这就是在遇到具体情况时,要选择异常检查还是监督学习的方式。

其实关键区别就是在异常检测算法中我们只有一小撮正样本,因此监督学习算法不能从这些样本中学到太多东西。


关于上面表格中,有关异常检测中的不同类型的异常情况,我们用之前的垃圾邮件的例子来说明。

在那个例子中,垃圾邮件的类型其实也有很多种。有的是想卖东西给你、有的是想钓出你的密码(这种就叫钓鱼邮件)、还有其他一些类型的垃圾邮件…但对于垃圾邮件的问题,我们能得到绝大多数不同类型的垃圾邮件,因为我们有大量的垃圾邮件样本的集合。因此这也是为什么我们通常把垃圾邮件问题看作是监督学习问题的原因,虽然垃圾邮件的种类通常有太多太多 。

因此,我们可以看看一些异常检测的应用和监督学习应用的比较,我们不难发现对于欺诈检测(fraud detection),如果你掌握了许多种不同类型的诈骗方法,并且只有相对较小的训练集(只有很少一部分用户有异常行为)那我会使用异常检测算法。当然,有时候欺诈检测的方法也可能会偏向于使用监督学习算法,但是如果你并没有看到许多在你网站上进行异常行为的用户样本,那么欺诈检测通常还是被当做是一个异常检测算法,而不是一个监督学习算法。

选择使用什么特征

视频地址

前面我们讲到了异常检测算法,并且我们也讨论了如何评估一个异常检测算法。

事实上当你应用异常检测时,对它的效率影响最大的因素之一,是你使用什么特征变量。那么在本节,我将给出一些关于如何设计或选择异常检测算法的特征变量建议。

对不服从高斯分布的数据进行转换

在我们的异常检测算法中,我们做的事情之一就是使用正态(高斯)分布来对特征向量建模。通常情况下,我们都需要用直方图来可视化这些数据,如下图:

这么做的原因是为了在使用算法之前,确保我们的数据看起来是服从高斯分布的(当然即使你的数据并不是高斯分布,它也基本上可以良好地运行,但最好转换成高斯分布的样式之后在带入计算)。

如果你的样本的某个特征展示效果完全不像一个正态分布的形状:

那么我们就需要对数据进行一些转换,来确保这些数据能看起来更像高斯分布。这样你的算法才能效果更好。

一般情况下,我们都会对原始数据尝试求对数或者开根号操作进行转换,下图是通过对数来转换的:

$x$ $log(x)$

你也可以尝试使用以下的方式来带入:

$$
x←log(x) \\
x←log(x + c) \\
x←\sqrt x
$$

选择哪一个都可以,唯一的原则就是保证转换后的分布看起来更像高斯分布(正态分布)一些。


下面是对于一个不服从高斯分布的数据进行转换的过程:

原始数据 $x^{0.5}$ $x^{0.2}$ $x^{0.1}$ $x^{0.05}$ $log(x)$

我们对原始数据尝试了不同的转换之后,图像最终趋于了正太分布的样式。在Octive中实现的过程如下:

1
2
3
4
5
6
7
hist(x,50)
hist(x.^0.5,50)
hist(x.^0.2,50)
hist(x.^0.1,50)
hist(x.^0.05,50)
hist(log(x),50)
xNew = log(x);

最终我们选择了$log(x)$来代替原来的$x$。

异常检测算法的特征变量的获取

对于异常检测算法的特征变量的获取的方法,其实和之前学习的误差分析步骤是类似的。

  • 首先我们先训练处一个异常检测学习算法。
  • 然后在一组交叉验证集上运行算法。
  • 然后找出那些异常样本。
  • 然后我们尝试其他的特征变量,看是否能让我们的算法变得更好。

举例说明

我们用一个具体的例子来说明上面的过程:

在异常检测中,我们希望$p(x)$的值对正常样本来说是比较大的,而对异常样本来说,值是很小的。但一个很常见的问题是$p(x)$是具有可比性的(即对于两者都很大)。

这是我的一组无标签数据:

这里我只有一个特征变量$x_1$,我要用高斯分布来拟合它。

假设我们绘制出它的高斯分布,如下图所示:

假设我们遇到了一个有异常的样本:

但是,从图中我们能看出这个样本在这一特征下的$p(x_1)$并不低,我们无法从这一特征下区分出这一异常样本。

如果我们引入另一个特征$x_2$,图像如下:

再来看看我们的异常样本,出现在了这两个特征所分布的区域的外侧:

这个时候,我们的异常检测算法就会给出很小的值来验证这一点代表的样本属于异常样本。

总结:选择异常检测需要考虑的特征时,先找出异常样本,然后尝试通过引入新的特征来验证对异常样本的识别的准确性。如果有所提高,就可以考虑引入这个特征。

关于特征选择的思考

最后我想与你分享一些我平时在为异常检查算法选择特征变量时的一些思考。

通常,我会选择那些既不是特别大也不是特别小的特征变量。以数据中心异常计算机的监测的例子为例,我们有以下四个特征:

$$
x_1=机器内存使用 \\
x_2=硬盘资源使用 \\
x_3=CPU使用 \\
x_4=网络情况
$$

我假设CPU使用情况和网络情况呈线性关系,正常情况下如果其中一个服务器正在运行时,CPU负载和流量都很大。

现在,假设有一种异常情况,就是流量消耗很小,但CPU负载却很高,因为可能是机器遇到了某个死循环导致CPU负载飙升,因此我们可以定义一个新的特征变量来更好的说明这一情况:

$$
x_5=\frac{CPU 负载}{流量消耗}
$$

同样,你也可以尝试使用下面这种特征变量:

$$
x_6=\frac{(CPU 负载)^2}{流量消耗}
$$

其实,解决这类问题的思路就是尝试组合新的特征,从而能更好的检测异常情况。

坚持原创技术分享,您的支持将鼓励我继续创作!