【问题标题】:Efficiently accumulating a collection of sparse scipy matrices有效地累积稀疏 scipy 矩阵的集合
【发布时间】:2012-06-30 12:57:53
【问题描述】:

我有一个 O(N) NxN scipy.sparse.csr_matrix 的集合,每个稀疏矩阵都有 N 个元素集的顺序。我想将所有这些矩阵加在一起以获得常规的 NxN numpy 数组。 (N 大约为 1000)。矩阵中非零元素的排列使得结果和肯定不是稀疏的(实际上几乎没有零元素)。

目前我只是在做

reduce(lambda x,y: x+y,[m.toarray() for m in my_sparse_matrices])

这可行,但有点慢:当然,在那里进行的大量无意义的零处理绝对是可怕的。

有没有更好的方法? docs 对我来说没有什么明显的。

更新: 根据 user545424 的建议,我尝试了对稀疏矩阵求和的替代方案,并将稀疏矩阵求和到密集矩阵上。下面的代码显示了在可比时间内运行的所有方法(amd64 Debian 上的 Python 2.6.6/四核 i7 上的 Squeeze)

import numpy as np
import numpy.random
import scipy
import scipy.sparse
import time

N=768
S=768
D=3

def mkrandomsparse():
    m=np.zeros((S,S),dtype=np.float32)
    r=np.random.random_integers(0,S-1,D*S)
    c=np.random.random_integers(0,S-1,D*S)
    for e in zip(r,c):
        m[e[0],e[1]]=1.0
    return scipy.sparse.csr_matrix(m)

M=[mkrandomsparse() for i in xrange(N)]

def plus_dense():
    return reduce(lambda x,y: x+y,[m.toarray() for m in M])

def plus_sparse():
    return reduce(lambda x,y: x+y,M).toarray()

def sum_dense():
    return sum([m.toarray() for m in M])

def sum_sparse():
    return sum(M[1:],M[0]).toarray()

def sum_combo():  # Sum the sparse matrices 'onto' a dense matrix?
    return sum(M,np.zeros((S,S),dtype=np.float32))

def benchmark(fn):
    t0=time.time()
    fn()
    t1=time.time()
    print "{0:16}:  {1:.3f}s".format(fn.__name__,t1-t0)

for i in xrange(4):
    benchmark(plus_dense)
    benchmark(plus_sparse)
    benchmark(sum_dense)
    benchmark(sum_sparse)
    benchmark(sum_combo)
    print

然后退出

plus_dense      :  1.368s
plus_sparse     :  1.405s
sum_dense       :  1.368s
sum_sparse      :  1.406s
sum_combo       :  1.039s

虽然您可以通过弄乱 N、S、D 参数使一种方法或另一种方法领先 2 倍左右...但没有什么比您希望看到的数量级改进考虑到零添加的数量,应该可以跳过。

【问题讨论】:

    标签: python optimization numpy scipy sparse-matrix


    【解决方案1】:

    @user545424 已经发布了可能是最快的解决方案。具有相同精神的东西更具可读性和〜相同的速度... nonzero() 具有各种有用的应用程序。

    def sum_sparse(m):
            x = np.zeros(m[0].shape,m[0].dtype)
            for a in m:
                # old lines
                #ri = np.repeat(np.arange(a.shape[0]),np.diff(a.indptr))
                #x[ri,a.indices] += a.data
                # new line
                x[a.nonzero()] += a.data
            return x
    

    【讨论】:

      【解决方案2】:

      如果您的矩阵非常稀疏,我想我已经找到了一种将其加速约 10 倍的方法。

      In [1]: from scipy.sparse import csr_matrix
      
      In [2]: def sum_sparse(m):
         ...:     x = np.zeros(m[0].shape)
         ...:     for a in m:
         ...:         ri = np.repeat(np.arange(a.shape[0]),np.diff(a.indptr))
         ...:         x[ri,a.indices] += a.data
         ...:     return x
         ...: 
      
      In [6]: m = [np.zeros((100,100)) for i in range(1000)]
      
      In [7]: for x in m:
         ...:     x.ravel()[np.random.randint(0,x.size,10)] = 1.0
         ...:     
      
              m = [csr_matrix(x) for x in m]
      
      In [17]: (sum(m[1:],m[0]).todense() == sum_sparse(m)).all()
      Out[17]: True
      
      In [18]: %timeit sum(m[1:],m[0]).todense()
      10 loops, best of 3: 145 ms per loop
      
      In [19]: %timeit sum_sparse(m)
      100 loops, best of 3: 18.5 ms per loop
      

      【讨论】:

      • 啊,太好了!这是我期望的那种高效算法;可惜它似乎没有作为更有效的“内置”提供。很快就会尝试...
      • 是的,有点取决于密度,但对于我感兴趣的数字,x10 的速度提升是典型的。
      • 太棒了。我刚刚在其他一些我有稀疏密集交互的地方应用了同样的模式 - 通常是点产品类型的东西 - 并且每次都获得显着的加速 (x2-x3)。
      【解决方案3】:

      这不是一个完整的答案(我也希望看到更详细的回复),但您可以通过不创建中间结果轻松获得两个或更多改进:

      def sum_dense():
          return sum([m.toarray() for m in M])
      
      def sum_dense2():
          return sum((m.toarray() for m in M))
      

      在我的机器 (YMMV) 上,这是最快的计算。通过将求和放在() 而不是[] 中,我们在求和之前构造了一个生成器而不是整个列表。

      【讨论】:

      • 谢谢;在 python.org/dev/peps/pep-0289 之前没有遇到过“生成器表达式”。在我的测试用例中只让我有一个小的改进(~25%),但肯定会更多地使用这些。
      • @timday 注意到的改进是在比较sum_densesum_dense2,而不是针对其他方法。如果你要在算法之间进行比较,你不应该因为糟糕的实现而惩罚一个特定的选择(在这种情况下你是在不必要地复制数组)。
      【解决方案4】:

      你不能在转换成密集矩阵之前把它们加在一起吗?

      >>> sum(my_sparse_matrices[1:],my_sparse_matrices[0]).todense()
      

      【讨论】:

      • 试过这个(见更新的问题),但这并不是一个巨大的(如果有的话)速度提升,可能是因为随着中间结果变得更加密集,它变得很复杂。我确实希望将稀疏矩阵与(最初为零)密集矩阵相加会更有效,但似乎并非如此。
      猜你喜欢
      • 2020-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-12
      • 2015-03-26
      • 2017-05-17
      • 1970-01-01
      相关资源
      最近更新 更多