【问题标题】:scikit-learn DBSCAN memory usagescikit-learn DBSCAN 内存使用情况
【发布时间】:2013-04-29 04:40:51
【问题描述】:

更新:最后,我选择用于对我的大型数据集进行聚类的解决方案是 Anony-Mousse 下面建议的解决方案。也就是说,使用 ELKI 的 DBSCAN 实现我的聚类而不是 scikit-learn 的。它可以从命令行运行,并通过适当的索引,在几个小时内执行此任务。使用 GUI 和小样本数据集来制定您想要使用的选项,然后前往城镇。值得研究。任何人,请继续阅读我最初的问题的描述和一些有趣的讨论。

我有一个包含约 250 万个样本的数据集,每个样本包含 35 个我正在尝试聚类的特征(浮点值)。我一直在尝试使用 scikit-learn 的 DBSCAN 实现来做到这一点,使用曼哈顿距离度量和从数据中抽取的一些小随机样本估计的 epsilon 值。到目前为止,一切都很好。 (这里是sn-p,供参考)

db = DBSCAN(eps=40, min_samples=10, metric='cityblock').fit(mydata)

我目前的问题是我很容易耗尽内存。 (我目前正在使用 16 GB RAM 的机器)

我的问题是,DBSCAN 是否会在运行时即时计算成对距离矩阵,这就是吞噬我记忆的原因? (250 万 ^ 2) * 8 字节显然大到愚蠢,我会理解的。我不应该使用fit() 方法吗?更一般地说,有没有办法解决这个问题,或者我通常在这里吠叫错误的树?

如果答案很明显,我们深表歉意。这几天我一直在纠结这个问题。谢谢!

附录:另外,如果有人能更明确地向我解释 fit(X)fit_predict(X) 之间的区别,我也将不胜感激——恐怕我只是不太明白。

附录 #2:可以肯定的是,我只是在一台内存约为 550 GB 的机器上尝试过,但它仍然崩溃,所以我觉得 DBSCAN 可能会尝试制作成对距离矩阵或我显然不知道的东西'不想让它这样做。我想现在最大的问题是如何阻止这种行为,或者找到其他可能更适合我需要的方法。谢谢你在这里陪我。

附录#3(!):我忘了附上回溯,在这里,

Traceback (most recent call last):
  File "tDBSCAN.py", line 34, in <module>
    db = DBSCAN(eps=float(sys.argv[2]), min_samples=10, metric='cityblock').fit(mydata)
  File "/home/jtownsend/.local/lib/python2.6/site-packages/sklearn/base.py", line 329, in fit_predict
    self.fit(X)
  File "/home/jtownsend/.local/lib/python2.6/site-packages/sklearn/cluster/dbscan_.py", line 186, in fit
    **self.get_params())
  File "/home/jtownsend/.local/lib/python2.6/site-packages/sklearn/cluster/dbscan_.py", line 69, in dbscan
    D = pairwise_distances(X, metric=metric)
  File "/home/jtownsend/.local/lib/python2.6/site-packages/sklearn/metrics/pairwise.py", line 651, in pairwise_distances
    return func(X, Y, **kwds)
  File "/home/jtownsend/.local/lib/python2.6/site-packages/sklearn/metrics/pairwise.py", line 237, in manhattan_distances
    D = np.abs(X[:, np.newaxis, :] - Y[np.newaxis, :, :])
MemoryError

【问题讨论】:

    标签: python scikit-learn cluster-analysis data-mining dbscan


    【解决方案1】:

    我在 sklearn 0.19.1 上使用旧版本时遇到了同样的问题,因为复杂度是 O(N^2)。

    但现在这个问题已经在新版本 0.20.2 中得到解决,不再出现内存错误,复杂度变为 O(n.d),其中 d 是邻居的平均数量。 这不是理想的复杂性,但比旧版本要好得多。

    查看此版本中的注释,以避免高内存使用: https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html

    【讨论】:

      【解决方案2】:

      问题显然是scikit-learn 中的非标准 DBSCAN 实现。

      DBSCAN 不需要距离矩阵。该算法是围绕使用可以加速regionQuery函数的数据库设计的,并有效地返回查询半径内的邻居(空间索引应该支持O(log n)中的此类查询)。

      然而,scikit 中的实现显然会计算完整的O(n^2) 距离矩阵,这需要在内存方面和运行时方面都付出代价。

      所以我看到了两个选择:

      1. 您可能想改用ELKI 中的 DBSCAN 实现,当与 R*-tree 索引一起使用时,它通常比简单实现快得多。

      2. 否则,您可能需要重新实现 DBSCAN,因为 scikit 中的实现显然不太好。不要害怕:DBSCAN 自己实现起来非常简单。一个好的 DBSCAN 实现中最棘手的部分实际上是 regionQuery 函数。如果你能快速得到这个查询,DBSCAN 就会很快。您实际上也可以将此函数重用于其他算法。

      更新:到目前为止,sklearn 不再计算距离矩阵,并且可以使用 kd-tree 索引。但是,由于“矢量化”,它仍然会预先计算每个点的邻居,因此 sklearn 用于大 epsilon 的内存使用量为 O(n²),而据我了解,ELKI 中的版本只会使用O(n) 内存。因此,如果您的内存不足,选择较小的 epsilon 和/或尝试ELKI

      【讨论】:

      • 其实改进sklearn的实现似乎并不会太难。我们有一个完全支持半径查询的球树数据结构。我对 dbscan 不是很熟悉,所以我不知道它只需要这些查询。我们绝对应该在那里改进。
      • 认为 sklearn 实现在 sklearn 0.14 中得到了显着改进:ball-tree 实现现在支持良好的指标选择,并且 DBSCAN 已适应不内部计算整个成对距离矩阵。所以它似乎又是一个选项,不幸的是,成对度量包仍然不支持半正弦距离。相关 github 票证(注意,更改分布在许多拉取请求和票证上):github.com/scikit-learn/scikit-learn/issues/1938
      • 我同意,sklearn 改进了它的 DBSCAN。尽管如此,ELKI 在索引加速和聚类分析方面仍然更强大。例如,它还具有 OPTICS 和其他 DBSCAN 衍生物。
      • 问题是 ELKI 没有好的文档或“hello world”示例。
      • 我发现“hello world”网站上的thr mouse example等教程已经足够了。而且 javadoc 也很不错。
      【解决方案3】:

      这里讨论 sklearn 的这个问题:

      https://github.com/scikit-learn/scikit-learn/issues/5275

      这里有两个选项;

      一种是使用 OPTICS(需要 sklearn v21+),这是一种替代但与 DBSCAN 密切相关的算法:

      https://scikit-learn.org/dev/modules/generated/sklearn.cluster.OPTICS.html

      其他的是预先计算邻接矩阵,或者使用样本权重。 有关这些选项的更多详细信息,请参见此处的注释:

      https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html

      【讨论】:

      • 很好很好,我刚刚尝试使用 OPTICS 并且它工作正常,使用 43000 行的 ndarray 大约需要 2 分钟,使用 DBSCAN 使用相同的 ndarray 我遇到了内存崩溃错误。
      【解决方案4】:

      您可以将 scikit-learn 的 DBSCAN 与半正弦度量和球树算法结合使用。您不需要预先计算距离矩阵。

      此示例 clusters over a million GPS latitude-longitude points 使用 DBSCAN/haversine 并避免内存使用问题:

      df = pd.read_csv('gps.csv')
      coords = df.as_matrix(columns=['lat', 'lon'])
      db = DBSCAN(eps=eps, min_samples=ms, algorithm='ball_tree', metric='haversine').fit(np.radians(coords))
      

      请注意,这专门使用 scikit-learn v0.15,因为一些早期/后期版本似乎需要计算完整的距离矩阵,这会很快炸毁你的 RAM。但如果你使用 Anaconda,你可以通过以下方式快速设置:

      conda install scikit-learn=0.15
      

      或者,为这个集群任务创建一个干净的虚拟环境:

      conda create -n clusterenv python=3.4 scikit-learn=0.15 matplotlib pandas jupyter
      activate clusterenv
      

      【讨论】:

      • 确认,sklearn v0.15.2 需要的内存远少于 v0.17.1 来运行相同的模型拟合
      【解决方案5】:

      DBSCAN 算法实际上确实计算了距离矩阵,所以这里没有机会。 对于这么多数据,我建议使用 MiniBatchKMeans。 您不能开箱即用地使用曼哈顿指标,但您可以自己实现。也许先尝试使用欧几里德度量的标准实现。

      我不知道很多不执行成对距离的聚类算法。

      使用新嵌入的cheat-sheet底部中心:虽然很幸运。

      【讨论】:

      • 没有办法即时计算它们吗?我理解 DBSCAN 的方式我不清楚为什么我不能从一个随机点开始,计算它到其他点的距离,然后将它与 epsilon 进行比较,一次又一次地将其丢弃或添加为邻居...
      • @JamesT:虽然有可能,但当前的 scikit-learn 实现根本不这样做。它并不能真正扩展到大量样本,因为它需要二次空间和时间。
      • 不正确。 DBSCAN 不需要距离矩阵(特别是不需要矩阵)。一个好的实现应该使用空间索引,以显着减少所需的距离计算次数。它应该在 O(n) 内存和 O(n log n) 运行时中实现。
      • DBSCAN 算法本身不需要计算整个距离矩阵。例如,参见 Wikipedia en.wikipedia.org/wiki/DBSCAN#Algorithm 上的基本伪代码@ scikit 的早期版本依赖于距离矩阵的完整计算,但现在不再是这种情况
      • @titus 根据我的经验,v0.15.2 运行相同代码所需的内存远少于 v0.17.1。知道为什么吗?
      猜你喜欢
      • 2015-03-05
      • 2016-01-01
      • 2014-09-11
      • 2016-06-25
      • 2019-04-15
      • 2013-04-12
      • 2014-10-02
      • 2013-07-01
      • 2014-05-31
      相关资源
      最近更新 更多