在 python 中处理大型密集矩阵

Posted

技术标签:

【中文标题】在 python 中处理大型密集矩阵【英文标题】:Handling large dense matrices in python 【发布时间】:2011-03-14 05:11:37 【问题描述】:

基本上,在 python 中存储和使用密集矩阵的最佳方法是什么?

我有一个项目可以在数组中的每个项目之间生成相似度指标。

每个项目都是一个自定义类,并存储一个指向另一个类的指针和一个表示它与该类“接近度”的数字。

现在,它可以出色地处理大约 8000 个项目,之后它会因内存不足错误而失败。 基本上,如果您假设每次比较使用 ~30(根据测试似乎准确)字节来存储相似性,这意味着所需的总内存为:numItems^2 * itemSize = Memory 因此,内存使用量是基于项目数的指数。 就我而言,每个链接的内存大小约为 30 字节,因此:8000 * 8000 * 30 = 1,920,000,000 bytes, or 1.9 GB 这正好是单个线程的内存限制。

在我看来,必须有一种更有效的方法来做到这一点。我看过 memapping,但它已经是计算密集型的,只是为了生成相似度值,而且通过硬盘驱动器瓶颈似乎有点荒谬。

编辑 我看过numpy和scipy。不幸的是,它们也不支持非常大的数组。

>>> np.zeros((20000,20000), dtype=np.uint16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError
>>>

进一步编辑 Numpy 似乎很受欢迎。但是,numpy 不会真正做我想做的事,至少没有另一个抽象层。

我不想想要存储数字,我想存储对类的引用。 Numpy 支持对象,但这并不能真正解决数组大小问题。我提出 numpy 只是作为不起作用的一个例子。

有什么建议吗?

编辑好吧,我最后只是重写了所有逻辑,使其不再存储任何冗余值,从而将内存使用量从 O*n^2 减少到 O*((n*(n-1))/2)

基本上,这整个事件是handshake problem 的一个版本,所以我已经从存储所有链接切换到每个链接只存储一个版本。

这不是一个完整的解决方案,但我通常没有足够大的数据集来溢出它,所以我认为它会解决的。 PyTables 真的很有趣,但我不知道任何 SQL,而且似乎没有任何好的传统切片或基于索引的方式来访问表数据。我以后可能会重新讨论这个问题。

【问题讨论】:

嗯。刚刚找到 PyTables(pytables.org)。看起来很有希望 愿意将其发布为答案吗?看来是要走的路。 【参考方案1】:

好吧,我找到了解决方案:h5py

这是一个库,基本上提供了一个类似 numpy 的界面,但使用压缩的 memmapped 文件来存储任意大小的数组(它基本上是 HDF5 的包装器)。

PyTables 是建立在它之上的,而 PyTables 实际上引导我找到了它。但是,我不需要任何作为 PyTables 主要产品的 SQL 功能,而且 PyTables 没有提供我真正想要的干净的类似数组的接口。

h5py 基本上就像一个 numpy 数组,只是以不同的格式存储数据。

除了磁盘空间之外,它似乎对数组大小也没有限制。我目前正在对 100,000 * 100,000 的 uint16 数组进行测试。

【讨论】:

6个月后,我想知道您是否认为h5py仍然是使用Python和numpy对无法放入内存的数据进行操作的门票? 真的,我只是最近没有机会看这个项目。工作调停,我最近有点筋疲力尽,因此对从事个人项目不感兴趣。 感谢您的更新。如果您不介意离线联系我,我想问您一些其他问题,了解您使用 h5py 的体验,因为我目前在工作中遇到内存限制,需要基于 Python 磁盘的分析选项。 我不确定我的知识会有多大用处,但我是 lemuix AT imaginaryindustries DOT com 或 Skype 上的假名。我不假装自己是专家,但我设法使 h5py 工作(至少一点点)。请随时与我联系。 如果我想访问整个矩阵以将输入提供给大小为 57600 x 57600 的双精度函数怎么办?【参考方案2】:

PyTables 可以通过使用 memmap 和一些巧妙的压缩来处理任意大小的表(数百万列!)。

从表面上看,它为 python 提供了类似 SQL 的性能。但是,它需要对代码进行大量修改。

在我进行更彻底的审查之前,我不会接受这个答案,以确保它实际上可以做我想做的事。或者有人提供了更好的解决方案。

【讨论】:

【参考方案3】:

对于 20,000 x 20,000,您正在寻找 12GB 的 RAM?

在 win32 中尝试使用 12GB 人为限制操作系统可以寻址的内存,您不会最终陷入交换地狱吗?

我正在寻找可以支持 12GB 的操作系统(如果您需要坚持使用 32 位 Windows,32 bin win 2003 服务器可以),但是具有 64 位操作系统和 16GB 内存的 64 位机器似乎更合适.

升级的好借口:)

64 位 numpy 可以支持你的矩阵

Python 2.5.2 (r252:60911, Jan 20 2010, 23:14:04) 
[GCC 4.2.4 (Ubuntu 4.2.4-1ubuntu3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.zeros((20000,20000),dtype=np.uint16)
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ..., 
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=uint16)

【讨论】:

你不会陷入交换地狱,python 只是死了,要么是内存错误,要么是静默。我问是因为我不敢相信没有人遇到过类似的问题,并试图在 32 位上解决它。 @Fake Name,如果您尝试使用 12GB 的数据和限制为 2GB 或 3GB RAM 的内核,无论您尝试使用哪种方式,都会发生交换地狱。很可能没有人尝试在 32 位上解决它,因为使用具有足够 RAM 的 64 位机器更实用 (延迟响应)不,您不会陷入交换地狱,python 因内存不足错误而崩溃。它永远不会命中交换文件。 @Fake,我的意思是,如果您尝试对 12GB 数据进行操作,而一次只在进程内存中保留一小部分数据。交换需要由您的进程管理(因为您超出了进程的可寻址内存)您自己编写的东西或由 ORM 等抽象提供的东西 啊,是的。我真的不想编写自己的交换系统。事实上,一个压缩的 memmapped 数组似乎工作得很好。我受 CPU 限制,而不是 IO 限制。【参考方案4】:

您可能会在 NumPy (see SciPy) 文档(数组/矩阵)中找到一些建议:

http://www.scipy.org/NumPy_for_Matlab_Users http://www.scipy.org/Tentative_NumPy_Tutorial

【讨论】:

不 - 幸运的是,Numpy 具有与我已经描述的相同的数组大小限制。【参考方案5】:

如果您有 N 个对象,保存在列表 L 中,并且您希望存储每个对象和其他对象之间的相似性,那就是 O(N**2) 相似性。在similarity(A, B) == similarity(B, A)similarity(A, A) == 0 的共同条件下,你只需要一个相似的三角数组S。该数组中的元素数将为N*(N-1)//2。您应该能够为此目的使用 array.array。将您的相似性保持为浮点数只需要 8 个字节。如果您可以在range(256) 中将相似度表示为整数,则可以使用无符号字节作为 array.array 元素。

这大约是 8000 * 8000 / 2 * 8,即大约 256 MB。仅使用一个字节来表示相似性意味着只有 32 MB。您可以通过模拟正方形数组而不是使用 S[i*N+j]`; 来避免三角形事物的缓慢S[i*N-i*(i+1)//2+j] 索引计算;内存将翻倍至(浮点数为 512 MB,字节数为 64 MB)。

如果以上不适合你,那么也许你可以解释“”“每个项目[在哪个容器中?] 是一个自定义类,并存储一个指向另一个类的指针和一个表示它“接近”的数字那个类。””和“”“我不想存储数字,我想存储对类的引用”“”。即使在用“对象”替换“类”之后,我也很挣扎理解你的意思。

【讨论】:

【参考方案6】:

使用 uint8 可以减少内存使用,但要小心避免溢出错误。 uint16 需要两个字节,因此您的示例中的最小内存要求是 8000*8000*30*2 字节 = 3.84 Gb。

如果第二个示例失败,那么您需要一台新机器。内存要求为 20000*20000*2*bytes =800 Mb。

我的建议是您尝试创建更小的矩阵并使用“top”、“ps v”或 gnome 系统监视器来检查您的 python 进程使用的内存。开始检查带有小矩阵的单个线程并进行数学计算。请注意,您可以通过编写 del(x) 来释放变量 x 的内存。这对测试很有用。

您机器上的内存是多少? pytables创建一个20000*20000的表需要多少内存? numpy使用uint8创建一个20000*20000的表需要多少内存?

【讨论】:

另外,不能使用 uint8 作为 20000 行表中位置的引用,因此 uint16 的选择是经过仔细计算的。 问题可能是由 32 位 Windows 限制引起的:“首先,32 位 Python 解释器只能使用 2 GB 的内存。其次,numpy 数组需要一个连续的、未分段的, 2 GB 地址空间内可用的内存范围。” (引自 old.nabble.com/… )我认为您可以强制 numpy 使用非连续(c 样式)数组,但这不会消除 2Gb 内存限制。 也许解决方案是停止使用 Windows 32。您可能希望查看名为 IMAGE_FILE_LARGE_ADDRESS_AWARE 的标志,但我不知道这是否可以安全使用。以下是 Microsoft 内存限制的说明:msdn.microsoft.com/en-us/library/aa366778(VS.85).aspx

以上是关于在 python 中处理大型密集矩阵的主要内容,如果未能解决你的问题,请参考以下文章

C# 中的密集文件 I/O 和数据处理

使用密集和稀疏矩阵

从numpy python中的稀疏矩阵生成密集矩阵

numpy.ndarray稀疏矩阵到密集

机器学习入门——numpy100练习题

python中的矩阵运算