欢迎光临
我们一直在努力

机器学习————DBSCAN 聚类

        DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是基于密度的聚类算法,核心优势是能发现任意形状的聚类、自动识别离群点,无需提前指定聚类数量,这是 K-Means 等基于距离的聚类算法做不到的。

一、核心概念

        DBSCAN 用 “密度” 定义聚类:聚类是空间中密度相连的点的集合,噪声是密度过低的孤立点。

1. 两个超参数

DBSCAN 仅需两个人工设置的超参数,是参数极少的聚类算法:

  • ε(Epsilon,邻域半径):以某个点为中心,半径为 ε 的圆形区域,称为该点的ε- 邻域(二维)和ε- 球(高维)。
  • MinPts(最小点数):一个点的 ε- 邻域内至少包含的点数,其中包括自身,是判断点类型的核心阈值。

2. 三种点的类型

DBSCAN 将数据集中的点分为 3 类,点的类型决定了聚类的生成逻辑:

  • 核心点(Core Point):若点p的 ε- 邻域内的点数 ,则p为核心点。核心点是聚类的 “种子”,是密度聚类的核心支撑。
  • 边界点(Border Point):若点q的 ε- 邻域内的点数,但q落在某个核心点的 ε- 邻域内,则q为边界点。边界点是聚类的 “边缘”,依附于核心点存在。
  • 离群点(噪声点,Noise Point):既不是核心点、也不是边界点的点,即:ε- 邻域内点数 ,且不落在任何核心点的 ε- 邻域内。噪声点是算法自动识别的无效点。
  • 3. 两个密度关系

    点与点之间的密度关系,是连接成聚类的 “纽带”,决定了哪些点属于同一个聚类:

  • 直接密度可达:若点q在核心点p的 ε- 邻域内,则称q从p直接密度可达,仅核心点能触发该关系。
  • 密度相连:若存在点o,使得点p和点q都从o密度可达,则称p和q密度相连。
  • 4. 聚类的定义

    DBSCAN 中,一个聚类是满足以下两个条件的最大点集:

  • 密度相连性:聚类内任意两点都密度相连;
  • 最大性:聚类外的点与聚类内的点都不密度相连。
  •         总结:聚类是由核心点及其密度可达的所有点组成的集合,边界点依附核心点存在,噪声点无任何核心点支撑。

    二、数学公式

            设数据集为D=\\left \\{ p_{1} ,p_{2} ,...,p_{n} \\right \\},其中p_{i}为 d 维空间中的数据点,n为样本数;

            设距离函数为dist\\left ( p,q \\right )(常用欧氏距离,DBSCAN 可兼容任意距离函数,如曼哈顿、余弦距离);

            超参数:邻域半径ε>0,最小点数MinPts≥2,可以记作一般 MinPts≥维度 + 1,如二维数据 MinPts≥3)。

    1. 点p的 ε- 邻域

    N_{\\varepsilon }\\left ( p \\right )=\\left \\{ q\\epsilon D | dist\\left ( p,q \\right )\\leq \\varepsilon \\right \\}

    解释:N_{\\varepsilon }\\left ( p \\right )表示数据集中所有与p的距离≤ε 的点的集合,集合的元素个数记为∣Nε​(p)∣。

    2. 核心点

    p 是核心点  ⟺  \\left | N_{\\varepsilon } \\left ( p \\right )\\right |\\geq MinPts

    解释:ε- 邻域内的点数以及自身点≥MinPts,p即为核心点。

    3. 直接密度可达

    q 从 p 直接密度可达⟺p 是核心点 且 q∈Nε​(p);

    解释:仅核心点能让其他点直接密度可达,边界点(噪声点)无法触发该关系。

    4. 密度可达

            若存在点序列p_{0}= p,p_{1},p_{2},p_{3},...,p_{m}=q,使得p_{i+1}p_{i}直接密度可达(i=0,1,…,m−1),则称q从p密度可达。

    记为p⟶ε,MinPts​q

    解释:直接密度可达的传递闭包,核心点 A 的 ε- 邻域内的核心点 B,其 ε- 邻域内的点 C,从 A 密度可达。

    5. 密度相连

            若存在点o∈D,使得p从o密度可达且q从o密度可达,则称p和q密度相连。

            解释:密度相连的点共享同一个 “核心点祖先”,属于同一个聚类。

    6. 噪声点

            p 是噪声点⟺∄核心点 o∈D, 使得 p∈Nε​(o)。

            解释:不存在任何核心点,使得p落在其 ε- 邻域内,p即为噪声点。

    三、数学计算

    模块一:导入必须库

    import numpy as np # 数值计算,处理数组(核心)
    import matplotlib.pyplot as plt # 数据可视化–二维聚类展示

    模块二:DBSCAN 类的初始化方法

  • 超参数仅eps和min_pts,是 DBSCAN 的核心;
  • labels_遵循 sklearn 接口规范,方便用户习惯;
  • cluster_id用于标记不同聚类,每生成一个新聚类,ID 自增 1。
  • def __init__(self, eps, min_pts):
    self.eps = eps # 保存邻域半径ε
    self.min_pts = min_pts # 保存最小点数MinPts
    self.labels_ = None # 存储聚类标签,sklearn风格命名(下划线结尾表示训练后生成)
    self.cluster_id = 0 # 聚类ID计数器,从0开始

    模块三:欧氏距离计算

    def _euclidean_distance(self, p, q):
    return np.sqrt(np.sum((p – q) ** 2))

    模块四:获取 ε- 邻域

    • 核心辅助方法:根据索引找到目标点的所有 ε- 邻域点索引,存储索引比存储点本身更高效,尤其高维数据;
    • 输入X是n×d的 numpy 数组,n 样本数,d 维度,p_idx是目标点的索引;
    • 输出是邻域内所有点的索引列表,列表长度即为∣Nε​(p)∣。

    def _get_epsilon_neighborhood(self, X, p_idx):
    neighbors = [] # 存储邻域内点的索引(而非点本身,节省内存)
    p = X[p_idx] # 取出目标点p
    for q_idx in range(X.shape[0]): # 遍历所有点
    # 计算p和q的距离,≤eps则加入邻域
    if self._euclidean_distance(p, X[q_idx]) <= self.eps:
    neighbors.append(q_idx)
    return neighbors

    模块五:拟合方法

    def fit(self, X):
    n = X.shape[0] # 样本数n
    # 初始化标签数组:全-1(噪声),int类型
    self.labels_ = np.full(shape=n, fill_value=-1, dtype=int)
    # 初始化访问标记数组:全False(未访问),bool类型
    visited = np.full(shape=n, fill_value=False, dtype=bool)

    # 遍历每个点,按顺序处理
    for p_idx in range(n):
    if visited[p_idx]: # 已访问的点直接跳过,避免重复处理
    continue
    visited[p_idx] = True # 标记为已访问
    neighbors = self._get_epsilon_neighborhood(X, p_idx) # 找ε-邻域

    # 情况1:邻域点数<min_pts → 噪声点,标签保持-1
    if len(neighbors) < self.min_pts:
    self.labels_[p_idx] = -1
    # 情况2:邻域点数≥min_pts → 核心点,扩展生成新聚类
    else:
    self._expand_cluster(X, p_idx, neighbors, visited)
    self.cluster_id += 1 # 聚类生成后,ID+1

    模块六:聚类扩展方法

            这是 DBSCAN 的核心实现,负责循环吸收所有密度可达的点,用列表模拟队列实现循环扩展,比递归更稳定,避免栈溢出:

    def _expand_cluster(self, X, p_idx, neighbors, visited):
    # 第一步:将核心点分配到当前聚类(cluster_id)
    self.labels_[p_idx] = self.cluster_id
    # 用列表实现队列,i为队列指针,从0开始遍历
    i = 0
    while i < len(neighbors):
    q_idx = neighbors[i] # 取出队列中的第i个点q
    # 若q未访问,先标记为已访问,并检查是否为核心点
    if not visited[q_idx]:
    visited[q_idx] = True
    q_neighbors = self._get_epsilon_neighborhood(X, q_idx) # 找q的ε-邻域
    # 若q是核心点,将其邻域加入队列,继续扩展(密度可达)
    if len(q_neighbors) >= self.min_pts:
    # 集合去重→列表,避免队列中出现重复索引,节省计算
    neighbors = list(set(neighbors + q_neighbors))
    # 若q未分配聚类(标签-1),分配到当前聚类
    if self.labels_[q_idx] == -1:
    self.labels_[q_idx] = self.cluster_id
    # 指针后移,处理下一个点
    i += 1

    模块七:测试代码

    if __name__ == "__main__":
    from sklearn.datasets import make_moons, make_blobs
    np.random.seed(42) # 固定随机种子,结果可复现
    X1, _ = make_moons(n_samples=200, noise=0.05) # 两个月牙形(非凸)
    X2, _ = make_blobs(n_samples=100, centers=[[1, 1]], cluster_std=0.1) # 高斯凸聚类
    X = np.vstack([X1, X2]) # 合并数据
    noise = np.random.randn(30, 2) * 0.5 + [0, 2] # 30个随机噪声点
    X = np.vstack([X, noise])

    # 初始化DBSCAN,训练
    dbscan = DBSCAN(eps=0.15, min_pts=5)
    dbscan.fit(X)
    labels = dbscan.labels_

    # 可视化
    plt.figure(figsize=(10, 6))
    # 绘制聚类点
    for cluster in set(labels) – {-1}:
    mask = labels == cluster
    plt.scatter(X[mask, 0], X[mask, 1], label=f'Cluster {cluster}', s=50)
    # 绘制噪声点(黑色×)
    plt.scatter(X[labels==-1, 0], X[labels==-1, 1], c='black', marker='x', label='Noise', s=50)
    plt.title('DBSCAN Clustering Result', fontsize=14)
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.legend()
    plt.grid(alpha=0.3)
    plt.show()

    # 输出聚类统计信息
    n_clusters = len(set(labels)) – (1 if -1 in labels else 0)
    n_noise = list(labels).count(-1)
    print(f"检测到的聚类数:{n_clusters}")
    print(f"检测到的噪声点数:{n_noise}")

    运行结果

    检测到的聚类数:36 检测到的噪声点数:32

    四、结语

    • DBSCAN 是基于密度的聚类,用ε- 邻域和MinPts定义密度,将点分为核心点、边界点、噪声点;
    • 聚类由核心点及其所有密度可达的点组成,边界点依附核心点,噪声点无核心点支撑;
    • 核心逻辑是核心点的 ε- 邻域扩展,将所有密度相连的点纳入同一个聚类。

    感谢大家的观看!如果有不足,请大家的批评指正!

    赞(0)
    未经允许不得转载:171主机测评 » 机器学习————DBSCAN 聚类
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址