关于python:scikit-学习DBSCAN内存使用情况

scikit-learn DBSCAN memory usage

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

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

1
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 RAM的计算机上尝试了此方法,但它仍然被炸毁,所以我觉得DBSCAN可能试图建立成对的距离矩阵或我显然不想要的东西去做。我想现在最大的问题是如何停止这种行为,或者找到其他更适合我需要的方法。感谢您在这里与我保持联系。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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

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

但是,显然scikit中的实现会计算完整的O(n^2)距离矩阵,这会在内存和运行时方面都有所增加。

所以我看到两个选择:

  • 您可能要尝试在ELKI中尝试DBSCAN实现,当与R * -tree索引一起使用时,它通常比朴素的实现快得多。

  • 否则,您可能要重新实现DBSCAN,因为scikit中的实现显然不太好。不必担心:DBSCAN真的很容易实现。好的DBSCAN实现中最棘手的部分实际上是regionQuery函数。如果您可以快速获得此查询,则DBSCAN将很快。您实际上也可以将此功能重用于其他算法。

  • 更新:现在,sklearn不再计算距离矩阵,并且可以使用例如kd-tree索引。但是,由于"矢量化",它仍然会预先计算每个点的邻居,因此sklearn在大型epsilon上的内存使用量为O(n2),而据我了解,ELKI中的版本将仅使用O(n)内存。因此,如果内存不足,请选择较小的epsilon和/或尝试ELKI。


    您可以使用scikit-learn的DBSCAN和hasrsine度量标准以及Ball-tree算法来实现。您不需要预先计算距离矩阵。

    此示例使用DBSCAN / haversine对一百万个GPS纬度经度点进行聚类,并避免了内存使用问题:

    1
    2
    3
    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,则可以使用以下方法快速进行设置:

    1
    conda install scikit-learn=0.15

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

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


    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


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

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

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


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

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

    使用新嵌入的备忘单底部中心:尽管运气不错。