Python NUMPY HUGE Matrices乘法

时间:2014-07-30 20:14:42

标签: python performance numpy matrix-multiplication

我需要将两个大矩阵相乘并对其列进行排序。

 import numpy
 a= numpy.random.rand(1000000, 100)
 b= numpy.random.rand(300000,100)
 c= numpy.dot(b,a.T)
 sorted = [argsort(j)[:10] for j in c.T]

这个过程需要大量的时间和内存。有没有办法加快这个过程?如果不是,我如何计算执行此操作所需的RAM?我目前有一个带有4GB RAM且没有交换的EC2盒子。

我想知道这个操作是否可以序列化,我不必将所有内容存储在内存中。

3 个答案:

答案 0 :(得分:7)

你可以做的一件事就是加速编译numpy,使用优化的BLAS库,例如: ATLAS,GOTO blas或英特尔专有的MKL。

要计算所需的内存,您需要监控Python的驻留集大小(" RSS")。以下命令在UNIX系统上运行(准确地说,在64位计算机上运行FreeBSD)。

> ipython

In [1]: import numpy as np

In [2]: a = np.random.rand(1000, 1000)

In [3]: a.dtype
Out[3]: dtype('float64')

In [4]: del(a)

要获取我运行的RSS:

ps -xao comm,rss | grep python

[编辑:有关选项的完整说明,请参阅ps manual page,但基本上这些ps选项使其仅显示所有进程的命令和驻留集大小。我相信,Linux ps的等效格式为ps -xao c,r。]

结果是;

  • 启动翻译后:24880 kiB
  • 导入numpy后:34364 kiB
  • 创建a后:42200 kiB
  • 删除a后:34368 kiB

计算尺寸;

In [4]: (42200 - 34364) * 1024
Out[4]: 8024064

In [5]: 8024064/(1000*1000)
Out[5]: 8.024064

如您所见,计算出的大小非常匹配默认数据类型float64的8个字节。差异在于内部开销。

MiB中原始阵列的大小约为;

In [11]: 8*1000000*100/1024**2
Out[11]: 762.939453125

In [12]: 8*300000*100/1024**2
Out[12]: 228.8818359375

那不算太糟糕。但是,点积太大了:

In [19]: 8*1000000*300000/1024**3
Out[19]: 2235.1741790771484

那是2235 GiB!

您可以做的是分解问题并将dot操作分成几部分;

  • b加载为ndarray
  • 依次将a的每一行加载为ndarray
  • 将行乘以b的每一列,并将结果写入文件。
  • del()行并加载下一行。

这不会让它变得更快,但它会减少使用内存!

编辑:在这种情况下,我建议以二进制格式编写输出文件(例如使用structndarray.tofile)。这样可以更容易地从文件中读取列,例如一个numpy.memmap

答案 1 :(得分:3)

DrV和罗兰史密斯所说的是好的答案;应该听取他们的意见。我的回答只不过是提供一个让你的数据稀疏的选项,一个完整的游戏改变者。

稀疏性可以非常强大。它会将您的O(100 * 300000 * 1000000)操作转换为具有k个非零元素的O(k)操作(稀疏性仅表示矩阵大部分为零)。我知道DrV已经提到了稀疏性并且被忽视为不适用但我猜它是。

所有需要做的就是找到一个sparse representation来计算这个变换(并且解释结果是另一个球赛)。简单(和快速)方法包括Fourier transformwavelet transform(两者都依赖于矩阵元素之间的相似性),但这个问题可通过several different algorithms推广。

有这样的问题的经验,这闻起来像一个相对常见的问题,通常通过一些聪明的技巧解决。在像机器学习这样的领域中,这些类型的问题被归类为“简单”,而这种情况经常发生。

答案 2 :(得分:1)

在任何情况下你都有问题。正如Roland Smith在回答中显示的那样,数据量和计算次数都是巨大的。你可能不太熟悉线性代数,所以一些解释的话可能有助于理解(然后希望解决)这个问题。

你的数组都是长度为100的向量集合。其中一个数组有300 000个向量,另一个有1 000 000个向量。这些数组之间的点积意味着您计算每对可能的向量的点积。有300,000,000个这样的对,因此得到的矩阵是1.2 TB或2.4 TB,具体取决于您使用的是32位还是64位浮点数。

在我的计算机上,将(300,100)阵列与(100,1000)阵列相乘大约需要1 ms。从中推断,您需要1000秒的计算时间(取决于核心数量)。

获取点积的好处在于你可以分段进行。保持输出是另一个问题。


如果您在自己的计算机上运行,计算结果矩阵可以通过以下方式完成:

  • 在磁盘上创建一个输出数组np.memmap数组
  • 一次计算一行结果(由Roland Smith
  • 解释

这将导致使用较大(2.4 TB)文件写入线性文件。

这不需要太多代码行。但是,确保一切都以适当的方式转换;转置输入数组很便宜,转换输出非常昂贵。如果您可以访问彼此相近的元素,那么访问生成的巨大数组是很便宜的,如果您访问远离彼此的元素,则价格昂贵。

必须小心地对巨大的memmapped数组进行排序。您应该使用对连续数据块进行操作的就地排序算法。数据存储在4 KiB块(512或1024浮点数)中,您需要读取的块越多越好。


既然您没有在我们自己的计算机上运行代码,而是在云平台上运行,那么事情就会发生很大变化。通常,随着访问的速度,云SSD存储速度非常快,但IO价格昂贵(也就金钱而言)。可能最便宜的选择是计算合适的数据块并将它们发送到S3存储器以供进一步使用。 "合适的块"部分取决于您打算如何使用数据。如果需要处理单个列,则一次向云对象存储发送一列或几列。


但是,很大程度上取决于您的分拣需求。您的代码看起来好像您最终只查看每列的几个第一项。如果是这种情况,那么您应该只计算前几个项而不是完整的输出矩阵。这样你就可以在记忆中做所有事情。

也许如果你更多地了解你的分拣需求,可以有一种可行的方法来做你想要的。

哦,有一件重要的事情:你的矩阵是密集的还是稀疏的? (稀疏意味着它们大多包含0')如果您希望输出矩阵大部分为零,那么可能会完全改变游戏。