sklearn 的标准 DBSCAN 怎么跑得这么快?

Posted

技术标签:

【中文标题】sklearn 的标准 DBSCAN 怎么跑得这么快?【英文标题】:How does sklearn's standard DBSCAN run so fast? 【发布时间】:2018-12-14 07:52:43 【问题描述】:

我一直在研究用于集群雷达数据的 DBSCAN 的替代实现(如基于网格的 DBSCAN)。到目前为止,我一直在使用 sklearn 的标准欧几里得 DBSCAN,它可以在不到一秒的时间内运行 26,000 个数据点。但是,当我指定自己的距离度量时,如下所示:

X = np.column_stack((beam, gate, time_index))
num_pts = X.shape[0]
epsilons = np.array([[beam_eps]*num_pts, [gate_eps] * num_pts, [time_eps] * num_pts]).T

metric = lambda x, y, eps: np.sqrt(np.sum((x/eps - y/eps)**2))
def dist_metric(x, y, eps):
    return np.sqrt(np.sum((x - y)**2))

db = DBSCAN(eps=eps, min_samples=minPts, metric=dist_metric, metric_params='eps': epsilons).fit(X)

在相同的数据上运行从 0.36 秒到 92 分钟。

我在该代码 sn-p 中所做的也可以通过预先转换数据并运行标准欧几里得 DBSCAN 来完成,但我正在尝试实现一个相当快的基于网格的 DBSCAN 版本,其中水平 epsilon根据与雷达的距离而有所不同,所以我无法做到这一点。

上述距离度量的部分缓慢是因为我认为除以 epsilon,因为如果我使用只是欧几里德距离的“自定义度量”,它只需要大约一分钟即可运行:

metric = lambda x, y: np.sqrt(np.sum((x - y)**2))

sklearn 的 euclidean DBSCAN 是如何跑得这么快的?我一直在挖掘代码,但到目前为止还没有弄明白。

【问题讨论】:

你确定你的欧几里得距离 lambda 有那么快吗?因为通常情况下,我认为作为 Python 函数或 lambda 的自定义指标与内置指标之间的区别在于它们是直接用 Cython 甚至 C 或 C++ 实现的,请参阅我的帖子 here。 如果你有足够的空间,另一个选项是预先计算矩阵 【参考方案1】:

因为它使用索引。

此外,它避免了缓慢且占用大量内存的 Python 解释器,而是使用本机代码(从 Cython 编译)完成所有工作。在处理 Python 解释器需要装箱的大量原始数据(例如双精度和整数)时,这会产生巨大的差异。

索引对相似性搜索至关重要。他们可以将运行时间从 O(n²) 减少到 O(n log n)。

但是虽然球树索引允许自定义指标,但每次距离计算调用 python 解释器的成本非常高,所以如果你真的想要自定义指标,编辑 cython 源代码并自己编译 sklearn。或者您可以使用 ELKI,因为 Java JVM 可以在必要时将扩展代码编译为本机代码;它不需要回退到像 sklearn 这样的缓慢的解释器回调。

在您的情况下,对数据进行预处理可能会好得多。在集群之前对其进行缩放。

【讨论】:

不是问题的直接答案,但open3d DBSCAN implementation 比 sklearn 快大约 2 倍(在我的 Intel i7 上 10,000 点上为 34ms v 62ms)

以上是关于sklearn 的标准 DBSCAN 怎么跑得这么快?的主要内容,如果未能解决你的问题,请参考以下文章

grep 怎么跑得这么快?

如何在 DBSCAN sklearn 中获取质心?

在 python 中的 sklearn 中绘制 DBSCAN 中的特定点

用sklearn对弧度距离矩阵进行DBSCAN?

如何在sklearn dbscan中使用多个内核?

DBSCAN聚类以及sklearn库代码实现