如何高效地在SciPy的稀疏矩阵sparse.lil_matrix中设置行?

5
我有大约200,000维的稀疏向量。我还有一个与向量数量相同的行数和列数的矩阵。我想以增量方式将所有向量设置到矩阵中,也就是说,第一个向量应该设置为第一行,依此类推。
目前,矩阵和向量的类型为scipy.sparse.lil_matrix。使用以下函数将向量设置为矩阵的特定行:
In [7]: us.get_utterance_representation('here is a sentence')
Out[7]:
<1x188796 sparse matrix of type '<type 'numpy.float64'>'
    with 22489 stored elements in Compressed Sparse Row format>

def set_row_vector(self, row, rowvector):
    self.matrix[row] = rowvector[0]

for row, utterance in enumerate(utterances):
    uvector = self.get_utterance_representation(utterance)
    self.utterancematrix.add_row_vector(row, uvector)

其中uvector是一个1x~200000维的lil_matrix矩阵。

用这种方式创建矩阵非常低效,一个单独的文本字符串(话语)需要多达5秒的时间。通过分析性能,我得出结论,将向量设置为矩阵中的一行是主要问题。

     55     def set_row_vector(self, row, rowvector):
         2564609 function calls (2564606 primitive calls) in 5.046 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    22489    1.397    0.000    1.397    0.000 {numpy.core.multiarray.where}
    22489    0.783    0.000    2.188    0.000 csr.py:281(_get_single_element)
    44978    0.365    0.000    0.916    0.000 stride_tricks.py:35(broadcast_arrays)
    44978    0.258    0.000    0.413    0.000 stride_tricks.py:22(as_strided)
   202490    0.244    0.000    0.244    0.000 {numpy.core.multiarray.array}
    22489    0.199    0.000    2.221    0.000 lil.py:280(__setitem__)
    44978    0.174    0.000    0.399    0.000 sputils.py:171(_unpack_index)
   584777    0.171    0.000    0.171    0.000 {isinstance}
    44988    0.170    0.000    0.230    0.000 sputils.py:115(isintlike)
    67467    0.166    0.000    0.278    0.000 sputils.py:196(_check_boolean)
    22489    0.154    0.000    0.647    0.000 sputils.py:215(_index_to_arrays)
        1    0.129    0.129    5.035    5.035 dsm_classes.py:55(set_row_vector)
    22489    0.120    0.000    0.171    0.000 lil.py:247(_insertat2)
    67467    0.102    0.000    0.102    0.000 {method 'ravel' of 'numpy.ndarray' objects}

我的问题是,有没有更好的方法来从话语中创建矩阵呢?
(谢谢)
1个回答

7

首先,我认为你的实际上是以CSR格式而不是LIL格式存储的。这可能是最好的选择,然而:

In [30]: import scipy.sparse as ss

In [31]: row = ss.rand(1,5000,0.1,'csr')

In [32]: matrix = ss.lil_matrix((30,5000))

In [33]: %timeit matrix[0] = row
10 loops, best of 3: 65.6 ms per loop

In [34]: row_lil = row.tolil()

In [35]: %timeit matrix[0] = row_lil
10 loops, best of 3: 93.4 ms per loop

接下来,您可以通过省略 [0] 下标来减少一些成本,这在您的 rowvector 上:

In [38]: %timeit matrix[0] = row[0]
10 loops, best of 3: 104 ms per loop

In [39]: %timeit matrix[0] = row
10 loops, best of 3: 68.7 ms per loop

最后,解决方案的真正核心是尽可能避免使用LIL格式。虽然它是最灵活的格式,但通常也是最慢的。例如,如果您只想逐行构建矩阵,则可以使用scipy.sparse.vstack

In [40]: %%timeit
   ....: for i in xrange(matrix.shape[0]):
   ....:   matrix[i] = row
   ....:
1 loops, best of 3: 3.14 s per loop

In [41]: %timeit ss.vstack([row for i in xrange(matrix.shape[0])])
1000 loops, best of 3: 1.46 ms per loop

In [44]: m2 = ss.vstack([row for i in xrange(matrix.shape[0])])

In [45]: numpy.allclose(matrix.todense(), m2.todense())
Out[45]: True

编辑:如果内存是一个问题,而你仍然想要最大速度,那么你可以基于CSR矩阵的快速vstack,自己制作vstack。我建议你先复制_compressed_sparse_stack函数,并使用你的CSR行列表和axis = 0进行调用。然后,你应该能够修改它以接受迭代器而不是列表,这将避免高内存开销。或者,你可以将步骤内联到for循环中。无论哪种方式,都会损失一些速度,但可能会节省大量内存。


谢谢你的帮助!这确实显著提高了性能,但不幸的是我在这样做时遇到了内存问题。使用vstack,甚至40GB的内存也无法成功容纳整个过程。你是否有任何进一步的想法? - Jimmy C
请看我上面的编辑。我还没有尝试过我建议的方法,但我认为它应该可以工作。 - perimosocordiae
谢谢,我会尝试一下! - Jimmy C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接