前言
DBSCAN(Density-Based Spatial Clustering of Applications with Noise
,具有噪声的基于密度的空间聚类方法)是一种很典型的密度聚类算法,和 K-Means
, BIRCH
这些一般只适用于凸样本集的聚类相比,DBSCAN
既可以适用于凸样本集,也可以适用于非凸样本集。
一、基于密度的聚类思想
DBSCAN
是一种基于密度的聚类算法,这类密度聚类算法一般假定类别可以通过样本分布的紧密程度决定。同一类别的样本,他们之间是紧密相连的,也就是说,在该类别任意样本周围不远处一定有同类别的样本存在。
通过将紧密相连的样本划为一类,这样就得到了一个聚类类别。通过将所有各组紧密相连的样本划为各个不同的类别,则我们就得到了最终的所有聚类类别结果。
二、DBSCAN密度定义
在上一小节我们定性描述了密度聚类的基本思想,本节我们就看看DBSCAN
是如何描述密度聚类的。DBSCAN
是基于一组邻域来描述样本集的紧密程度的,参数(
ϵ
\epsilon
ϵ, MinPts
)用来描述邻域的样本分布紧密程度。其中,
ϵ
\epsilon
ϵ 描述了某一样本的邻域距离阈值,MinPts
描述了某一样本的距离为
ϵ
\epsilon
ϵ 的邻域中样本个数的阈值。
假设我的样本集是
D
=
(
x
1
,
x
2
,
.
.
.
,
x
m
)
D=(x_1, x_2, ..., x_m)
D=(x1,x2,...,xm) ,则DBSCAN
具体的密度描述定义如下:
- ① ϵ \epsilon ϵ - 邻域:对于 x j ∈ D x_j \in D xj∈D ,其 ϵ \epsilon ϵ - 邻域包含样本集 D D D 中与 x j x_j xj 的距离不大于 ϵ \epsilon ϵ 的子样本集,即 N ϵ ( x j ) = { x i ∈ D ∣ d i s t a n c e ( x i , x j ) ≤ ϵ } N_{\epsilon}(x_j) = \{x_i \in D | distance(x_i, x_j) \leq \epsilon \} Nϵ(xj)={xi∈D∣distance(xi,xj)≤ϵ} ,这个子样本集的个数记为 ∣ N ϵ ( x j ) ∣ |N_{\epsilon}(x_j)| ∣Nϵ(xj)∣ ;
- ② 核心对象:对于任一样本
x
j
∈
D
x_j \in D
xj∈D ,如果其
ϵ
\epsilon
ϵ - 邻域对应的
N
ϵ
(
x
j
)
N_{\epsilon}(x_j)
Nϵ(xj) 至少包含
MinPts
个样本,即如果 ∣ N ϵ ( x j ) ∣ ≥ M i n P t s |N_{\epsilon}(x_j)| \geq MinPts ∣Nϵ(xj)∣≥MinPts,则 x j x_j xj 是核心对象; - ③ 密度直达:如果 x i x_i xi 位于 x j x_j xj 的 ϵ \epsilon ϵ - 邻域中,且 x j x_j xj 是核心对象,则称 x i x_i xi 由 x j x_j xj 密度直达。注意反之不一定成立,即此时不能说 x j x_j xj 由 x i x_i xi 密度直达,除非且 x i x_i xi 也是核心对象;
- ④ 密度可达:对于 x i x_i xi 和 x j x_j xj ,如果存在样本序列 p 1 , p 2 , . . . , p T p_1, p_2, ..., p_T p1,p2,...,pT ,满足 p 1 = x i , p T = x j p_1=x_i, p_T=x_j p1=xi,pT=xj ,且 p t + 1 p_{t+1} pt+1 由 p t p_t pt 密度直达,则称 x j x_j xj 由 x i x_i xi 密度可达。也就是说,密度可达满足传递性。此时序列中的传递样本 p 1 , p 2 , . . . , p T − 1 p_1, p_2, ..., p_{T−1} p1,p2,...,pT−1 均为核心对象,因为只有核心对象才能使其他样本密度直达。注意密度可达也不满足对称性,这个可以由密度直达的不对称性得出;
- ⑤ 密度相连:对于 x i x_i xi 和 x j x_j xj ,如果存在核心对象样本 x k x_k xk ,使 x i x_i xi 和 x j x_j xj 均由 x k x_k xk 密度可达,则称 x i x_i xi 和 x j x_j xj 密度相连。注意密度相连关系是满足对称性的。
从下图可以很容易理解上述定义,图中 MinPts=5
,红色的点都是核心对象,因为其
ϵ
\epsilon
ϵ - 邻域至少有 5
个样本。黑色的样本是非核心对象。所有核心对象密度直达的样本在以红色核心对象为中心的超球体内,如果不在超球体内,则不能密度直达。图中用绿色箭头连起来的核心对象组成了密度可达的样本序列。在这些密度可达的样本序列的
ϵ
\epsilon
ϵ - 邻域内所有的样本相互都是密度相连的。
有了上述定义,DBSCAN
的聚类定义就简单了。
三、DBSCAN密度聚类思想
DBSCAN
的聚类定义很简单:由密度可达关系导出的最大密度相连的样本集合,即为我们最终聚类的一个类别,或者说一个簇。
这个DBSCAN
的簇里面可以有一个或者多个核心对象。如果只有一个核心对象,则簇里其他的非核心对象样本都在这个核心对象的
ϵ
\epsilon
ϵ -邻域里;如果有多个核心对象,则簇里的任意一个核心对象的
ϵ
\epsilon
ϵ -邻域中一定有一个其他的核心对象,否则这两个核心对象无法密度可达。这些核心对象的
ϵ
\epsilon
ϵ -邻域里所有的样本的集合组成的一个DBSCAN
聚类簇。
那么怎么才能找到这样的簇样本集合呢?DBSCAN
使用的方法很简单,它任意选择一个没有类别的核心对象作为种子,然后找到所有这个核心对象能够密度可达的样本集合,即为一个聚类簇。接着继续选择另一个没有类别的核心对象去寻找密度可达的样本集合,这样就得到另一个聚类簇。一直运行到所有核心对象都有类别为止。
基本上这就是DBSCAN
算法的主要内容了,是不是很简单?但是我们还是有三个问题没有考虑。
第一个是一些异常样本点或者说少量游离于簇外的样本点,这些点不在任何一个核心对象的周围,在DBSCAN
中,我们一般将这些样本点标记为噪音点。
第二个是距离的度量问题,即如何计算某样本和核心对象样本的距离。在DBSCAN
中,一般采用最近邻思想,采用某一种距离度量来衡量样本距离,比如欧式距离。这和KNN
分类算法的最近邻思想完全相同。对应少量的样本,寻找最近邻可以直接去计算所有样本的距离,如果样本量较大,则一般采用KD-tree
或者ball-tree
来快速的搜索最近邻。
第三种问题比较特殊,某些样本可能到两个核心对象的距离都小于
ϵ
\epsilon
ϵ ,但是这两个核心对象由于不是密度直达,又不属于同一个聚类簇,那么如果界定这个样本的类别呢?一般来说,此时DBSCAN
采用先来后到,先进行聚类的类别簇会标记这个样本为它的类别。也就是说DBSCAN
的算法不是完全稳定的算法。
四、DBSCAN聚类算法的实现流程
输入:样本集
D
=
(
x
1
,
x
2
,
.
.
.
,
x
m
)
D = (x_1, x_2, ..., x_m)
D=(x1,x2,...,xm),邻域参数(
ϵ
\epsilon
ϵ ,MinPts
),样本距离度量方式。
输出: 簇划分(样本所属类别) C C C 。
- ① 初始化核心对象集合 Ω = ϕ \Omega = \phi Ω=ϕ ,初始化聚类簇数 k = 0 k=0 k=0 ,初始化未访问样本集合 Γ = D \Gamma = D Γ=D ,簇划分 C = ϕ C = \phi C=ϕ ;
- ② 对于
j
=
1
,
2
,
.
.
.
,
m
j = 1, 2, ..., m
j=1,2,...,m ,按下面的步骤找出所有的核心对象:
a) 通过距离度量方式,找到样本 x j x_j xj 的 ϵ \epsilon ϵ - 邻域子样本集 N ϵ ( x j ) N_{\epsilon}(x_j) Nϵ(xj) ;
b) 如果子样本集的样本个数满足 ∣ N ϵ ( x j ) ∣ ≥ M i n P t s |N_{\epsilon}(x_j)| \geq MinPts ∣Nϵ(xj)∣≥MinPts , 将样本 x j x_j xj 加入核心对象样本集合: Ω = Ω ∪ { x j } \Omega = \Omega \cup \{x_j\} Ω=Ω∪{xj} 。 - ③ 如果核心对象集合 Ω = ϕ \Omega = \phi Ω=ϕ,则算法结束,否则转入步骤④;
- ④ 在核心对象集合 Ω \Omega Ω 中,随机选择一个核心对象 o o o ,初始化当前簇核心对象队列 Ω c u r = { o } \Omega_{cur} = \{o\} Ωcur={o} ,初始化类别序号 k = k + 1 k = k+1 k=k+1,初始化当前簇样本集合 C k = { o } C_k = \{o\} Ck={o} ,更新未访问样本集合 Γ = Γ − { o } \Gamma = \Gamma − \{o\} Γ=Γ−{o} ;
- ⑤ 如果当前簇核心对象队列 Ω c u r = ϕ \Omega_{cur} = \phi Ωcur=ϕ ,则当前聚类簇 C k C_k Ck 生成完毕,更新簇划分 C = { C 1 , C 2 , . . . , C k } C=\{C_1, C_2, ..., C_k\} C={C1,C2,...,Ck} ,更新核心对象集合 Ω = Ω − C k \Omega = \Omega − C_k Ω=Ω−Ck ,转入步骤③;
- ⑥ 在当前簇核心对象队列 Ω c u r \Omega_{cur} Ωcur 中取出一个核心对象 o ′ o' o′ ,通过邻域距离阈值 ϵ \epsilon ϵ 找出所有的 ϵ \epsilon ϵ - 邻域子样本集 N ϵ ( o ′ ) N_{\epsilon}(o') Nϵ(o′),令 Δ = N ϵ ( o ′ ) ∩ Γ \Delta = N_{\epsilon}(o') \cap \Gamma Δ=Nϵ(o′)∩Γ ,更新当前簇样本集合 C k = C k ∪ Δ C_k = C_k \cup \Delta Ck=Ck∪Δ ,更新未访问样本集合 Γ = Γ − Δ \Gamma = \Gamma − \Delta Γ=Γ−Δ ,更新 Ω c u r = Ω c u r ∪ ( Δ ∩ Ω ) − o ′ \Omega_{cur} = \Omega_{cur} \cup (\Delta \cap \Omega) − o' Ωcur=Ωcur∪(Δ∩Ω)−o′ ,转入步骤⑤。
输出结果为: 簇划分 C = { C 1 , C 2 , . . . , C k } C = \{C_1, C_2, ..., C_k\} C={C1,C2,...,Ck} 。
接下来我们使用一个简单的案例使用DBSCAN完成对数据样本的聚类:
首先构建数据集:
import numpy as np
from sklearn.datasets import make_blobs
from sklearn import metrics
from matplotlib import pyplot as plt
X, y_true = make_blobs(n_samples=300, centers=4,
cluster_std=0.50, random_state=0)
# sample visualization
plt.scatter(X[:, 0], X[:, 1], marker='o')
plt.show()
构造的样本显示如下:
接下来使用 sklearn
中的 DBSCAN
API
对它进行聚类演示:
API中主要参数:
class sklearn.cluster.DBSCAN(eps=0.5, *, min_samples=5, metric='euclidean', metric_params=None, algorithm='auto', leaf_size=30, p=None, n_jobs=None)
eps
:一个样本被判定为在另外一个样本的领域内的两个样本之间的最大距离,默认值为 0.5;min_samples
:一个样本点被判定为聚类中心时,其邻域内(包括其自身)所包含的样本点最小数目(或者最小权重),默认值为 5;metric
:指定距离度量方法,必须是sklearn.metrics.pairwise_distances
中的一种,默认为euclidean
;metric_params
:距离度量方法的额外指定关键字参数,默认为 None ;- algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’} 中的一种, 默认为 ’auto’ ;
leaf_size
:当样本空间表示算法为 BallTree 或者 cKDTree 时,指定的叶子节点的最大数量,默认为 30;p
:衡量两个样本点之间的闵可夫斯基距离的指数,默认为 2 。
from sklearn.cluster import DBSCAN
db = DBSCAN(eps=0.4, min_samples=10).fit(X)
# get clustered samples' mask without noise
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
# get number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(db.labels_)) - (1 if -1 in db.labels_ else 0)
# Plot result
# Black removed and is used for noise instead.
unique_labels = set(db.labels_)
unique_labels.remove(-1)
colors = ['y', 'b', 'g', 'r']
for k, col in zip(unique_labels, colors):
class_member_mask = (db.labels_ == k)
xy = X[class_member_mask & core_samples_mask]
plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=col,
markeredgecolor='k',
markersize=6)
class_member_mask = (db.labels_ == -1)
xy = X[class_member_mask & ~core_samples_mask]
plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor='k',
markeredgecolor='k',
markersize=6)
plt.title('number of clusters: %d' % n_clusters_)
plt.show()
展示结果如下:
显然,这个聚类效果的好坏是根据你指定的参数来决定的,上图中黑色点是被判定为不属于任何类簇的噪声(异常点),大家可以发现这些点基本上是在类簇的外围离聚类中心较远的边缘地带,所以我们在聚类的过程中,合理的选择参数(邻域大小 ϵ \epsilon ϵ 和 组成类簇的最少样本点 M i n P t s MinPts MinPts)十分重要。
最后,使用评估标准看一下当前聚类的效果:
#evaluation metrics without noise
sc = metrics.silhouette_score(X[core_samples_mask], db.labels_[core_samples_mask])
print("Silhouette Coefficient:%0.2f"%sc)
ari = metrics.adjusted_rand_score(y_true[core_samples_mask], db.labels_[core_samples_mask])
print("Adjusted Rand Index: %0.2f"%ari)
结果为:
Silhouette Coefficient:0.83
Adjusted Rand Index: 1.00
五、总结
和传统的 K-Means
算法相比,DBSCAN
最大的不同就是不需要输入类别数k
,它是根据一定范围内的近邻点去进行判定(distance between nearest points),符合条件的则形成簇,不符合条件的则归为其它簇或者噪声(异常点),当然它最大的优势是可以发现任意形状的聚类簇,而不是像K-Means
,一般仅仅使用于凸的样本集聚类。同时它在聚类的同时还可以找出异常点,这点和BIRCH
算法类似,如下图:
那么我们什么时候需要用DBSCAN
来聚类呢?一般来说,如果数据集是稠密的,并且数据集不是凸的,那么用DBSCAN
会比K-Means
聚类效果好很多。如果数据集不是稠密的,则不推荐用DBSCAN
来聚类。
DBSCAN
的主要优点有:
-
① 可以对任意形状的稠密数据集进行聚类,相对的,
K-Means
之类的聚类算法一般只适用于凸数据集。 -
② 可以在聚类的同时发现异常点,对数据集中的异常点不敏感。
-
③ 聚类结果没有偏倚,相对的,
K-Means
之类的聚类算法初始值对聚类结果有很大影响。
DBSCAN
的主要缺点有:
-
① 如果样本集的密度不均匀、聚类间距相差很大时,聚类质量较差,这时用
DBSCAN
聚类一般不适合。 -
② 如果样本集较大时,聚类收敛时间较长,此时可以通过在搜索最近邻时建立的
KD-tree
或者ball-tree
进行规模限制来改进。 -
③ 调参相对于传统的
K-Means
之类的聚类算法稍复杂,主要需要对距离阈值 ϵ \epsilon ϵ ,邻域样本数阈值 M i n P t s MinPts MinPts 联合调参,不同的参数组合对最后的聚类效果有较大影响。
六、参考文献
- https://en.wikipedia.org/wiki/DBSCAN
- https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html#sklearn.cluster.DBSCAN
- DBSCAN密度聚类算法 - 刘建平
- https://www.geeksforgeeks.org/dbscan-clustering-in-ml-density-based-clustering/
- https://towardsdatascience.com/dbscan-clustering-explained-97556a2ad556