首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

基于密度的聚类算法原理与实现

各位朋友大家好,欢迎来到月来客栈,我是掌柜空字符。

1 引言

在前面的几篇文章中,掌柜陆续介绍了3种常见聚类算法的原理与实现,包括原始的Kmeans聚类算法[1]、Kmeans++聚类算法 [2]以及基于特征权重的WKmeans聚类算法 [3] ,并且这3种都算是基于Kmeans框架的聚类算法,也就是说它们本质上解决的都是一类数据的聚类问题。但是,在实际场景中可能存在一些其它簇结构形式的数据,例如像图1所示的数据。

图 1. 异形数据集

如图1所示,对于这两种异形的数据集来说,不管是采用上面3种聚类算法中的哪一种,最后得到的结果都会很差,因为类Kmeans聚类算法根本就不适合对这种数据进行聚类处理。如果一定要用类Kmeans聚类算法来进行聚类,那么将会得到如图2所示的结果。

图 2. 异形数据集K-means聚类结果

如图2所示,左右两边均是采用Kmeans++算法聚类后的结果(不同颜色表示聚类算法给出的聚类结果),可以看到由于类Kmeans聚类算法的特性,其在这类数据集上的表现并不好。那对于类似图2这样的数据集应该采用什么样的聚类算法进行聚类呢?

想象一下现在有这样一种聚类算法,它可以根据样本之间的密度(紧凑程度)来对样本进行聚类处理。例如对于图2中所示的两种异形数据来说,两种类别之间有着明显空白之处(密度小),而对于各类别内部的样本来说样本之间分布却非常紧凑(密度大),因此只需要将所有各自相互紧邻的样本点划分为不同的簇结构即可完成整个聚类过程。同时还可以发现,基于这样的聚类思想不管待聚类的数据集是什么样的分布形式,都可以很好的对其进行聚类处理。

在接下来的这篇文章中,掌柜将会介绍一种基于密度的聚类算法来解决这类问题。

2 基于密度的聚类算法

基于密度的聚类算法全称为Density-based spatial clustering of applications with noise (DBSCAN) [1],1996年由Martin Ester等人提出[2]。从算法的全称可以看出,DBSCAN算法的原理除了是基于密度这一特性外,它还能有效地发掘数据中的异常样本。

2.1 核心概念

在正式介绍DBSCAN算法的原理之前,掌柜先来介绍算法中几个非常重要的核心概念,核心样本、直接可达、可达和异常样本。在DBSCAN算法中还有两个非常关键的超参数,半径

和最小样本数

,如图3所示。

图 3. 核心样本原理图

1)核心样本(core point):以样本点

为圆心

为半径作圆,如果圆域内至少存在

个样本(包括

自身),则称

为核心样本,否则称为非核心样本(non-core point)。

2)直接可达(directly reachable ):以核心样本

为圆心

为半径,圆域内的所有样本点对于

来说直接可达。

如图3所示,以样本点

为圆心

为半径作圆,圆域内恰好存在

个样本,则此时称

为核心样本,称

称为非核心样本。同时,称圆域内的所有样本对于核心样本

来说直接可达。值得注意的一点是,直接可达是针对核心样本而言,例如图3中样本

对于

来说直接可达,但不能说成样本

对于

来说直接可达。

3)可达(reachable):如果存在样本点

,且

对于

来说直接可达,那么称

对于

来说可达。注意,这里暗含着

均为核心样本。

4)异常点(outliers):如果样本点

对于其它任何样本点来说均不直接可达,则称样本点

为异常点。

图 4. 样本可达原理图

如图4所示,

均为核心样本,且

对于

来说直接可达,

对于

来说直接可达,

对于

来说直接可达,因此

对于

来说可达。同时,由于样本点

既不是核心样本,且对于其它样本来说也不直接可达,因此

为异常点。

2.2 算法原理

在介绍完DBSCAN算法中的几个核心概念后我们再来学习DBSCAN算法的原理就相对简单一些了。总的来说DBSCAN算法的聚类过程大致可以分为两大步:先计算得到每个样本点以

为半径圆域内的样本点,并得到相应的核心样本点;然后再依次遍历每个样本,根据其是否为核心样本来对直接可达的样本点进行簇类别的划分。

具体地,整个聚类过程的详细步骤可以通过如下伪代码来进行表示:

最后,簇标签向量中保存的便是每个样本点所属的簇编号;同时如果为-1,则表示第个样本点为异常点。

2.3 计算示例

在介绍完DBSCAN算法的原理过后,掌柜下面再来通过一个实际的例子对算法的整个聚类过程进行示例,以便大家能够理解得更加透彻。

图 5. dbscan聚类过程示例图

如图5所示,图中一共包含有12个样本点,

,且同时掌柜已经以每个样本点为圆心

为半径进行了作圆。同时,根据图示可知样本2、3和8均为核心样本。下面,掌柜开始来一步步模拟DBSCAN算法的整个聚类过程。在阅读下面内容时最好结合2.2节中的伪代码。

不失一般性,以1-12的顺序遍历每个样本点。此时,由于第1个样本点为非核心样本所以继续遍历第2个样本。

由于第2个样本为核心样本,且第2个样本未被访问过,记,进一步继续遍历第2个样本点周围的3个样本点1、3和6。因为这3个样本点均未被访问过,所以放入栈中。由于此时栈不为空,所以取栈中的最后一个样本出栈,此时。因为第6个样本点未被访问过,所以记,进一步因为第6个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以取栈中的最后一个样本出栈,此时。因为第3个样本点为核心样本,且第3个样本未被访问过,记,进一步继续遍历第3个样本点周围的3个样本点2、4和5。由于第2个样本点之前被访问过,所以只需要将4和5放入栈中,即。由于此时栈不为空,所以取栈中的最后一个样本出栈,此时。因为第5个样本点未被访问过,所以记,进一步因为第5个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以取栈中的最后一个样本出栈,此时。因为第4个样本点未被访问过,所以记,进一步因为第4个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以继续取栈中的最后一个样本出栈,此时。因为第1个样本点未被访问过,所以记,进一步因为第1个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈为空,所以退出当前循环。

到此,图5中第1个簇已经聚类完毕,即图中的红色样本点。

进一步,开始遍历第3个样本点。由于第3个样本点已近被访问过,所以开始遍历第4个样本点。同时,第4、5、6个样本点均在上面被访问过,于是开始遍历第7个样本点。由于第7个样本不是核心样本,所以继续遍历第8个样本。

由于第8个样本为核心样本,且第8个样本未被访问过,记,进一步继续遍历第8个样本点周围的3个样本点7、9和10。因为这3个样本点均未被访问过,所以放入栈中。由于此时栈不为空,所以取栈中的最后一个样本出栈,此时。因为第10个样本点未被访问过,所以记,进一步因为第10个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以取栈中的最后一个样本出栈,此时。因为第9个样本点未被访问过,所以记,进一步因为第9个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以取栈中的最后一个样本出栈,此时。因为第7个样本点未被访问过,所以记,进一步因为第7个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈为空,所以退出当前循环。

到此,图5中第2个簇已经聚类完毕,即图中的蓝色样本点。同时,绿色样本表示异常样本。

进一步,开始遍历第9个样本。由于第9个样本点已经被访问过,同理第10个样本点也被访问过,所以开始遍历第11个样本点。由于第11个样本点不是核心样本,所以继续遍历第12个样本点。同样,虽然第12个样本点未被访问过,但是其同样不是核心样本点,所以退出循环,整个遍历过程结果。

最终,将会得到如下所示的聚类:

以上示例就是DBSCAN算法的整个聚类流程。大家同时还可以去网站[3]来看查看DBSCAN算法的动态聚类过程。

2.3 优缺点

在介绍完整个DBSCAN的算法原理后我们再来看看它的一些优缺点。对于DBSCAN算法来说,其最大的有点在于它能够对任何形状的数据集进行距离,其次在聚类之前并不需要指定簇的数量,这一点同类Kmeans算法不同。同时,在聚类结束后DBSCAN算法还能顺便从数据集中找到异常样本,而这对于某些异常检测场景来说是非常有用的。

虽然DBSCAN算法有着上述明显的优点,但是也存在的不少的缺点,其中最明显的就是慢。DBSCAN聚类算法慢主要体现在两个方面,一方面是根据半径

来查找每个样本点周围样本的过程;另一方面则是对每个样本点进行簇划分的过程。当然最慢的还前者,如果采用暴力法则需要

的时间复杂度,采用类似于kd树这样的方法则也需要

的时间复杂度,这相较于类Kmeans聚类算法

的时间复杂度来说明显慢了不少。同时,在聚类过程中如果簇与簇边界上的样本对于不同簇中的核心样本来说均是直接可达的,那么其最终归属于哪个簇则取决于样本的遍历顺序。例如在图5中,如果6行样本点也是核心样本,那么7号样本点属于左边的簇还是右边的簇则取决于是先遍历到6号样本还是8号样本。除此之外,如果某个数据集各个簇内部的密度差异过大,那么DBSCAN算法聚类结果将会大受影响。最后,对于半径

的选择也需要一定的经验。

到此,对于DBSCAN聚类算法的整个原理部分就介绍完了,下面掌柜再来介绍如何一步一步进行实现。

3 代码实现

根据上面介绍的原理可知,DBSCAN聚类算法主要分为两大步:第一步是搜索得到每个样本点周围以

为半径

为阈值的样本点;第二步便是依次遍历每个样本,根据其是否为核心样本来对直接可达的样本点进行簇类别的划分。下面掌柜便开始来分别对这两部分的代码实现进行介绍。

3.1 样本搜索实现

作为实现DBSCAN聚类算法的关键一步,我们首先需要知道每个样本以自身为圆心

为半径,在这个圆域内有多少样本点。当然,最容易想到也但也最低效的做法就是依次遍历每个样本点,然后再分别计算它们与自身的距离并判断是否在半径

以内。不过这里我们可以沿用之前在介绍KNN算法时所使用的kd树来完成这一过程。

对于KNN算法来说,其核心在于如何快速找到距离自身最近的K个样本点。因此,只需要对这部分代码稍加改动就能够实现如何快速找到距离自身半径为

圆域内的样本点。

首先,我们定义一个,并继承自之前实现的类,代码如下:

在上述代码中,第1行表示原始的数据集;表示

距离中的参数

,当时表示使用欧氏距离;表示半径。在根据上述初始化方法实例化类对象后,便得到了一个根据构造而成的kd树。

下面,我们再根据kd树中的K近邻搜索代码修改为此处所需要的根据距离来搜索的代码即可,代码如下:

在上述代码中,第1行表示传入的一个样本点,形状为;第5-23行则是递归遍历整个kd树并完成找寻距离半径为圆域内的样本点。具体的搜索原理大家可以参考之前介绍KNN算法部分的内容,掌柜在这里就不再赘述。

所有完整代码均可从本仓库获取:https://github.com/moon-hotel/MachineLearningWithMe

进一步,我们需要实现一个方法来循环完成对所有样本点周围样本的搜索过程,代码如下:

在上述代码中,第1行表示传入的所有样本点,形状为;第4-7行是遍历每个样本点,并找到每个样本点周围的样本点;第8-10行是取每个样本点周围的样本点以及对应的索引;第14行则是返回整体最后的结果。

到此,我们就完成了对于样本点周围样本的搜索,示例如下:

上述代码运行结束后的结果如下:

上述结果中,列表中的每个元素表示距离中对应样本在半径为以内的样本点的索引。

3.2 样本划分实现

在完成每个样本点以自身为圆心

为半径圆域内样本的搜索过程后,便可以进一步实现样本的簇划分过程。在这之前,我们需要先定义一个辅助函数(即实现2.1节中的伪代码)来完成整个划分过程,实现代码如下:

在上述代码中,第1行是一个只含0和1,形状为的向量, 表示第个样本为核心样本;为一个列表,表示第个样本以

为半径周围存在样本的索引;为一个形状为的向量, 表示第个样本所属的簇编号,初始情况全部为-1,即没有被访问过。

第2行中用来记录当前时刻的簇编号;第3行中是深度优先遍历样本时的栈;第4行构造索引开始遍历每一个样本;第5-6行判断如果该样本已经访问过或者不是核心样本则继续遍历下一个样本,即只有没访问过且为核心样本才继续往下进行;第8行开始进行深度优先遍历;第9-10行判断如果第个样本没有被访问过,则为第样本赋予一个簇类别;第11--12行判断如果第个样本是核心样本,则取以第个核心样本为圆心,周围半径

内的所有样本点;第13-16行开始遍历核心样本点周围的所有样本,如果该样本没有被访问过则将其放入到栈中;第17行判断如果栈为空,则表示当前簇中所有样本都访问完毕,即此轮深度遍历结束;第19行是返回栈中最后一个元素并将其从栈中删除;第20行是累加簇编号,并进入下一个簇的划分过程中。

到此,对于DBSCAN算法中最重要的两个部分就实现完毕了。下面,我们需要将这两部分结合起来实现最终的DBSCAN算法。

3.3 DBSCAN实现

在有了前面的铺垫之后,DBSCAN算法实现起来就变得非常容易了。首先我们仍旧需要先定义一个类并实现相应的构造函数,实现代码如下:

在上述代码中,第2行中表示半径,表示

距离中的参数

,表示构成核心样本的最小样本数。

进一步,利用3.2节中实现的代码来完成整个聚类过程的实现,代码如下:

在上述代码中,第1行中表示数据集,形状为;第2行是实例化一个类对象并同时完成了kd树的构建;第4行是返回得到中每个变量以自身为圆心为半径圆域内的所有样本点;第8行是得到每个样本点周围圆域内样本的个数;第9行是初始化一个全为-1的簇标记向量;第10行是得到核心样本的索引向量,表示第个样本为核心样本;第11行是对每个样本进行簇类别的划分,并得到最终的结果。

所有完整代码均可从本仓库获取:https://github.com/moon-hotel/MachineLearningWithMe

到此,对于整个DBSCAN算法的实现过程就介绍完了,下面来看使用示例。

3.4 使用示例

在完成DBSCAN算法的实现过程后,可以通过如下所示的方式来进行使用,代码如下:

在上述代码中,第3-4行用来构造数据集并进行标准化;第5-9行是分别用sklearn中的算法和上面实现的做对比;第12-19行则是对两者预测后的结果进行可视化。

在上述代码运行结束后将会得到如下所示的输出结果:

根据上述结果可以发现,和在调整兰德系数这一指标上的结果没有任何却别;同时可以发现算法在聚类过程中主要时间都花费在了第一步计算样本点周围的样本上了。进一步,根据聚类标签还可以得到如图6所示的可视化结果

图 6. DBSCAN与MyDBSCAN算法聚类结果对比图

4 总结

在这篇文章中,掌柜首先通过一个引例介绍了传统基于Kmeans框架聚类算法的不足之处,并一步步引出了基于密度的聚类算法DBSCAN;然后详细介绍了DBSCAN算法的核心概念和基本原理,还通过一个实际的计算示例来进一步介绍了DBSCAN算法的整个聚类过程;最后,掌柜一步步详细地介绍了DBSCAN算法的实现过程,并通过一个实际的示例来介绍了DBSCAN算法的使用方式。

本次内容就到此结束,感谢您的阅读!如果你觉得上述内容对你有所帮助,欢迎点赞分享!青山不改,绿水长流,我们月来客栈见

引用

[1] https://en.wikipedia.org/wiki/DBSCAN

[2] Ester M, Kriegel H P, Sander J, et al. A density-based algorithm for discovering clusters in large spatial databases with noise[C]//kdd. 1996, 96(34): 226-231.

[3] https://www.naftaliharris.com/blog/visualizing-dbscan-clustering/

[4] https://scikit-learn.org/stable/modules/clustering.html#dbscan

[5]代码仓库:https://github.com/moon-hotel/MachineLearningWithMe

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230104A00HXS00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券