我需要将两个大矩阵相乘并对其列进行排序。
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盒子。
我想知道这个操作是否可以序列化,我不必将所有内容存储在内存中。
答案 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
。]
结果是;
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()
行并加载下一行。这不会让它变得更快,但它会减少使用内存!
编辑:在这种情况下,我建议以二进制格式编写输出文件(例如使用struct
或ndarray.tofile
)。这样可以更容易地从文件中读取列,例如一个numpy.memmap
。
答案 1 :(得分:3)
DrV和罗兰史密斯所说的是好的答案;应该听取他们的意见。我的回答只不过是提供一个让你的数据稀疏的选项,一个完整的游戏改变者。
稀疏性可以非常强大。它会将您的O(100 * 300000 * 1000000)
操作转换为具有k个非零元素的O(k)
操作(稀疏性仅表示矩阵大部分为零)。我知道DrV已经提到了稀疏性并且被忽视为不适用但我猜它是。
所有需要做的就是找到一个sparse representation来计算这个变换(并且解释结果是另一个球赛)。简单(和快速)方法包括Fourier transform或wavelet 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')如果您希望输出矩阵大部分为零,那么可能会完全改变游戏。