异常值检测

背景

无论在自然界还是人类社会生产生活中,都会存在那么一小撮不随大流的人或事物,这一小撮往往对整体影响很大,如何准确高效的把这一小部分的对象甄别出来俨然称为一个很具挑战性的工作。本文将带你利用高斯分布这一有利工具来进行异常值检测。

高斯分布

高斯分布又名正态分布,是用来描述随机变量的分布规律,这里先给出一维高斯分布的定义

如果随机变量X服从一个数学期望值为 μ \mu μ、方差为 σ 2 \sigma^2 σ2的概率分布,且概率密度函数为

f ( x ) = 1 2 π σ e − ( x − μ ) 2 2 σ 2 f(x) = \frac{1}{\sqrt {2\pi} \sigma} e^{-\frac{(x-\mu)^2}{2\sigma^2}} f(x)=2π σ1e2σ2(xμ)2

则称X服从高斯分布,记为

X ∼ N ( μ , σ 2 ) X \thicksim N(μ,σ^2) XN(μσ2)

其中数学期望值μ决定其位置,方差 σ 2 \sigma^2 σ2决定其波幅。特别的,当$μ = 0, \sigma^2 = 1 $时的高斯分布是标准正态分布(一般不称标准高斯分布)。

下面不加证明的给出二维独立高斯分布的概率密度函数

f ( x , y ) = 1 2 π σ 1 σ 2 e − [ ( x − μ 1 ) 2 2 σ 1 2 + ( x − μ 2 ) 2 2 σ 2 2 ] f(x,y ) = \frac{1}{ {2\pi} \sigma_1 \sigma_2} e^{-[\frac{(x-\mu_1)^2}{2\sigma_1^2}+\frac{(x-\mu_2)^2}{2\sigma_2^2}]} f(x,y)=2πσ1σ21e[2σ12(xμ1)2+2σ22(xμ2)2]

有意思的是在三维空间中这个函数的图像投影到平面上是一系列同心椭圆,他们的中心都在 ( u 1 , u 2 ) (u_1, u_2) (u1,u2)

Figure_1  二维标准正态分布

高斯分布是最常见的一种概率分布,科学家认为现实生活中很多现象符合高斯分布,且认为偏离高斯分布的3个标准差之外的是极小可能发生的情况,即异常情况,于是可以利用高斯分布来看数据是否是正常的还是异常的。

实践

本次以吴恩达第八次作业为例,动手实验异常值检测,包括原始数据的可视化,二维高斯分布的概率密度函数及其参数估计,最佳阈值的选定和异常值的筛选及异常数据的可视化。

数据可视化

我们一直强调如果原始数据能够可视化,那么毫不犹豫先可视化,可视乎之后能够非常直观的看到数据的大致分布情况,异常值大概是哪些。

import math
import numpy as np
import matplotlib
import scipy.io as scio
import matplotlib.pyplot as plt 

data = scio.loadmat(r"D:\项目\机器学习\吴恩达机器学习课件\CourseraML\ex8\data\ex8data1.mat") #读取mat数据
X  = data["X"] #特征
Xval = data['Xval']  #验证集
yval = data["yval"] #验证集对应的标签

def dataVisual(myX): #数据可视化
    plt.figure(figsize = (6, 4)) #新建画布
    plt.scatter(myX[:,0], myX[:,1], c="b", marker="x", linewidths=0.5) #散点图
    plt.xlabel("latency (ms)") #横坐标标签
    plt.ylabel("throughput (mb/s)") #纵坐标标签
    plt.show()

Figure_2 原数据散点图

从原数据的散点图可以看到大概有那么一些点的确跟大伙不一样,离群性严重,将其圈起来便是

Figure_3  圈出异常值

现在就是要设计一套模式把刚才手动圈起来的点自动识别出来。

二维高斯分布

要自动识别异常点,这时候多维高斯分布能够为我所用,利用样本数据来估计高斯分布的参数( μ , σ 2 \mu, \sigma^2 μ,σ2),然后再计算各样本的高斯概率值,并且还可以把高斯分布概率密度函数的投影画出来。

def estimateGaussian(myX): #高斯分布的参数估计,即均值和方差(标准差)
    m = len(myX) #样本数
    n = myX.shape[1] #特征数
    mu = myX.mean(axis = 0) #每个特征的均值
    sigma = myX.std(axis =0, ddof = 0) #每个特征的方差,ddof=1表示无偏估计,ddof=0表示无偏估计
    return mu, sigma

def gaussianDistribution(myx, myy, mymu1, mymu2, mysigma1, mysigma2): #二维高斯概率密度函数
    gaussian = (1/(2*math.pi*mysigma1*mysigma2))*math.e**(-(myx-mymu1)**2/(2*mysigma1**2) - (myy-mymu2)**2/(2*mysigma2**2) )
    return gaussian

def plotContour(myX): #等值线
    mu, sigma = estimateGaussian(myX) #高斯分布的估计参数
    x = np.linspace(np.min(myX[:,0])-1, np.max(myX[:,0])+1, 100) #经度
    y = np.linspace(np.min(myX[:,0])-1, np.max(myX[:,1])+1, 100) #纬度
    xx, yy  = np.meshgrid(x, y) #制作经纬网格线
    zz = gaussianDistribution(xx, yy, mu[0], mu[1], sigma[0], sigma[1]) #网格点上的高斯概率值
    #print(xx.shape, yy.shape, zz.shape)
    cont_levels = [10 ** h for h in range(-20, 1, 3)] #指定的级别绘制轮廓线, 必须按递增顺序排列
    plt.contour(xx, yy, zz, cont_levels, linewidths=0.7) #等值线
    plt.scatter(myX[:,0], myX[:,1], c="b", marker="x", linewidths=0.5) #原散点图
    plt.show()

Figure_4 等值线

模型挑选

虽然多维高斯分布可以识别出异常值,但是怎么具体确定哪些是异常值,哪些是正常值,并且能够评估这种异常值自动识别的效果如何?针对第一个问题,可以设定一个阈值,小于阈值的断定为异常值,反之为正常值;针对第二个问题,可以设定一个损失函数,比如f1得分,利用验证集f1的表现情况来确定最终的阈值。

def computeF1(y_pred, y_true): #计算f1-score,即损失函数
    tp, fp, tn, fn = 0, 0, 0, 0  #初始化
    for i in range(len(y_pred)):
        if y_pred[i]==y_true[i]:
            if y_true[i]==1:
                 tp +=1
            else:
                 tn +=1
        else:
            if y_true[i] ==1:
                fn +=1
            else:
                fp+=1
    precision = tp/(tp+fp) if tp+fp else 0 #精确率
    recall = tp/(tp+fn)  if tp+fn else 0 #召回率
    f1 = 2*precision*recall/(precision+recall)  if precision+recall else 0 #f1得分
    return f1

def selectThreshold(yval, pval): #通过验证集来选择最佳的阈值
    epsilons = np.linspace(np.min(pval), np.max(pval), 1000) 
    best_f1, best_epsilon = 0, 0 #初始化
    for e in epsilons: #从极小概率值到极大概率值
        y_pred = (pval < e).astype(float) #如果小于阈值则认为是异常值
        f1 = computeF1(y_pred, yval) #计算f1值
        if f1 > best_f1: #如果f1值有所提升
            best_f1 = f1 #更新f1
            best_epsilon = e #更新epsilon
    return best_f1, best_epsilon

挑选异常值和可视化

知道哪些是异常值,哪些是非异常值,就能够把这些异常值样本挑出来,并打上标签。

def abnormlyVisual(myX, myepsilon, mymu, mysigma): #先挑出异常值并可视化标记出来
    guassian = gaussianDistribution(myX[:,0], myX[:,1], mu[0], mu[1], sigma[0], sigma[1]) #计算高斯概率分布函数值
    abnorm_points = np.array([myX[i] for i in range(len(guassian)) if guassian[i]< myepsilon]) #跳出异常值点
    plt.figure() #新建画布
    plt.scatter(myX[:,0], myX[:,1], c="b", marker="x", linewidths=0.5) #原散点图
    plt.scatter(abnorm_points[:,0], abnorm_points[:,1], facecolor="w", edgecolors="r", marker="o") #异常值标注
    plt.show()

Figure_5 异常值可视化

大致经过这样几个步骤,就完成了异常值检测及其可视化工作了。

优缺点分析

(1) 理论通俗,操作简单

异常值检测主要利用了高斯分布理论来界定哪些是异常值,哪些是非异常值,理论比较通俗,操作起来也比较简单。

(2) 不具备通用性

虽然很多现象符合高斯分布,是从人类的经验中总结出来的规律,然而规律始终是规律,不能严谨证明,不能当成公理来使,现实生活中肯定还有非高斯分布的现象,这时候利用高斯分布来区分异常值和非异常值是缺乏科学性的,不符合真实情况。

应用场景

(1) 数据预处理

在数据预处理的时候需要对异常值进行剔除,常规做法是利用箱线图的上下四分位点的1.5倍内距来框定,而箱线图的理论基础也是高斯分布,与异常值识别的理论基础是一致,所以在数据预处理的时候可以利用今天所讲的异常值处理办法;

(2) 风险控制

做风控模型的朋友经常要和“黑”样本,“白”样本打交道,现实生活中的"黑"样本占比是极少的,这时候可以把"黑"样本当作异常值来识别和处理;

参考文献

1,https://blog.csdn.net/cowry5/article/details/80541524
2,https://blog.csdn.net/weixin_44027820/article/details/104616231

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zengbowengood/article/details/113529506