码迷,mamicode.com
首页 > 其他好文 > 详细

基于聚类的异常值检测方法( 2 )

时间:2015-10-05 23:16:06      阅读:3403      评论:0      收藏:0      [点我收藏+]

标签:

韩家炜书中在介绍基于聚类的异常值检测中的第二种方法提到: 如果某对象与最近的簇的距离非常远,则该对象为Outlier。这句话其实有两个意思,第一个意思假定某对象在cluster A中,则它必定距离A簇比较近( 一般指的是球型的cluster )。第二个意思假定某对象是未知的点,距离A簇最近。其实在使用中,大部分情况我们可以把这两个场景合二为一。在本文中,我们假定对象已经在cluster A中。

 

本文基于聚类的异常值检测的算法思路是:

1 ) 用聚类算法做cluster

2 ) 计算cluster中每个点到该cluster中心的距离( 假定是欧式距离 ), 再得出cluster中所有点到中心的平均距离l

3 ) 用户设置一个spec参数

4 ) 计算cluster中的每个点到cluster中心的距离s,如果s / l > spec,则该点判断为outlier。

所以这个方法依赖于聚类算法的结果以及后续那个spec参数的设置。

 

为了更好地说明问题,我在这里用了个测试数据集。它主要由3个cluster组成,密度差不多,每个cluster周围有些outlier,有两个global的outlier。

技术分享

 

首先做聚类。这里就采用最简单的KMeans。KMeans实在太有名,原理就不描述了( 也许以后会分享KMeans++或者优化算法结合KMeans )。这里K设置为3,聚类结果如下:

技术分享

 

三个cluster,两个global的outlier分别被分到两个不同的cluster里去了。每个cluster的中心用黄色的五角星标出。聚类完成后,接着对每个cluster做异常值检测,代码如下:

import numpy as np
import scipy.spatial.distance as ssd


def calClusterDiffOutlier( clusterSamples, clusterCenter, diffSpec = 2 ):

    dists = np.array( [ ssd.euclidean( sample, clusterCenter ) 
                            for sample in clusterSamples ] )    
    lco = np.mean( dists )
    labels = np.array( dists / lco > diffSpec, np.int )
    
    return labels

这里的spec我们设置为2。也就是说到中心的距离,超过平均中心距离的一倍。算法的结果如下( 绿色的为outlier ):

技术分享

 

两个global的outlier抓到了。大部分的local outlier也抓到了,有几个没有检测到。有部分outlier处于灰色地带,不同的人会有不同的看法,有些人觉得是outlier,而有些人则认为不是。无论如何,这是一个思路,先用无监督的方法获取数据的大致结构,然后再这个结构上,去除那些global或者local的outlier。

 

刚刚我们用了KMeans, 我们换个思路用MeansShift也可以解决这个问题,只是MeanShift算法会给我们一些不同的见解。关于MeanShift方法,我以后会分享,当然读者可以参考http://www.cnblogs.com/liqizhou/archive/2012/05/12/2497220.html,这文章写得不错。MeanShift得结果如下:

技术分享

由于MeansShift方法的特性,outlier有可能被当作单独一个cluster来处理。这个时候直接用前面的异常值处理方法就会出问题。但是这里的结果又给我们一个新的启发。我们发现如果出现很小的cluctser,则整个custer可能都是outlier! 所以基于这样的认识,我们可以将前面基于聚类的方法再扩充下,用来处理小簇或者说稀疏簇:

def detectOutliers( X, clusterLabels, clusterCenters, minPts = 3, diffSpec = 2 ):
    
    labels = np.zeros( len( clusterLabels ) )
    uniqLabels = np.unique( clusterLabels )

    for label in uniqLabels:
        mask = clusterLabels == label
        if np.sum( mask ) <  minPts: # small cluster --> all are outliers 
            labels[ mask ] = 1
            continue
        
        clusterCenter = clusterCenters[ label ]
        results = calClusterDiffOutlier( X[ mask ], clusterCenter, diffSpec )
        labels[ mask ] = results
   
return labels

我们给算法加一个参数minPts,意思是如果一个cluster中样本数小于这个minPts,则该簇全为outlier。这样就能处理像MeanShift或产生单样本cluster的情况。而且很小的簇,很可能就是outlier,该方法的结果如下:

技术分享

基于MeanShift的方法的中心貌似比KMeans产生的中心更合理点,而且也消除了global outlier对簇的影响。所以cluster附近的outlier检测得更加合理了。

 

当我们欣喜结果不错得时候,我们用这个方法来看看上次文章中的数据。显然那篇文章只有一个cluster。

技术分享

非球状的cluster的outlier检测显然不能直接用这个方法来完成。

 

前途是光明的,道路是曲折的。方法是死的,人是活的。我们已经介绍了一些方法,基于聚类的、基于近邻的,基于统计的。用合适的方法,或者加以组合、改进。任何方法都给我们一个idea!关键是idea!而不是算法固定的流程! 比如聚类算法CURE也包含了聚类过程中outlier的处理,以后我们会专门做分享。

 

如果文章中的测试数据有人感兴趣的话,可以留言联系。

基于聚类的异常值检测方法( 2 )

标签:

原文地址:http://www.cnblogs.com/zhuyubei/p/4856363.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!