如何将一个大的(10^6 * 10^6)Numpy稀疏矩阵转换成Scipy稀疏矩阵?

fv2wmkja  于 2022-11-10  发布在  其他
关注(0)|答案(1)|浏览(138)

我有一个非常大的稀疏Numpy矩阵(numpy.ndarray类型)。该矩阵非常大,可能需要存储在虚拟内存中。如何高效地将其转换为稀疏Scipy矩阵(从scipy.sparse)(用于算术运算)?
以下是使用dok_matrix进行的直接转换,失败的原因可能是内存问题。将dok_matrix更改为csr_matrix会导致相同的内存问题。

In [1]: N=int(1e6)

In [2]: from scipy.sparse import dok_matrix

In [3]: import numpy as np

In [4]: mat=np.zeros((N,N))

In [5]: dok_matrix(mat)
zsh: killed     ipython

我目前的方法是使用嵌套循环,即使我什么都不做,它也很慢。

In [9]: for i in range(N):
   ...:     for j in range (N):
   ...:         pass

有什么有效的方法可以把一个大的(10^6 * 10^6)Numpy稀疏矩阵转换成Scipy稀疏矩阵吗?

v9tzhpje

v9tzhpje1#

当你做mat = np.zeros((N,N))的时候,Numpy分配了一个全是零的大矩阵。它向操作系统请求一个大的零化缓冲区(OS)。大多数OS实际上并不在物理内存中执行分配,而是在虚拟内存中执行分配。内存页在首次接触时进行Map。这意味着任何读或写操作都会导致虚拟页Map到物理内存中。请看这篇文章了解更多关于虚拟内存的信息。另外,请考虑阅读著名的文章What Every Programmer Should Know About Memory了解更多关于虚拟内存的信息。问题是dok_matrix(mat)需要读取整个矩阵,以便创建稀疏矩阵,因此它将触发矩阵所有页面的Map,导致内存不足。当没有更多的空间时,Linux的OOM-Killer会杀死使用过多内存的程序,因此会出现killed ipython消息。同样的问题也会发生在任何类型的稀疏矩阵上。主要的问题是你无法读取整个创建的矩阵。事实上,创建这样的矩阵几乎是没有意义的,除非你知道只有一些微小的部分将被读/写(永远不会是整个矩阵)。
解决方案是直接创建一个空间矩阵,然后像Numpy稠密矩阵一样填充它。这明显要慢得多,但这是使用稀疏矩阵的代价。DOK矩阵通常会占用大量内存,除非只填充很少的项。也就是说,DOK矩阵是最快的随机存取矩阵之一(因为它们在内部是使用哈希表实现的)。CSR适用于创建后不会更改的矩阵(即修改CSR矩阵非常慢)并且每行只有很少的非零项。请注意,CSR矩阵可以从数据1D数组和(row_ind, col_ind)数组元组中快速创建。如需详细信息,请参阅documentation
矩阵的稀疏度,也就是比率NumberOfNonZeroValue / NumberOfValues需要(非常)小,稀疏矩阵才有用。稀疏矩阵往往很慢,因为它们的内部表示是在一种压缩矩阵上操作。

相关问题