DBSCAN (Density-Based Spatial Clustering of Applications with Noise)
DBSCAN 会将密集分布的点聚成任意形状的簇,并将稀疏点标记为噪声,且无需预先指定簇的数量。附带代码。
DBSCAN(含噪声的基于密度的空间聚类)是一种无监督学习算法,它将彼此靠近的点聚成簇,并将稀疏区域中的点标记为噪声。Martin Ester、Hans-Peter Kriegel、Jörg Sander 和 Xiaowei Xu 于 1996 年在 KDD 会议上提出了该算法。它无需预先指定簇的数量,可以找到任意形状的簇,例如环形、新月形或蜿蜒的道路;其噪声标签也可作为一种简单的异常检测形式。整个算法由两个参数控制:邻域半径,以及让一个邻域达到密集状态所需的点数。
核心点、边界点与噪声点#
DBSCAN 会查看每个点的邻域,即距离 eps 以内的点集,并统计其中的点数:
- 核心点:邻域内至少有
min_samples个点;在 scikit-learn 的实现中,计数包含该点自身。核心点构成簇的密集内部区域。 - 边界点:邻居太少,不足以成为核心点,但位于某个核心点的
eps距离范围内。边界点构成簇的边缘。 - 噪声点:既不是核心点也不是边界点,因此不属于任何簇,标签为 -1。
任意一个尚未访问的核心点都可以作为起点来扩展一个簇。DBSCAN 会添加其邻域中的所有点,然后从每个新加入的核心点重复这一过程,直到没有更多可达点为止,因此一连串相互重叠的密集邻域无论弯曲成什么形状,都会形成一个簇。边界点会加入簇,但不会扩展簇。给定输入顺序时,结果是确定的;但如果某个边界点同时位于两个簇的可达范围内,它会加入最先到达它的簇,因此打乱数据可能会使边界点在簇之间移动。
选择 eps 和 min_samples#
半径 eps 最为重要,并且取决于数据的尺度,因此通常要先对特征进行标准化。半径太小会使大多数点成为噪声,半径太大则会把相邻簇合并为一个簇。常用的启发式方法是对每个点到其第 k 近邻的距离进行排序,其中 k 等于 min_samples,并在曲线开始陡升的拐点处选择 eps。min_samples 值越大,聚类对噪声的容忍度越高,因此常用于大型或噪声较多的数据集。在图像嵌入等高维空间中,点与点之间的距离会变得相近,因此实践中通常会先应用降维,例如主成分分析 (PCA),再运行 DBSCAN。
DBSCAN 与 K-Means 和 HDBSCAN#
K-means 聚类需要预先指定簇的数量,会将每个点分配给最近的质心,因此更适合紧凑、圆形的簇,并会把离群点归入某个簇。DBSCAN 根据密度发现簇的数量,并将离群点排除在外。它的弱点是只能使用一个全局 eps:当簇的密度差异很大时,没有一个半径能同时将稀疏簇与噪声分开,并将密集簇彼此分开。HDBSCAN 通过实质上运行所有 eps 值下的 DBSCAN 并保留最稳定的簇来解决这个问题;这两种算法以及相关的 OPTICS 都包含在 scikit-learn 聚类模块中。
此示例对两个月牙形交错簇进行聚类,并加入 20 个随机离群点;随后使用调整兰德指数对照真实分组为每种结果评分,1.0 表示完全匹配:
import numpy as np
from sklearn.cluster import DBSCAN, KMeans
from sklearn.datasets import make_moons
from sklearn.metrics import adjusted_rand_score
# Two interleaving crescents plus 20 scattered outliers (true label -1)
X, y = make_moons(n_samples=300, noise=0.05, random_state=0)
rng = np.random.default_rng(0)
X = np.vstack([X, rng.uniform(-1.5, 2.5, size=(20, 2))])
y = np.concatenate([y, np.full(20, -1)])
# DBSCAN infers clusters from density; k-means is told there are 2
db_labels = DBSCAN(eps=0.2, min_samples=5).fit_predict(X)
km_labels = KMeans(n_clusters=2, random_state=0).fit_predict(X)
print(f"DBSCAN: {db_labels.max() + 1} clusters, {(db_labels == -1).sum()} noise points")
print(f"ARI: DBSCAN {adjusted_rand_score(y, db_labels):.2f}, k-means {adjusted_rand_score(y, km_labels):.2f}")脚本输出 DBSCAN 的 2 个簇和 15 个噪声点,以及 DBSCAN 的 0.97 和 k-means 的 0.20 两个调整兰德指数。DBSCAN 能沿着两个月牙形簇进行聚类,并排除落在簇外的离群点;而 k-means 即使获得了正确的簇数量,仍会用一条直线切分这两个月牙形簇。
实际应用#
- LiDAR 感知:自动驾驶车辆和机器人系统会从 LiDAR 扫描中去除地面点,然后将剩余的 3D 点聚成独立障碍物。由于物体数量未知,密度聚类是常见的选择。
- 检测后处理:YOLO26检测结果的框中心点可通过
result.boxes.xywh获取,并可使用 DBSCAN 聚类,以找出人群中的人群组或生产线上的重复缺陷位置;孤立的检测结果则会作为噪声被排除。 - 欺诈与传感器监控:标记为 -1 的交易或读数远离所有正常行为的密集群组,因此会成为待审核对象。
- 地理空间分析:配送和网约车公司会对 GPS 点进行聚类,以找出常见停靠点和热门上车地点。Scikit-learn 的 DBSCAN 接受
metric="haversine"作为以弧度表示的经纬度,在这种情况下,eps是单位球面上的角度,因此 100 米的半径对应eps = 0.1 / 6371,计算时使用地球平均半径 6,371 km。










