【问题标题】:Why is my NumPy array taking much *less* memory than it should?为什么我的 NumPy 数组占用的内存比应有的少得多?
【发布时间】:2023-04-09 04:00:01
【问题描述】:

我正在处理大型矩阵,例如Movielens 20m dataset。我重组了在线文件,使其与页面上提到的尺寸(138000 x 27000)匹配,因为原始文件包含更大的索引(138000 x 131000),但包含很多空列。 只需丢弃那些空列并重新建立索引即可产生所需的维度。

无论如何,将稀疏 csv 文件转换为密集格式的 sn-p 如下所示:

import pandas as pd
from scipy import sparse

# note that the file is not the one described in the link, but the smaller one
X = pd.read_csv("ml-20m-dense.dat", sep=",", header=None)
mat = sparse.coo_matrix((X[2], (X[0], X[1]))).todense()

现在,内存中的估计大小应该接近 138000 * 27000 * 8 / (1024^3) = 27.5 GB
然而,当我使用 htop 检查进程时,内存消耗仅显示大约 7 GB,尽管保留了大约 32 GB 虚拟内存。

起初我认为这可能是由于 pandas 阅读器或 scipy.sparse 包的一些“效率技巧”,以规避内存消耗。
但即使在我调用我的 PCA 函数之后,它也不会将活动内存消耗增加到应有的量。 请注意,调用 mat.nbytes 会返回估计的确切数量,因此 NumPy 似乎至少知道数据。


(PCA代码供参考:)

from fbpca import pca
result = pca(mat, k=3, raw=False, n_iter=3)

请注意,尽管 fbpca 使用随机算法,并且我只计算前三个分量,code 仍然执行输入矩阵的(单个但完整的)矩阵乘法(更小)随机矩阵。本质上,它仍然必须至少访问输入矩阵中的每个元素一次。

最后一句话也使这与我发现的帖子略有不同,例如this,因为在那篇帖子中,元素从未真正被访问过。

【问题讨论】:

  • 7GB + 32GB = 39GB,足以容纳数据。您为什么假设该进程未将数据存储在 32GB 中?
  • 它为整个大小保留虚拟内存,但从未真正“直接使用”它。一旦我乘以矩阵,IMO 的活动消耗应该在 27 GB 左右,因为它会访问每个元素。
  • 我认为你误解了虚拟内存是什么。
  • 您愿意详细说明一下吗?我的意思是,当我乘以矩阵时,它仍应显示为主动消费,对吗?
  • 这可能是开始阅读它的好地方:serverfault.com/questions/138427/…。虚拟并不意味着进程未使用的内存,它仅表示分配给当前不在物理内存中的进程的内存。当然要执行必须加载到 ram 然后到 CPU 的矩阵乘法,但是 numpy 一次只乘几个元素。操作系统知道不将整个数组加载到内存中,只加载当前正在操作的部分。

标签: python numpy


【解决方案1】:

我认为你的问题在于todense() 调用,它使用np.asmatrix(self.toarray(order=order, out=out)) internallytoarray 使用 np.zeros 创建其输出。 (见toarray_process_toarray_args

所以你的问题可以简化为:为什么np.zeros 没有分配足够的内存?

答案大概是lazy-initializationzero pages

Why does numpy.zeros takes up little space
Linux kernel: Role of zero page allocation at paging_init time

所以矩阵中的所有零区域实际上都在同一个物理内存块中,只有写入所有条目才会强制操作系统分配足够的物理内存。

【讨论】:

  • 所以你说的是稀疏矩阵的密度(即非零元素的数量)应该对最初消耗的内存有影响......非常有趣,我会尝试用稍微不同的矩阵变化来测试它。
  • 问题仍然存在,为什么一旦我为矩阵乘法传递它就不会分配该内存。
  • @dennlinger 不仅仅是零的数量,还有只包含零的块/页面的数量。表示长的连续零序列
  • @dennlinger 您是否在数据迭代时修改了值?如果您只阅读,零页应该保留。您可以尝试用随机的非零值覆盖整个矩阵,然后再次检查 RAM 消耗
  • 确实,fbpca 的计算似乎创建了行/列的本地副本,但从未覆盖原始矩阵。因此,零页似乎仍然存在,这大大减少了内存消耗。
猜你喜欢
  • 1970-01-01
  • 2017-11-20
  • 2022-01-05
  • 2019-01-09
  • 2018-03-21
  • 2021-07-13
  • 1970-01-01
  • 1970-01-01
  • 2013-12-12
相关资源
最近更新 更多