在scipy中计算成对距离时的内存错误

时间:2012-04-11 23:43:36

标签: python numpy scipy cluster-analysis

我正在尝试将分层聚类应用于我的数据集,该数据集由14039个用户向量组成。每个向量具有10个特征,其中每个特征基本上是由该用户标记的标签的频率。 我正在使用Scipy api进行聚类。 现在我需要计算这些14039用户之间的成对距离,并将tis距离矩阵传递给连接函数。

  import scipy.cluster.hierarchy as sch
  Y = sch.distance.pdist( allUserVector,'cosine')
  set_printoptions(threshold='nan')
  print Y

但是我的程序在计算距离矩阵本身时给了我MemoryError

  File "/usr/lib/pymodules/python2.7/numpy/core/numeric.py", line 1424, in array_str
    return array2string(a, max_line_width, precision, suppress_small, ' ', "", str)
  File "/usr/lib/pymodules/python2.7/numpy/core/arrayprint.py", line 306, in array2string
    separator, prefix)
  File "/usr/lib/pymodules/python2.7/numpy/core/arrayprint.py", line 210, in _array2string
    format_function = FloatFormat(data, precision, suppress_small)
  File "/usr/lib/pymodules/python2.7/numpy/core/arrayprint.py", line 392, in __init__
    self.fillFormat(data)
  File "/usr/lib/pymodules/python2.7/numpy/core/arrayprint.py", line 399, in fillFormat
    non_zero = absolute(data.compress(not_equal(data, 0) & ~special))
MemoryError

知道怎么解决这个问题吗?我的数据集太大了吗?但我想集群14k用户不应该太多,它应该导致内存错误。 我在i3和4 Gb Ram上运行它。 我也需要应用DBScan聚类,但这也需要距离矩阵作为输入。

任何建议表示赞赏。

编辑:我只在打印Y时才收到错误。有什么想法吗?

2 个答案:

答案 0 :(得分:5)

你可能真的没用RAM了。找到N个对象之间的成对距离意味着存储N ^ 2个距离。在你的情况下,N ^ 2将是14039 ^ 2 = 1.97 * 10 ^ 8。如果我们假设每个距离只需要四个字节(几乎可以肯定不是这种情况,因为它们必须保存在某种可能具有非常量开销的数据结构中),这可以达到800兆字节。对于翻译来说,这是一个很大的记忆。 32位体系结构只允许最多2 GB的进程内存,而原始数据只占用50%左右。随着数据结构的开销,你可能会看到比这更高的使用率 - 我不能说多少因为我不知道SciPy / numpy背后的内存模型。

我会尝试将您的数据集分成更小的集合,或者不构建完整的距离矩阵。您可以将其分解为更易于管理的块(例如,大约1000个元素的14个子集),并在每个块和所有向量之间进行最近邻居 - 然后您正在考虑在任何内存中加载一个数量级的内存。一次(14000 * 1000,14次而不是14000 * 14000次)。

编辑: agf在两个方面完全正确:我错过了你的编辑,当问题尝试构建代表你的矩阵的巨型字符串时,可能会出现问题。如果它正在打印浮点值,并且我们假设每个元素打印10个字符,并且字符串每个字符存储一个字节,那么您只需查看字符串的2 GB内存使用情况。

答案 1 :(得分:5)

好吧,层次聚类对大型数据集没有多大意义。在我看来,它实际上主要是一本教科书的例子。分层聚类的问题在于它并不真正构建合理的聚类。它构建了一个树形图,但是有了14000个对象,树形图几乎无法使用。并且很少有层次聚类的实现具有从树形图中提取敏感聚类的非平凡方法。此外,在一般情况下,层次聚类具有复杂性O(n^3),这使得它对大型数据集的扩展非常糟糕。

DBSCAN技术上需要距离矩阵。事实上,当你使用距离矩阵时,它将,因为计算距离矩阵已经是O(n^2)。即使这样,您也可以通过计算飞行距离来保证DBSCAN的O(n^2)内存成本,但每次计算距离为两倍。 DBSCAN访问每个点一次,因此除了对称增益之外,使用距离矩阵几乎没有任何好处。从技术上讲,你可以做一些巧妙的缓存技巧甚至减少,因为DBSCAN也只需要知道哪些对象低于epsilon阈值。当合理选择epsilon时,在计算距离矩阵的相同CPU成本下,动态管理邻居集将使用比O(n^2)少得多的内存。

DBSCAN的任何非常好的实现(它拼写为全部大写,顺便说一句,因为它是缩写,而不是扫描)但是应该支持索引结构,然后在O(n log n)运行时运行。

http://elki.dbs.ifi.lmu.de/wiki/Benchmarking上,他们在110250对象数据集和8维上运行DBSCAN,非索引变体需要1446秒,索引只有219.这大约快7倍,包括索引构建。 (但这不是python)同样,OPTICS的索引速度要快5倍。在我的实验中他们的kmeans实现速度比WEKA kmeans快6倍,并且使用的内存要少得多。它们的单链路层次聚类也是一种优化的O(n^2)实现。实际上到目前为止,我见过的唯一一个不是天真的O(n^3)矩阵编辑方法。 如果你愿意超越python,这可能是一个不错的选择。