如何减小存储大小scipy.sparse.dok_矩阵

2024-04-26 03:58:14 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望使用中间的^{}存储格式构建一个稀疏实矩阵,然后将该矩阵转换为^{}存储格式,并对得到的矩阵进行计算。然而,中间矩阵的大小大大超过了我的估计和可用内存。在

我的环境

$ python3 -V
Python 3.4.2
$ pip freeze | grep -E 'scipy|numpy'
numpy==1.14.2
scipy==1.0.1

使用numpy

当我构建一个20000×20000 numpy的16位整数数组时,我估计其大小将是20000**2*16/8/2**20≈763M。进程的驻留集大小(RSS)为815M,由htopunix工具报告,这与我的估计相符。在

^{pr2}$

使用dict

当我使用16位整数作为键和值构建20000×20000scipy.sparse.dok_matrix时,我估计大小将是20000**2*(16*3)/8/2**30≈2.24G加上哈希表的一些小开销。但是,这个过程的RSS为66.4G,这表明我在估计中犯了一个严重的错误。在

import numpy as np

n = 20000
M = dict()
for i in range(n):
    i = np.uint16(i)
    for j in range(n):
        j = np.uint16(j)
        M[(i, j)] = np.uint16(1)

使用scipy.sparse.dok_matrix

当我使用16位整数作为键和值构建一个20000×20000scipy.sparse.dok_matrix时,我估计大小再次是20000**2*(16*3)/8/2**30≈2.24G加上哈希表的一些小开销。但是,这个过程的RSS为81.3G,这比dict示例的RSS还要远。在

from scipy.sparse import dok_matrix
import numpy as np

n = 20000
M = dok_matrix((n, n), dtype=np.uint16)
for i in range(n):
    i = np.uint16(i)
    for j in range(n):
        j = np.uint16(j)
        M[i, j] = 1

Tags: inimportnumpyfornprange矩阵整数
1条回答
网友
1楼 · 发布于 2024-04-26 03:58:14

虽然您可以控制datadatadtype,但不能控制密钥存储。在

查看dok_matrix类代码:

def __setitem__(self, index, x):
   ...
   v = np.asarray(x, dtype=self.dtype)
   ...
   dict.__setitem__(self, (int(i), int(j)), v[()])

因此,这些元素存储为选定的dtype的numpy“scalar”对象:

^{pr2}$

我对Python dict存储了解不够,无法估计哈希表所需的内存。我不认为实际的索引元组存储在任何地方,只是它们的哈希值。在

最近的另一个问题试图使用sys.getsizeof比较数组和列表的内存使用情况。在这件事上:

In [133]: sys.getsizeof(M)     # 1 item
Out[133]: 256
In [134]: for i in range(10):
     ...:     for j in range(10):
     ...:         M[i,j]=1
     ...:         
In [135]: sys.getsizeof(M)     # full
Out[135]: 4720

对于列表,getsizeof只捕获对象开销和指针缓冲区。我不知道字典能捕捉到什么。也许只是哈希表。数据值存储在内存的其他位置:

In [136]: sys.getsizeof(Out[131][0][1])
Out[136]: 26
In [137]: M.nnz
Out[137]: 100
In [138]: 26*M.nnz
Out[138]: 2600

其他稀疏格式的存储更容易估计。coo和{}使用3个numpy数组。根据矩阵的维数,索引数组存储为int32或{}。在


为了粗略估计内存使用情况,我编写了各种格式的文件:

In [164]: np.save('Mdense',M.A)
In [165]: sparse.save_npz('Mcsr',M.tocsr())
In [166]: sparse.save_npz('Mcoo',M.tocoo())

In [179]: f = open('Mdok',mode='wb')
In [180]: pickle.Pickler(f).dump(M)
In [181]: f.close()

In [182]: ll M*
-rw-rw-r  1 paul  900 Apr 16 11:53 Mcoo.npz
-rw-rw-r  1 paul  911 Apr 16 11:53 Mcsr.npz
-rw-rw-r  1 paul  328 Apr 16 11:53 Mdense.npy
-rw-rw-r  1 paul 3023 Apr 16 11:57 Mdok

由于M已满,因此稀疏coo格式将占用密集数组的3倍空间。coodatarow和{}中的每一个都有一个100元素数组。csr试图压缩row数组,但差别并不总是那么显著。在

我不得不用pickle来表示doksparse.save_npz不处理dok。在

相关问题 更多 >