美文网首页
DBSCAN 聚类

DBSCAN 聚类

作者: 魏允臣 | 来源:发表于2019-01-26 18:47 被阅读0次
以下使用Out[数字]:的方式表示代码输出结果
Out[数字]:
代码输出

DBSCAN 密度聚类算法

密度聚类是一类常用并且有效的聚类方法,通过评估样本的紧密程度来划分对应的类别,理论上可以找出任何形状的聚类,包括非凸(non-convex)数据。除此之外,密度聚类不需要事先知道要形成的簇类的数量,还能有效识别噪声点。
密度聚类算法中最为常用的就属 DBSCAN 算法了,DBSCAN 是英文 Density-based spatial clustering of applications with noise 的缩写,直译过来就是「具有噪声的基于密度的聚类方法」。这种方法是由 Martin Ester 等在 1996 年提出。
相比其他的聚类方法,DBSCAN 的显著优点是可以在有噪音的数据中发现形状与大小各异的类别。另外,相比于划分聚类方法,DBSCAN 不需要预先声明聚类数量,这一点和层次聚类非常相似。

DBSCAN 聚类原理

DBSCAN 作为一种典型的密度聚类方法,它的聚类原理当然是和密度有关。简单来讲,DBSCAN 一般假定样本的类别可以通过样本分布的紧密程度决定,于是先发现密度较高的点,然后把相近的高密度点逐步连成一片,进而找到不同的类别(簇)。

DBSCAN 具体的运作过程

  1. 首先,DBSCAN 会以各数据点为圆心,以 eps\varepsilon-邻域) 为半径画圆。
  2. 然后,DBSCAN 会计算相应圆中有多少个其他点,并以该数目作为圆心数据点的密度值。
  3. 接下来,我们需要确定密度阈值 MinPts,并分别将小于或大于该密度阈值的数据点(包含自己)称作低密度或高密度点(核心点)。
  4. 如果,此时有一个高密度的点在另一个高密度的点的圆圈范围内,我们就把这两个点串联起来。
  5. 之后,如果有低密度的点也在高密度的点的圆圈范围内,也将其连接到最近的高密度点上,并称之为边界点。
    下面根据一个图示来详细阐述该过程

    如上图所示,假设我们指定密度阈值 MinPts = 4,那么图中的红色点就是核心点,因为它们的 \varepsilon-邻域里包含最少 4 个点(包括自己),由于它们之间相互可达,它们形成了一个聚类。图中蓝色的点不是核心点,但它们在核心点的圆圈内,所以也属于同一个聚类,也就是边界点。而紫色点既不是核心点,又不是边界点,即被称之为异常点。

DBSCAN 相关定义

首先,我们假设样本集是 D=(x_1,x_2,\cdots,x_m),则 DBSCAN 中相关的定义描述如下:

\varepsilon-邻域:

对于 x_j \in D,其\varepsilon-邻域包含样本集 D 中与 x_j 的距离不大于 \varepsilon 的子样本集,即
N_{\epsilon}(x_j) = \{x_i \in D| distance(x_i, x_j) \leq \epsilon\},
这个子样本集的个数记为 |N_{\epsilon}(x_j)|

核心对象(核心点):

对于任一样本 x_j \in D,如果其 \varepsilon-邻域对应的 N_{\epsilon}(x_j) 至少包含 MinPts 个样本,即如果
|N_{\epsilon}(x_j)| \geq MinPts,
x_j 是核心对象。

密度直达:

如果 x_i 位于 x_j\varepsilon-邻域中,且 x_j 是核心对象,则称 x_ix_j 密度直达。注意反之不一定成立,即此时不能说 x_jx_i 密度直达, 除非 x_i 也是核心对象。

密度可达:

对于 x_ix_j,如果存在样本序列 p_1, p_2,\cdots,p_t,满足 p_1=x_i,p_t=x_j, 且p_{k+1}p_k(其中 k = 1, 2, \cdots, t-1)密度直达,则称x_jx_i 密度可达。也就是说,密度可达满足传递性。此时序列中的传递样本 p_1, p_2,\cdots,p_{t-1} 均为核心对象,因为只有核心对象才能使其他样本密度直达。

密度相连:

对于 x_ix_j,如果存在核心对象样本 x_k,使 x_ix_j 均由 x_k 密度可达,则称 x_ix_j 密度相连。注意密度相连关系是满足对称性的。

另外,关于确定某样本是否在核心样本的 \varepsilon-邻域内,其实也就是二者之间距离度量的问题,我们一般可以使用欧式距离计算。其实你会发现,这里\varepsilon-邻域确定了一个圆,而这就类似于 K-近邻算法中找最近邻的 K 个点的情形。

DBSCAN 聚类算法 Python 实现

实现 DBSCAN 聚类过程前,我们同样需要先生成一组示例数据。这里用到了 make_moons 方法。

import numpy as np
from sklearn import datasets
from matplotlib import pyplot as plt
%matplotlib inline
# 生成 100 个样本并添加噪声
noisy_moons, _ = datasets.make_moons(n_samples=100, noise=.05, random_state=10) 
noisy_moons[:5]
Out[2]:
array([[ 0.2554364 ,  0.90420806],
       [ 0.55299636,  0.84445141],
       [-0.90343862,  0.39161309],
       [-0.62792219,  0.62502915],
       [ 0.60777269, -0.33777687]])
plt.scatter(noisy_moons[:,0], noisy_moons[:,1])

从上图可以看出,虽然数据点的分布比较奇怪,但肉眼观察明显呈现出 2 类


image

此时,我们尝试使用划分聚类中的 K-Means 和层次聚类中的 BIRCH 方法来尝试对上面的数据集进行聚类,这两种方法是划分和层次聚类法中的代表,这里为了方便直接使用 scikit-learn 实现。

from sklearn.cluster import KMeans, Birch

kmeans_c = KMeans(n_clusters=2).fit_predict(noisy_moons)
birch_c = Birch(n_clusters=2).fit_predict(noisy_moons)

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(15, 5))
axes[0].scatter(noisy_moons[:,0], noisy_moons[:,1], c=kmeans_c, cmap='bwr')
axes[1].scatter(noisy_moons[:,0], noisy_moons[:,1], c=birch_c, cmap='bwr')
axes[0].set_xlabel('K-Means')
axes[1].set_xlabel('BIRCH')

K-Means 和 BIRCH 方法无法对上面的示例数据正确聚类。面对这种凹状数据集,划分和层次聚类法往往都无法正确聚类,此时需要用到密度聚类方法。
下面,我们使用 Python 实现 DBSCAN 密度聚类算法
首先,我们实现一个 search_neighbors() 函数,这个函数的目的是找出核心点周围可到达的数据点。
其中,计算距离的方式使用欧式距离。
"""欧式距离
"""
def euclidean_distance(a, b):
    """
    参数:
    a -- 数组 a
    b -- 数组 b
    
    返回:
    dist -- a, b 间欧式距离
    """
    x = float(a[0]) - float(b[0])
    x = x * x
    y = float(a[1]) - float(b[1])
    y = y * y
    dist = round(np.sqrt(x + y), 2)
    return dist
"""找出数据集中距核心点 P 在 eps 范围内的邻近点
"""
def search_neighbors(D, P, eps):
    """
    参数:
    D -- 数据集(二维数组)
    P -- 核心点
    eps -- eps 邻域
    
    返回:
    neighbors -- 核心点在 eps 范围内的邻居
    """
    neighbors = []
    for Pn in range(0, len(D)):
        # 距离判断是否在 eps 范围内
        if euclidean_distance(D[P], D[Pn]) < eps:
            neighbors.append(Pn)
            
    return neighbors

接下来实现 DBSCAN 的主体代码:

"""DBSCAN 密度聚类算法
"""
def dbscan_cluster(D, eps, MinPts):
    """
    参数:
    D -- 数据集(二维数组)
    eps -- eps 邻域
    MinPts -- 密度阀值

    返回:
    labels -- 聚类标签
    """
    labels = [0]*len(D)  # 初始化数据集中的数据类别全部为 0
    C = 0
    # 选择 P 作为核心点
    for P in range(0, len(D)):

        # 选择类别为 0 的点作为中心
        if not (labels[P] == 0):
            continue

        # 搜寻该数据点在 eps 圆中的邻居
        Neighbors = search_neighbors(D, P, eps)

        # 标记噪声点为 -1
        if len(Neighbors) < MinPts:
            labels[P] = -1

        # 非噪声点作为新类别中心
        else:
            C += 1  # 原类别 +1 作为新类别的标签
            labels[P] = C  # 将非噪声点设定为新类别

            # 开始检查 P 在 eps 圆中邻居的可达性
            for i, n in enumerate(Neighbors):
                Pn = Neighbors[i]  # 从 P 的邻居中取 1 个点

                # P 的邻居,设定为一样的类别
                if labels[Pn] == 0:
                    labels[Pn] = C

                    # 进一步搜索 P 的邻居的邻居
                    PnNeighbors = search_neighbors(D, Pn, eps)
                    if len(PnNeighbors) >= MinPts:  # 如果满足密度阈值要求则连通
                        Neighbors += PnNeighbors

                # 如果该点曾被标记为噪声点, 则重新连接到类别中
                elif labels[Pn] == -1:
                    labels[Pn] = C

    return labels

然后使用 DBSCAN 算法对上面的月牙形示例数据进行聚类,得到聚类标签。这里设定 εε -邻域的值为 0.5,而密度阈值 MinPts=5,即半径为 0.5 的圆内至少有 5 个其他点才能被看作是核心点。

dbscan_c = dbscan_cluster(noisy_moons, eps=0.5, MinPts=5)
np.array(dbscan_c) # 显示聚类标签
Out[8]:
array([1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 2,
       2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
       1, 2, 1, 2, 2, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1,
       2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 1, 1,
       1, 1, 1, 2, 1, 1, 2, 2, 1, 2, 1, 1])

依照 DBSCAN 算法得到的聚类标签,重新对散点图进行着色,查看聚类效果。

plt.scatter(noisy_moons[:,0], noisy_moons[:,1], c=dbscan_c, cmap='bwr')

如上图所示,DBSCAN 聚类结果符合我们的心理预期,同时弥补了划分和层次聚类的不足。

DBSCAN 聚类算法 scikit-learn 实现

sklearn.cluster.DBSCAN(eps=0.5, min_samples=5, metric='euclidean', metric_params=None, algorithm='auto', leaf_size=30, p=None, n_jobs=1)

其中,主要参数有:

  • eps: 即 \varepsilon-邻域的值。
  • min_samples: 即密度阈值 MinPts 的值。
  • metric: 距离度量方式,默认为欧式距离。
  • algorithm:近邻算法求解方式:auto, ball_tree, kd_tree, brute 可选。

对示例数据完成聚类:

from sklearn.cluster import DBSCAN

dbscan_sk = DBSCAN(eps=0.5, min_samples=5, metric='euclidean')
dbscan_sk_c = dbscan_sk.fit_predict(noisy_moons)
dbscan_sk_c
array([0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
       1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
       0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0,
       1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0,
       0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0])

依照得到的聚类标签,重新对散点图进行着色,查看聚类效果。

plt.scatter(noisy_moons[:,0], noisy_moons[:,1], c=dbscan_sk_c, cmap='bwr')

聚类结果和使用 Python 手动实现的算法计算结果一模一样

相关文章

  • 13 聚类算法 - 谱聚类

    11 聚类算法 - 密度聚类 - DBSCAN、MDCA12 聚类算法 - 代码案例五 - 密度聚类(DBSCAN...

  • 3 聚类 - DBSCAN

    DBSCAN DBSCAN: 具有噪声的基于密度的空间聚类 DBSCAN理解 Epsilon聚点搜索范围,如果范围...

  • 无监督学习 - 聚类 - DBSCAN

    DBSCAN密度聚类DBSCAN算法是一种基于密度的聚类算法: 聚类的时候不需要预先指定簇的个数 最终的簇个数不定...

  • Clustering

    本文结构安排 经典聚类算法:线性聚类 Kmeans 经典聚类算法:非线性聚类 DBSCAN、谱聚类 新兴聚类算法:...

  • CH8 Clustering

    K-means Cluster 4、DBSCAN算法的聚类过程 DBSCAN算法基于一个事实:一个聚类可以由其中的...

  • DBSCAN聚类

  • DBSCAN 聚类

    以下使用Out[数字]:的方式表示代码输出结果 DBSCAN 密度聚类算法 密度聚类是一类常用并且有效的聚类方法,...

  • 聚类算法总结

    1、K 均值聚类 2、凝聚聚类 3、DBSCAN 算法 4、聚类算法的评估

  • 2019-03-17派森学习第119天

    DBSCAN聚类算法 DBSCAN实验网站 半径设置过小: 半径稍微设置大一些,就比较好了: 各种聚类算法准确度对...

  • 机器学习(七) 聚类之DBSCAN

    针对聚类K-means算法中不能对特定形状的样本进行分类,提出了一种新的聚类算法(DBSCAN)。DBSCAN 是...

网友评论

      本文标题:DBSCAN 聚类

      本文链接:https://www.haomeiwen.com/subject/mdiijqtx.html