第10.10节 基于密度的聚类算法
各位朋友大家好,欢迎来到月来客栈,我是掌柜空字符。
本期推送内容目录如下,如果本期内容对你有所帮助,欢迎点赞、转发支持掌柜!
10.10 基于密度的聚类算法 10.10.1 算法思想 10.10.2 核心概念 10.10.3 算法原理 10.10.4 计算示例 10.10.5 优缺点 10.10.6 从零实现DBSCAN算法 10.10.7 使用示例 10.10.8 小结 引用
10.10 基于密度的聚类算法
在前面几节内容中,笔者陆续介绍了3种常见聚类算法的原理与实现,包括原始的-means聚类算法、-means++聚类算法以及基于特征权重的-means聚类算法 ,并且这3种都算是基于-means框架的聚类算法,也就是说它们本质上解决的都是一类数据的聚类问题。但是,在实际场景中可能存在一些其它簇结构形式的数据,例如像图10-19所示的数据。
如图10-20所示,左右两边均是采用-means++算法聚类后的结果(不同颜色表示聚类算法给出的聚类结果),可以看到由于类-means聚类算法的特性,其在这类数据集上的表现并不好。那对于类似图10-20这样的数据集应该采用什么样的聚类算法进行聚类呢?
10.10.1 算法思想
基于密度的聚类算法全称为Density-based spatial clustering of applications with noise (DBSCAN) [1],1996年由Martin Ester等人提出[2]。从算法的全称可以看出,DBSCAN算法的原理除了是基于密度这一特性外,它还能有效地发掘数据中的异常样本。
基于密度的聚类算法其核心思想是它可以根据样本之间的密度(紧凑程度)来对样本进行聚类处理。例如对于图10-20中所示的两种异形数据来说,两种类别之间有着明显空白之处(密度小),而对于各类别内部的样本来说样本之间分布却非常紧凑(密度大),因此只需要将所有各自相互紧邻的样本点划分为不同的簇结构即可完成整个聚类过程。同时还可以发现,基于这样的聚类思想不管待聚类的数据集是什么样的分布形式,都可以很好的对其进行聚类处理。
10.10.2 核心概念
在正式介绍DBSCAN算法的原理之前,笔者先来介绍算法中几个非常重要的核心概念,核心样本、直接可达、可达和异常样本。在DBSCAN算法中还有两个非常关键的超参数,半径和最小样本数,如图10-21所示。
1)核心样本(core point):以样本点为圆心为半径作圆,如果圆域内至少存在个样本(包括自身),则称为核心样本,否则称为非核心样本(non-core point)。
2)直接可达(directly reachable ):以核心样本为圆心为半径,圆域内的所有样本点对于来说直接可达。
如图10-21所示,以样本点为圆心为半径作圆,圆域内恰好存在个样本,则此时称为核心样本,称称为非核心样本。同时,称圆域内的所有样本对于核心样本来说直接可达。值得注意的一点是,直接可达是针对核心样本而言,例如图10-21中样本对于来说直接可达,但不能说成样本对于来说直接可达。
3)可达(reachable):如果存在样本点,且对于来说直接可达,那么称对于来说可达。注意,这里暗含着均为核心样本。
4)异常点(outliers):如果样本点对于其它任何样本点来说均不直接可达,则称样本点为异常点。
10.10.3 算法原理
在介绍完DBSCAN算法中的几个核心概念后我们再来学习DBSCAN算法的原理就相对简单一些了。总的来说DBSCAN算法的聚类过程大致可以分为两大步:先计算得到每个样本点以为半径圆域内的样本点,并得到相应的核心样本点;然后再依次遍历每个样本,根据其是否为核心样本来对直接可达的样本点进行簇类别的划分。
具体地,整个聚类过程的详细步骤可以通过如下伪代码来进行表示:
1 def dbscan():
2 label_num = 0
3 labels =[-1,...,-1] # 长度为样本个数,均为-1的簇标签向量
4 for i = 0,...,n # 遍历所有样本点
5 if 样本i已经访问过 或 者样本i不是核心样本:
6 continue
7 idx = i
8 while True:
9 if 第idx个样本没有被访问过:
10 为当前样本labels[idx]赋予一个簇类别label_num
11 if 当前样本idx为核心样本:
12 for j = 0,...,k : # 遍历第idx个核心样本周围的所有样本
13 if 第j个样本未被访问过:
14 将样本j对应的索引加入到栈s中
15 if 栈为空:
16 退出当前循环
17 idx = s.pop() # 取栈中最后一个元素并删除
18 label_num += 1
最后,簇标签向量labels
中保存的便是每个样本点所属的簇编号;同时如果labels[i]
为-1,则表示第i
个样本点为异常点。
10.10.4 计算示例
在介绍完DBSCAN算法的原理过后,笔者下面再来通过一个实际的例子对算法的整个聚类过程进行示例,以便大家能够理解得更加透彻。
不失一般性,以1-12的顺序遍历每个样本点。此时,由于第1个样本点为非核心样本所以继续遍历第2个样本。
由于第2个样本为核心样本,且第2个样本未被访问过,记label[2]=0
,进一步继续遍历第2个样本点周围的3个样本点1、3和6。因为这3个样本点均未被访问过,所以放入栈中s=[1,3,6]
。由于此时栈不为空,所以取栈s
中的最后一个样本出栈,此时s=[1,3]
。因为第6个样本点未被访问过,所以记label[6]=0
,进一步因为第6个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以取栈s
中的最后一个样本出栈,此时s=[1]
。因为第3个样本点为核心样本,且第3个样本未被访问过,记label[3]=0
,进一步继续遍历第3个样本点周围的3个样本点2、4和5。由于第2个样本点之前被访问过,所以只需要将4和5放入栈中,即s=[1,4,5]
。由于此时栈不为空,所以取栈s
中的最后一个样本出栈,此时s=[1,4]
。因为第5个样本点未被访问过,所以记label[5]=0
,进一步因为第5个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以取栈s
中的最后一个样本出栈,此时s=[1]
。因为第4个样本点未被访问过,所以记label[4]=0
,进一步因为第4个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈不为空,所以继续取栈s
中的最后一个样本出栈,此时s=[]
。因为第1个样本点未被访问过,所以记label[1]=0
,进一步因为第1个样本不是核心样本所以不用遍历其周围的样本点。由于此时栈为空,所以退出当前循环。
为你认可的知识付费,欢迎订阅本专栏阅读更多优质内容!
到此,图10-23中第1个簇已经聚类完毕,即图中的红色样本点。