大数跨境

导师急了:“K-Means都没搞明白,就敢直接学DBSCAN?”我:“Codex能跑!”导师:“密度聚类、核心点、噪声点,先把这些基础搞懂再说!”

导师急了:“K-Means都没搞明白,就敢直接学DBSCAN?”我:“Codex能跑!”导师:“密度聚类、核心点、噪声点,先把这些基础搞懂再说!” 机器学习和人工智能AI
2026-09-17
2

哈喽,大家好~

最近有一个问题,聚类是不是一定要先告诉模型分几类?

还真不是,咱们今儿来聊聊DBSCAN。

DBSCAN最大的特点是:不需要提前指定聚类数量,还能把离群点单独识别出来

理解 DBSCAN

假设我们把地图上的商场、居民区和工业区看成一堆散落的坐标点。

有些区域点特别密集,说明这里可能是一个真实的区域;有些点零零散散,可能只是异常位置。DBSCAN做的事情,就是:

从密集区域出发,把相互靠近的点连接起来,形成一个簇;太孤立的点就标记为噪声。

它不关心簇是不是圆形,所以对于月牙形、环形、弯曲形数据,DBSCAN通常比K-Means更自然。

DBSCAN中有三个重要概念:

  • 核心点:在半径   内,至少有 min_samples 个点。
  • 边界点:自己附近不够密集,但落在某个核心点附近。
  • 噪声点:既不是核心点,也不属于任何核心点的邻域。

一句话概括:核心点负责“生长”簇,边界点负责补充簇,噪声点被单独留下。

DBSCAN核心原理

对于样本点 ,它的 邻域可以写成:

这里的 表示距离 不超过 的所有点。

如果邻域内点的数量满足:

那么 就是核心点。

DBSCAN会从一个核心点开始,把它邻域里的点加入当前簇。如果邻域里的点也是核心点,就继续向外扩展,最终把一整片密集区域连接起来。

参数主要有两个:

  • eps:邻域半径,越大越容易把不同簇连接起来。
  • min_samples:成为核心点所需的最少邻居数量,越大,模型越严格。

所以参数不能乱设。eps太小,会产生大量噪声;eps太大,不同簇可能被粘在一起。

完整案例

这次我们构造三类数据:

  1. 两个月牙形数据;
  2. 一个环形数据;
  3. 一些随机噪声点。

这种数据并不是规则圆团,非常适合观察DBSCAN的优势。代码如下:

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

np.random.seed(42)

# 月牙数据
X1, _ = make_moons(n_samples=4500, noise=0.08, random_state=1)
X1 = X1 * [2.21.5] + [-2.00.2]

# 环形数据
X2, _ = make_circles(
    n_samples=5500, factor=0.45,
    noise=0.06, random_state=2
)
X2 = X2 * 2.3 + [2.20.0]

# 随机噪声
noise = np.random.uniform([-4.8-3.0], [4.83.0], size=(802))

X = np.vstack([X1, X2, noise])
X = StandardScaler().fit_transform(X)

# DBSCAN聚类
model = DBSCAN(eps=0.16, min_samples=8)
labels = model.fit_predict(X)

# 计算每个点到第k近邻的距离,用于辅助选择eps
k = 8
neighbors = NearestNeighbors(n_neighbors=k).fit(X)
distances, _ = neighbors.kneighbors(X)
k_dist = np.sort(distances[:, -1])

# 绘图
fig, axes = plt.subplots(13, figsize=(185))

# 图1:原始数据
axes[0].scatter(
    X[:, 0], X[:, 1],
    c="#00BFFF", s=28, alpha=0.8,
    edgecolors="white", linewidths=0.4
)
axes[0].set_title("Original Virtual Data")
axes[0].set_xlabel("Feature 1")
axes[0].set_ylabel("Feature 2")

# 图2:DBSCAN聚类结果
unique_labels = sorted(set(labels))
colors = plt.cm.turbo(np.linspace(01, len(unique_labels)))

for color, label in zip(colors, unique_labels):
    mask = labels == label

    if label == -1:
        color = "#111111"
        name = "Noise"
        size = 42
        marker = "x"
    else:
        name = f"Cluster {label}"
        size = 30
        marker = "o"

    axes[1].scatter(
        X[mask, 0], X[mask, 1],
        c=[color], s=size, marker=marker,
        alpha=0.85, label=name
    )

axes[1].set_title("DBSCAN Clustering Result")
axes[1].set_xlabel("Feature 1")
axes[1].set_ylabel("Feature 2")
axes[1].legend()

# 图3:k距离曲线
axes[2].plot(k_dist, color="#FF1493", linewidth=2.5)
axes[2].axhline(
    y=0.16, color="#00AA55",
    linestyle="--", label="eps = 0.16"
)
axes[2].set_title("k-distance Curve")
axes[2].set_xlabel("Sorted Samples")
axes[2].set_ylabel("Distance to 8th Neighbor")
axes[2].legend()

plt.tight_layout()
plt.show()

print("聚类数量:", len(set(labels)) - (1 if -1 in labels else 0))
print("噪声点数量:", np.sum(labels == -1))

第一张图是原始数据。可以看到,样本包含弯曲的月牙、环状结构以及散落在外围的噪声点。它们并不是几个标准的圆形簇。

第二张图是DBSCAN结果。不同鲜艳颜色代表不同簇,黑色叉号代表噪声点。你会发现,DBSCAN可以沿着月牙和环形结构进行聚类,而不是简单地用一个圆形中心去切分。

第三张图是k距离曲线。我们把每个点到第8近邻的距离排序,曲线突然变陡的位置,通常可以作为eps的参考。曲线平缓区域对应密集样本,后面快速上升的部分往往对应边界点和噪声点。

这里的eps=0.16不是固定答案,而是结合数据尺度和曲线形状调出来的。我们先做了标准化,所以这个值是在标准化后的空间中使用的。

DBSCAN的优点和注意事项

DBSCAN的优点很明显:

  • 不需要提前指定聚类数量;
  • 可以识别任意形状的簇;
  • 能够自动发现噪声点;
  • 对异常检测、地理坐标聚类比较实用。

但它也有局限。如果不同簇的密度差异特别大,同一个eps很难同时适应所有区域。另外,数据维度很高时,距离的区分度会下降,DBSCAN的效果也可能变差。

实际使用时,可以这样做:

  1. 先对特征进行标准化;
  2. 用k距离曲线辅助选择eps
  3. 尝试几个不同的min_samples
  4. 观察噪声比例和聚类结果是否符合业务直觉。

总结

DBSCAN可以理解成一种“基于密度连接”的聚类算法:密集点形成簇,稀疏点变成噪声。它不需要提前指定类别数量,尤其适合月牙、环形等复杂形状的数据。

大家可以继续尝试:把这份代码中的eps改成0.080.30,观察噪声点和簇数量如何变化;也可以加入真实的经纬度数据,体验DBSCAN在地理位置聚类中的效果~

【声明】内容源于网络
0
0
机器学习和人工智能AI
让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
内容 392
粉丝 0
机器学习和人工智能AI 让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
总阅读6.0k
粉丝0
内容392