DBSCAN 密度聚类:发现任意形状的簇

FreeGuideOnline 最新 2026-06-16

DBSCAN 密度聚类:以数据密度重塑簇的边界

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,与K-Means等基于距离的方法不同,它通过样本分布的紧密程度来划分簇,能够发现任意形状的簇并自动识别噪声点。作为初学者,理解DBSCAN的核心是抓住“密度相连”这一思想,而不需要预先指定簇的数量。

核心概念:密度的语言

DBSCAN 用两个关键参数定义密度的含义:eps(邻域半径)和 min_samples(密度阈值)。基于这两个参数,数据点被分为三类。

  • ε-邻域:对于给定点p,以p为圆心、eps为半径的圆形区域称为p的ε-邻域。该区域内的样本数称为p的密度。
  • 核心点(Core Point):若点p的ε-邻域内至少包含 min_samples 个样本(含自身),则p是核心点。这些区域是簇的骨架。
  • 边界点(Border Point):点q的密度不足 min_samples,但落在某个核心点的ε-邻域内。边界点被归入该核心点所在的簇,但无法通过自身进一步扩展簇。
  • 噪声点(Noise Point):既非核心点也非边界点的样本,视为游离于任何簇之外的噪声。
图示理解:
核心点像城市中心,人口密集(邻居≥min_samples);
边界点像城郊,自身邻居少但紧挨市中心;
噪声点则是荒野孤屋,远离任何人口密集区。

簇的形成:密度可达与密度相连

DBSCAN 通过“密度可达”和“密度相连”的关系将核心点连结起来,最终形成簇。

  • 直接密度可达:若点q在核心点p的ε-邻域内,则称q从p出发是直接密度可达的。注意对称性不成立,除非q也是核心点。
  • 密度可达:存在一串核心点序列 p₁, p₂, …, pₙ,使得 pᵢ₊₁ 从 pᵢ 直接密度可达,则 pₙ 从 p₁ 密度可达。这种传递关系是非对称的(仅由核心点向前传递)。
  • 密度相连:若存在点o使点p和点q均从o密度可达,则p与q密度相连。密度相连是对称关系,它是定义簇的依据。

一个簇就是由密度相连性导出的最大密度相连样本集合。噪声点不与任何簇密度相连。

算法如何工作:逐步流程

  1. 参数设定:给定 epsmin_samples
  2. 标记所有点为“未访问”
  3. 随机选择未访问点p,标记为“已访问”。检查p的ε-邻域样本数。
  4. 若p是核心点:创建新簇C,将p所有密度可达点加入C(通过持续搜索邻域内的核心点来扩展簇)。
  5. 若p是边界点:暂时将其标记为噪声,后续可能被某个核心点划入簇中。
  6. 重复,直到所有点都被访问。
  7. 最终未被归入任何簇的点标记为噪声

该过程无需迭代更新质心,直接一次扫描完成,效率上受益于空间索引(如KD-Tree)的邻域查询优化。

参数选择:eps 与 min_samples 的实战智慧

参数的选择直接决定聚类质量。没有绝对通用的数值,但有一套可实践的方法。

  • min_samples:经验法则通常设为数据维度的2倍或更大,但至少 ≥ 3。较大的值会要求更高的密度,导致更少的簇和更多噪声;较小的值则相反。对于有噪声的数据,可以适当增大 min_samples
  • eps:最需要调优的参数。k-距离图是经典方法:对每个点计算其到第k个最近邻的距离(k = min_samples - 1),将所有点的该距离升序排列并绘图,寻找曲线“肘部”位置。肘部对应的距离值常作为 eps 的良好候选。太小导致大量离散噪声,太大则合并不同簇。
  • 迭代调整:固定 min_samples,尝试不同的 eps,观察聚类数目与噪声率的变化,选择业务上解释性最佳的参数组合。

优势与局限:何时选择DBSCAN

优势

  • 能发现任意形状的簇,不受球形假设限制。
  • 天然抗噪声,直接将异常点标记为噪声。
  • 无需预先指定簇数量。
  • 对数据输入顺序不敏感。

局限

  • 对参数 eps 高度敏感,难调优。
  • 无法良好处理密度差异悬殊的数据集:全局单一密度阈值会丢失稀疏簇或合并稠密簇。
  • 高维数据上距离度量失效(维度灾难),密度变得均匀,算法性能下降。此时需先降维。
  • 计算邻域时需要高效索引,最坏情况下复杂度 O(n²)。

Python 实战:用 sklearn 实现并可视化

以下示例生成包含噪声的月牙形数据集,展示DBSCAN识别任意形状簇的能力。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons
from sklearn.preprocessing import StandardScaler

# 1. 生成双月牙数据,添加噪声
X, y_true = make_moons(n_samples=300, noise=0.06, random_state=42)
X = StandardScaler().fit_transform(X)  # 标准化,使距离更有意义

# 2. 构建DBSCAN模型
db = DBSCAN(eps=0.25, min_samples=5).fit(X)
labels = db.labels_

# 3. 识别核心样本与噪声
core_samples_mask = np.zeros_like(labels, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
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}')

# 4. 可视化
unique_labels = set(labels)
colors = [plt.cm.Spectral(each) for each in np.linspace(0, 1, len(unique_labels))]
for k, col in zip(unique_labels, colors):
    if k == -1:
        col = [0, 0, 0, 1]  # 黑色表示噪声

    class_member_mask = (labels == k)
    xy = X[class_member_mask & core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col), markeredgecolor='k', markersize=14)

    xy = X[class_member_mask & ~core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col), markeredgecolor='k', markersize=6)

plt.title(f'DBSCAN: 发现{n_clusters}个簇, {n_noise}个噪声点')
plt.show()

修改 epsmin_samples 会显著改变结果。将 eps=0.25 改为 0.15 会拆出更多小簇和噪声;改为 0.35 可能将两个月牙连成一体。这正是参数敏感性的直观体现。

进阶主题与衍生算法

  • OPTICS:为应对密度差异和 eps 难选的问题,OPTICS通过可达距离生成有序列表,从中提取不同密度层次的聚类,无需指定单一 eps
  • HDBSCAN:基于层次密度聚类,自适应地选择簇,只需 min_cluster_size 一个主要参数,鲁棒性更强,已成为现代密度聚类的首选之一。

最佳实践总结

  1. 对数据进行标准化或归一化,消除量纲影响。
  2. 绘制k-距离图辅助选择 epsmin_samples 参考维度。
  3. 结合领域知识验证簇的合理性,必要时尝试OPTICS/HDBSCAN。
  4. 高维数据先使用PCA、t-SNE等降维。
  5. 评估时使用轮廓系数等指标,但注意轮廓系数对密度聚类可能产生误导,可改用密度聚类评估指数(DBCV)

通过理解密度相连的逻辑,并亲手调参与可视化,你将能灵活运用DBSCAN解决现实世界中形状复杂的聚类任务。