使用gensim Scipy2Corpus而不在内存中实现稀疏矩阵

时间:2017-02-07 18:01:16

标签: python numpy scipy sparse-matrix gensim

我有三个以.npy格式保存到磁盘的NumPy数组,总共大约40 GB(表示来自非常大的文档集的文本计数数据)。这三个数组代表scipy.sparse.csc_matrix稀疏矩阵的dataindicesindptr属性。

我想在此数据上使用分布式gensim LsiModel,特别是使用gensim.matutils.Scipy2Corpus函数从底层稀疏矩阵提供语料库迭代器。

但是,我不想在内存中实现整个矩阵。相反,我如何告诉gensim有关底层磁盘数据的信息,并根据需要将gensim流从磁盘传输到csc矩阵以将块分发给工作进程?如果我理解正确,这就是Scipy2Corpusdistributed LsiModel examples声称要做的事情,但他们需要提前将数组实现为csc矩阵。

我尝试使用mmap_mode='r'加载每个底层数组,但是构造物化csc矩阵的函数scipy.sparse.csc_matrix将无论如何都会提取所有数据。

1 个答案:

答案 0 :(得分:0)

我做了一个稀疏矩阵:

In [207]: A=sparse.random(100,100,.1,'csr')
In [208]: A
Out[208]: 
<100x100 sparse matrix of type '<class 'numpy.float64'>'
    with 1000 stored elements in Compressed Sparse Row format>

制作其属性的副本,并保存它们

In [209]: D,I,J = A.data.copy(),A.indptr.copy(),A.indices.copy()
In [236]: np.save('sparse_d',D)
In [237]: np.save('sparse_I',I)
In [238]: np.save('sparse_J',J)

如果我将它们重新加载为mmap,我可以创建一个新矩阵:

In [240]: D1=np.load('sparse_d.npy', mmap_mode='r')
In [241]: I1=np.load('sparse_I.npy', mmap_mode='r')
In [242]: J1=np.load('sparse_J.npy', mmap_mode='r')
In [243]: Amm=sparse.csr_matrix((D1,J1,I1),A.shape)

但是这个新数组的属性是文件的内存副本:

In [246]: type(D1)
Out[246]: numpy.core.memmap.memmap
In [247]: type(Amm.data)
Out[247]: numpy.ndarray

因此,由mmap数组组成的稀疏矩阵会加载所有数据(如原始帖子中所述)。

In [252]: A.indptr
Out[252]: 
array([   0,   10,   20,   29,   36,   46,   60,   66,   74,   85,   93,
        104,  117,  128,  139,  147,  156,  165,  172,  188,  201,  211, ...

我可以通过

获得A切片
In [254]: A37=A[3:7,:]
In [255]: A37
Out[255]: 
<4x100 sparse matrix of type '<class 'numpy.float64'>'
    with 37 stored elements in Compressed Sparse Row format>

这是A的副本,而不是视图(就像密集阵列一样)。

这些行的相关indptr值为

In [256]: I1[3:8]
Out[256]: memmap([29, 36, 46, 60, 66])

66-29是37,A37中非零值的数量。

我可以从mmap数组的切片中创建一个稀疏矩阵:

In [259]: Amm37=sparse.csr_matrix((D1[29:66],J1[29:66],I1[3:8]-I1[3]), shape=(4, 100))
In [260]: Amm37
Out[260]: 
<4x100 sparse matrix of type '<class 'numpy.float64'>'
    with 37 stored elements in Compressed Sparse Row format>
In [261]: np.allclose(A37.A, Amm37.A)
Out[261]: True

虽然Amm37的属性在内存中,但该函数的D1J1输入为mmap个切片:

In [262]: D1[29:66]
Out[262]: 
memmap([ 0.08874032,  0.18952295,  0.43034343,  0.83725733,  0.61073925,
        0.9178207 ,  0.03644072,  0.32206696,  0.90945298,  0.20585155,
        .... 0.43905333])
In [263]: I1[3:8]-I1[3]
Out[263]: array([ 0,  7, 17, 31, 37])

所以是的,我可以从保存的属性中重新创建原始A的行切片,而无需加载整个mmap数组。我只需要从3个阵列中选择切片。同样,我可以从csc格式数组中选择列。

但是从csr属性中提取列而不完全加载它是非常困难的。所需的dataindices值将分散到存储的数组中。将csr属性转换为csc需要创建完整的csr矩阵并使用其tocsc()方法。