如何减少scipy.sparse.dok_matrix

时间:2018-04-16 09:11:04

标签: python python-3.x numpy scipy sparse-matrix

我希望使用中间scipy.sparse.dok_matrix存储格式构建稀疏实矩阵,然后将矩阵转换为scipy.sparse.csc_matrix存储格式,并使用生成的矩阵执行计算。但是,中间矩阵的大小大大超出了我的估计和可用内存。

我的环境

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

使用numpy

当我按如下方式构建一个20,000×20,000个16位整数的numpy数组时,我估计大小将是20000 ** 2 * 16/8/2 **20≈763M。该进程的驻留集大小(RSS)为815M,由htop unix工具报告,与我的估算相符。

import numpy as np

n = 20000
M = np.ones((n, n), dtype=np.uint16)

使用dict

当我使用16位整数作为关键字和值来构建20,000×20,000 scipy.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位整数作为键和值构建20,000×20,000 scipy.sparse.dok_matrix时,我估计大小将再次为20000 ** 2 *(16 * 3)/ 8/2 ** 30 ≈2.24G加上哈希表的一些小开销。但是,该过程的RSS为81.3G,与dict示例相比,我的估计值更远。

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

1 个答案:

答案 0 :(得分:2)

虽然您可以控制dtype的{​​{1}}值data,但您无法控制密钥存储空间。

查看dok类代码:

dok_matrix

因此元素存储为选定def __setitem__(self, index, x): ... v = np.asarray(x, dtype=self.dtype) ... dict.__setitem__(self, (int(i), int(j)), v[()]) 的numpy'标量'对象:

dtype

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

最近的另一个问题是尝试使用In [129]: M = sparse.dok_matrix((10,10), dtype=np.uint16) In [130]: M[0,0] = 1 In [131]: list(M.items()) Out[131]: [((0, 0), 1)] In [132]: type(_[0][1]) Out[132]: numpy.uint16 来比较数组和列表的内存使用情况。在这个上使用它:

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数组。索引数组存储为csrint32,具体取决于矩阵的维度。

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

int64

由于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格式占用密集数组空间的3倍是有意义的。 coo每个coodatarow都有一个100个元素数组。 col尝试压缩csr数组,但区别并不总是那么重要。

我必须使用row作为pickledok无法处理sparse.save_npz