异常检测(2)——基于概率统计的异常检测(1)

  某个工厂生产了一批手机屏幕,为了评判手机屏幕的质量是否达到标准,质检员需要收集每个样本的若干项指标,比如大小、质量、光泽度等,根据这些指标进行打分,最后判断是否合格。现在为了提高效率,工厂决定使用智能检测进行第一步筛选,质检员只需要重点检测被系统判定为“不合格”的样本。

  智能检测程序需要根据大量样本训练一个函数模型,也许我们的第一个想法是像监督学习那样,为样本打上“正常”和“异常”的标签,然后通过分类算法训练模型。假设xtest是数据样本,predict(xtest)来判断xtest是否是合格样本。某个偷懒的家伙写下了这样的代码:

 def predict(xtest):
        return 1

  由于工厂的质量管理过硬,仅有极少数不合格样本,因此这段荒唐的预测居然展现出极高的准确率!这是由于严重的数据偏斜导致的,或许我们可以通过查准率(Precision)和召回率(Recall)两个指标识别出这段不负责任的代码,但是当你再次试图使用某个监督学习算法时,仍然会面对同样的问题——仅有极少数不合格样本,以至于监督学习无法学到足够的知识。能否从极度偏斜的数据中学习出一个有效的检测模型呢?当然能,这就是基于统计的异常检测。这类方法通常会假设给定的数据集服从一个随机分布模型,将与模型不一致的样本视为异常样本。其中最常用的两种分布模型是一元正态分布模型和多元正态分布模型。

算法模型

  在正态分布的假设下,如果有一个新样本x,当x的正态分布值小于某个阈值时,就可以认为这个样本是异常的。

  在正态分布中,μ-3σ<=x<=μ+3σ的区域包含了绝大部分数据,可以以此为参考,调整ε的值:

  现在有一个包含了m个一维数据的训练集:

  可以通过下面的函数判断一个样本是否是异常的:

  这里x(i)是已知的,μ和σ才是未知的,我们的目的是设法根据训练集求得μ和σ的值,以得到一个确定的函数模型。具体来说,通过最大似然估计量可以得出下面的结果:

  具体推导过程参考 概率笔记11——一维正态分布的最大似然估计

算法实现

  我们通过一些模拟数据来一窥异常检测算法的究竟。

  1 import numpy as np
  2 import matplotlib.pyplot as plt
  3
  4 def create_data():
  5     ‘‘‘
  6     创建训练数据和测试数据
  7     :return: X_train:训练集, X_test:测试集
  8     ‘‘‘
  9     np.random.seed(42)  # 设置seed使每次生成的随机数都相等
 10     m, s = 3, 0.1 # 设置均值和方差
 11     X_train = np.random.normal(m, s, 100) # 100个一元正态分布数据
 12     # 构造10测试数据,从一个均匀分布[low,high)中随机采样
 13     X_test = np.random.uniform(low=m - 1, high=m + 1, size=10)
 14     return X_train, X_test
 15
 16 def plot_data(X_train, X_test):
 17     ‘‘‘
 18     数据可视化
 19     :param X_train: 训练集
 20     :param X_test: 测试集
 21     :return:
 22     ‘‘‘
 23     fig = plt.figure(figsize=(10, 4))
 24     plt.subplots_adjust(wspace=0.5)  # 调整子图之间的左右边距
 25     fig.add_subplot(1, 2, 1)  # 绘制训练数据的分布
 26     plt.scatter(X_train, [0] * len(X_train), color=‘blue‘, marker=‘x‘, label=‘训练数据‘)
 27     plt.title(‘训练数据的分布情况‘)
 28     plt.xlabel(‘x‘)
 29     plt.ylabel(‘y‘)
 30     plt.legend(loc=‘upper left‘)
 31
 32     fig.add_subplot(1, 2, 2)  # 绘制整体数据的分布
 33     plt.scatter(X_train, [0] * len(X_train), color=‘blue‘, marker=‘x‘, label=‘训练数据‘)
 34     plt.scatter(X_test, [0] * len(X_test), color=‘red‘, marker=‘^‘,label=‘测试数据‘)
 35     plt.title(‘整体数据的分布情况‘)
 36     plt.xlabel(‘x‘)
 37     plt.ylabel(‘y‘)
 38     plt.legend(loc=‘upper left‘)
 39
 40     plt.rcParams[‘font.sans-serif‘] = [‘SimHei‘]  # 用来正常显示中文标签
 41     plt.rcParams[‘axes.unicode_minus‘] = False  # 解决中文下的坐标轴负号显示问题
 42     plt.show()
 43
 44 def fit(X_train):
 45     ‘‘‘
 46     拟合数据,训练模型
 47     :param X_train: 训练集
 48     :return:  mu:均值, sigma:方差
 49     ‘‘‘
 50     global mu, sigma
 51     mu = np.mean(X_train)  # 计算均值μ
 52     sigma = np.var(X_train) # 计算方差 σ^2
 53
 54 def gaussian(X):
 55     ‘‘‘
 56     计算正态分布
 57     :param X: 数据集
 58     :return: 数据集的密度值
 59     ‘‘‘
 60     return np.exp(-((X - mu) ** 2) / (2 * sigma)) / (np.sqrt(2 * np.pi) * np.sqrt(sigma))
 61
 62 def get_epsilon(n=3):
 63     ‘‘‘ 调整ε的值,默认ε=3σ ‘‘‘
 64     return np.sqrt(sigma) * n
 65
 66 def predict(X):
 67     ‘‘‘
 68     检测训练集中的数据是否是正常数据
 69     :param X: 待预测的数据
 70     :return: P1:数据的密度值, P2:数据的异常检测结果,True正常,False异常
 71     ‘‘‘
 72     P1 = gaussian(X) # 数据的密度值
 73     epsilon = get_epsilon()
 74     P2 = [p > epsilon for p in P1] # 数据的异常检测结果,True正常,False异常
 75     return P1, P2
 76
 77 def plot_predict(X):
 78     ‘‘‘可视化异常检测结果 ‘‘‘
 79     epsilon = get_epsilon()
 80     xs = np.linspace(mu - epsilon, mu + epsilon, 50)
 81     ys = gaussian(xs)
 82     plt.plot(xs, ys, c=‘g‘, label=‘拟合曲线‘)  # 绘制正态分布曲线
 83
 84     P1, P2 = predict(X)
 85     normals_idx = [i for i, t in enumerate(P2) if t == True] # 正常数据的索引
 86     plt.scatter([X[i] for i in normals_idx], [P1[i] for i in normals_idx],
 87                 color=‘blue‘, marker=‘x‘, label=‘正常数据‘)
 88     outliers_idx = [i for i, t in enumerate(P2) if t == False] # 异常数据的索引
 89     plt.scatter([X[i] for i in outliers_idx], [P1[i] for i in outliers_idx],
 90                 color=‘red‘, marker=‘^‘, label=‘异常数据‘)
 91     plt.title(‘检测结果,共有{}个异常数据‘.format(len(outliers_idx)))
 92     plt.xlabel(‘x‘)
 93     plt.ylabel(‘y‘)
 94     plt.legend(loc=‘upper left‘)
 95     plt.rcParams[‘font.sans-serif‘] = [‘SimHei‘]  # 用来正常显示中文标签
 96     plt.rcParams[‘axes.unicode_minus‘] = False  # 解决中文下的坐标轴负号显示问题
 97     plt.show()
 98
 99 if __name__ == ‘__main__‘:
100     mu, sigma = 0, 0 # 模型的均值μ和方差σ^2
101     X_train, X_test = create_data()
102     plot_data(X_train, X_test)
103     fit(X_train)
104     print(‘μ = {}, σ^2 = {}‘.format(mu, sigma))
105     plot_predict(np.r_[X_train, X_test])

  create_data()创建了100个满足X~N(μ,σ2)的数据作为训练集;10个在 之间均匀分布的数据作为测试数据,它们极有可能是异常数据。plot_data()可视化了训练集,再把测试数据加进去一并展示:

  可以看出,大部分训练数据都集中在正态分布的均值区域,而异常数据偏向于“倒钟”的两端。

  接下来使用fit()方法对异常检测模型进行训练,得到的结果是μ = 2.98961534826059, σ^2 = 0.008165221946938589。

  得到了模型参数后就可以使用目标函数对数据进行预测。gussian(X)实现了正态分布的密度函数;predict(X)将对X中的所有样本进行检测,并返回X对应的检测结果列表。其可视化结果是:

一元模型的问题

  在面对多维数据时,基于一元正态分布的异常检测可以单独抽取某一维度进行检测,通常也能工作的很好,但这里有一个假设—所有维度都符合正态分布,并且各维度都是独立的,如果两个维度之间存在相关性,那么基于一元正态分布的异常检测就可能会出现很大程度的误判。

  人的身高和鞋码存在着关联关系,一般来说,身高是脚长的7倍左右。假设某地区成年男子的身高符合μ=1.7,σ2=0.036的正态分布,我们用下面的代码模拟身高和脚长的数据。

 1 import numpy as np
 2 import matplotlib.pyplot as plt
 3
 4 def create_train():
 5     ‘‘‘
 6     构造2维训练集
 7     :return: X1:第1纬度的列, X2:第2维度的列表
 8     ‘‘‘
 9     np.random.seed(21)  # 设置seed使每次生成的随机数都相等
10     mu, sigma = 1.7, 0.036  # 设置均值和方差
11     X1 = np.random.normal(mu, sigma, 200)  # 生成200个符合正态分布的身高数据
12     # 设置身高对应的鞋码,鞋码=身高/7±0.02
13     X2 = (X1 / 7) + np.random.uniform(low=-0.01, high=0.01, size=len(X1))
14     return X1, X2
15
16 def plot_train(X1, X2):
17     ‘‘‘
18     可视化训练集
19     :param X1: 训练集的第1维度
20     :param X2: 训练集的第2维度
21     ‘‘‘
22     fig = plt.figure(figsize=(10, 4))
23     plt.subplots_adjust(hspace=0.5)  # 调整子图之间的上下边距
24     # 数据的散点图
25     fig.add_subplot(2, 1, 1)
26     plt.scatter(X1, X2, color=‘k‘, s=3., label=‘训练数据‘)
27     plt.legend(loc=‘upper left‘)
28     plt.xlabel(‘身高‘)
29     plt.ylabel(‘脚长‘)
30     plt.title(‘数据分布‘)
31     # x1维度的直方图
32     fig.add_subplot(2, 2, 3)
33     plt.hist(X1, bins=40)
34     plt.xlabel(‘身高‘)
35     plt.ylabel(‘频度‘)
36     plt.title(‘身高直方图‘)
37     # x2维度的直方图
38     fig.add_subplot(2, 2, 4)
39     plt.hist(X2, bins=40)
40     plt.xlabel(‘脚长‘)
41     plt.ylabel(‘频度‘)
42     plt.title(‘脚长直方图‘)
43
44     plt.rcParams[‘font.sans-serif‘] = [‘SimHei‘]  # 用来正常显示中文标签
45     plt.rcParams[‘axes.unicode_minus‘] = False  # 解决中文下的坐标轴负号显示问题
46     plt.show()
47
48
49 if __name__ == ‘__main__‘:
50     X1_train, X2_train = create_train()
51     plot_train(X1_train, X2_train)

  create_train()方法先是构造了符合正态分布的身高数据,之后根据身高构造脚长,并为脚长加入了在±0.01之间的噪声,以便使脚长产生一些震荡。plot_train(X1, X2)用散点图和直方图的形式将数据可视化:

  从上面的直方图可以看出,身高和与之存在线性相关性的脚长都符合正态分布。我们可以用前面的代码的计算两个维度的均值和方差,在根据这些参数画出每个维度的拟合曲线。

  就训练集而言,低于1.6米或高于1.8米的身高被认为偏向于异常值,与这些身高有关联关系的脚长的数值也被认为偏向于异常值。现在有一个1.73米、脚长是0.23米的人,这个小脚男士远离了大部分样本点,应当被视为异常数据:

  现在的问题是,对于身高的分布来说,1.73米是一个大众化的身高,对于脚长来说,0.23米也不算太差:

  无论从身高维度还是脚长维度,这个小脚男士都被认为是正常数据,这与我们期望的结果不符。其原因是一元正态分布分别从两个独立的维度看待问题,而忽略了这两个维度间的相关性。为了识别相关性,我们需要使用更高维度的正态分布模型。(下篇继续)



  作者:我是8位的

  出处:http://www.cnblogs.com/bigmonkey

  本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!

  扫描二维码关注公作者众号“我是8位的”

原文地址:https://www.cnblogs.com/bigmonkey/p/11431468.html

时间: 2025-01-07 14:09:51

异常检测(2)——基于概率统计的异常检测(1)的相关文章

基于机器学习的web异常检测

基于机器学习的web异常检测 Web防火墙是信息安全的第一道防线.随着网络技术的快速更新,新的黑客技术也层出不穷,为传统规则防火墙带来了挑战.传统web入侵检测技术通过维护规则集对入侵访问进行拦截.一方面,硬规则在灵活的黑客面前,很容易被绕过,且基于以往知识的规则集难以应对0day攻击:另一方面,攻防对抗水涨船高,防守方规则的构造和维护门槛高.成本大. 基于机器学习技术的新一代web入侵检测技术有望弥补传统规则集方法的不足,为web对抗的防守端带来新的发展和突破.机器学习方法能够基于大量数据进行

在使用Linq to SQL并序列化一个数据对象的时候报System.InvalidOperationException异常,序列化类型XXX的对象时检测到循环引用。

在使用Linq to SQL并序列化一个数据对象的时候报System.InvalidOperationException异常,序列化类型 的对象时检测到循环引用. 异常信息(部分): System.Web.Services.Protocols.SoapException: 服务器无法处理请求. ---> System.InvalidOperationException: 生成 XML 文档时出错. ---> System.InvalidOperationException: 序列化类型 Web

如何开发一个异常检测系统:如何评价一个异常检测算法

利用数值来评价一个异常检测算法的重要性 使用实数评价法很重要,当你用某个算法来开发一个具体的机器学习应用时,你常常需要做出很多决定,如选择什么样的特征等等,如果你能找到如何来评价算法,直接返回一个实数来告诉你算法的好坏,那样你做决定就会更容易一些.如现在有一个特征,要不要将这个特征考虑进来?如果你带上这个特征运行你的算法,再去掉这个特征运行你的算法,得到返回的实数,这个实数直接告诉你加上这个特征算法是变好了还是变坏了,这样你就有一种更简单的算法来确定是否要加上这个特征. 为了更快地开发出一个异常

堆栈 Cookie 检测代码检测到基于堆栈的缓冲区溢出

 报错:0x000CC3C9 处有未经处理的异常(在 image_opencv2.exe 中):  堆栈 Cookie 检测代码检测到基于堆栈的缓冲区溢出. 主要检查代码中有没有对数组的越界操作,就解决了这个bug. 其它的相关知识查后再补充.

论文:基于数据挖掘的网络入侵检测关键技术研究-郭春

目录 1.文章主要工作: 1.1 设计了一种适用于入侵检测的特征提取方法.(降维)DSFE:Distance-sum based feature extraction method; 1.2 设计了一种能够适用于入侵检测的样本约简方法.(样本约简,即缩减数据集中的样本数量) 1.3 设计了一种离群点挖掘的异常检测方法.(能够发现数据集中偏离大部分数据的离群值) 1.4 设计了一种包含三个检测模块的两层混合入侵检测模型. 1.文章主要工作: 1.1 设计了一种适用于入侵检测的特征提取方法.(降维)

基于python+opencv的人脸检测+

人脸检测分为两种:一种是基于知识的,一种是基于深度学习的.深度不会学习 人脸识别属于目标检测,主要涉及两个方面: ①先对检测的物体进行概率统计,从而知道待检测对象的一些特征,建立其目标的检测模型 ②用得到的模型来匹配输入的图像,如果有匹配则则输出匹配的区域,否则什么也不做. 我们看到的图片和计算机不一样,计算机看到的是一串串数字矩阵,图片由多个像素组成,拿我们熟悉的RGB图像来说,每个像素又有红绿蓝三个通道,假如每个像素的单个通道由uint8类型字符组成,那么三通道的像素便会有24位,这是我们常

基于深度学习的目标检测研究进展

前言 开始本文内容之前,我们先来看一下上边左侧的这张图,从图中你看到了什么物体?他们在什么位置?这还不简单,图中有一个猫和一个人,具体的位置就是上图右侧图像两个边框(bounding-box)所在的位置.其实刚刚的这个过程就是目标检测,目标检测就是"给定一张图像或者视频帧,找出其中所有目标的位置,并给出每个目标的具体类别". 目标检测对于人来说是再简单不过的任务,但是对于计算机来说,它看到的是一些值为0~255的数组,因而很难直接得到图像中有人或者猫这种高层语义概念,也不清楚目标出现在

基于深度学习的目标检测

普通的深度学习监督算法主要是用来做分类,如图1(1)所示,分类的目标是要识别出图中所示是一只猫.而在ILSVRC(ImageNet Large Scale Visual Recognition Challenge)竞赛以及实际的应用中,还包括目标定位和目标检测等任务.其中目标定位是不仅仅要识别出来是什么物体(即分类),而且还要预测物体的位置,位置一般用边框(bounding box)标记,如图1(2)所示.而目标检测实质是多目标的定位,即要在图片中定位多个目标物体,包括分类和定位.比如对图1(3

【C++】异常简述(三):补充之如何看待C++异常

C++异常的使用,我相信在上文总结的已经比较完整了,本文主要对C++异常这块进行额外的补充. 即使C++将异常纳入标准已经很多年了,但是直到现在都能看到很多坚持不显式使用异常.(包括本人在内,在写的代码中很少使用异常). 在评论异常之前,首先引入google C++ 编码规范,我们先来看看谷歌怎么看待C++异常. 6. 异常(Exceptions) 不要使用C++异常. 优点: 1)异常允许上层应用决定如何处理在底层嵌套函数中发生的"不可能发生"的失败,不像出错代码的记录那么模糊费解: