我正在使用scipy的csc
稀疏矩阵,目前代码中的主要瓶颈是类似于以下内容的
for i in range(multiply_cols.shape[0]):
F = F - factor*values[i]*mat.getcol(multiply_cols[i])
我正在使用的矩阵非常大,通常大于10**6x10**6
并且我不想将它们转换为密集矩阵。事实上,我有一个限制,总是有csc
格式的矩阵。我的尝试表明,转换为coo_matrix
或lil_matrix
也无法获得回报。
以下是我使用csc
,csr
和coo
进行的基本尝试:
n=1000
sA = csc_matrix(np.random.rand(n,n))
F = np.random.rand(n,1)
multiply_cols = np.unique(np.random.randint(0,int(0.6*n),size=n))
values = np.random.rand(multiply_cols.shape[0])
def foo1(mat,F,values,multiply_cols):
factor = 0.75
for i in range(multiply_cols.shape[0]):
F = F - factor*values[i]*mat.getcol(multiply_cols[i])
def foo2(mat,F,values,multiply_cols):
factor = 0.75
mat = mat.tocsr()
for i in range(multiply_cols.shape[0]):
F = F - factor*values[i]*mat.getcol(multiply_cols[i])
def foo3(mat,F,values,multiply_cols):
factor = 0.75
mat = mat.tocoo()
for i in range(multiply_cols.shape[0]):
F = F - factor*values[i]*mat.getcol(multiply_cols[i])
def foo4(mat,F,values,multiply_cols):
factor = 0.75
mat = mat.tolil()
for i in range(multiply_cols.shape[0]):
F = F - factor*values[i]*mat.getcol(multiply_cols[i])
然后我得到他们的时间:
In [41]: %timeit foo1(sA,F,values,multiply_cols)
10 loops, best of 3: 133 ms per loop
In [42]: %timeit foo2(sA,F,values,multiply_cols)
1 loop, best of 3: 999 ms per loop
In [43]: %timeit foo3(sA,F,values,multiply_cols)
1 loop, best of 3: 6.38 s per loop
In [44]: %timeit foo4(sA,F,values,multiply_cols)
1 loop, best of 3: 45.1 s per loop
所以肯定coo_matrix
和lil_matrix
不是一个好选择。有没有人知道更快的方法。检索underlyng indptr
,indices
和data
是否有自定义cython
解决方案是一个不错的选择?
答案 0 :(得分:1)
那么你可以使用一种矢量化方法,使用稀疏矩阵的切片列与values
的矩阵乘法,就像这样 -
F -= (mat[:,multiply_cols]*values*factor)[:,None]
<强>基准强>
似乎foo1
是问题中列出的最快的一个。所以,让我们来对付那个提议的方法。
功能定义 -
def foo1(mat,F,values,multiply_cols):
factor = 0.75
outF = F.copy()
for i in range(multiply_cols.shape[0]):
outF -= factor*values[i]*mat.getcol(multiply_cols[i])
return outF
def foo_vectorized(mat,F,values,multiply_cols):
factor = 0.75
return F - (mat[:,multiply_cols]*values*factor)[:,None]
对稀疏性较大的集合进行计时和验证 -
In [242]: # Setup inputs
...: n = 3000
...: mat = csc_matrix(np.random.randint(0,3,(n,n))) #Sparseness with 0s
...: F = np.random.rand(n,1)
...: multiply_cols = np.unique(np.random.randint(0,int(0.6*n),size=n))
...: values = np.random.rand(multiply_cols.shape[0])
...:
In [243]: out1 = foo1(mat,F,values,multiply_cols)
In [244]: out2 = foo_vectorized(mat,F,values,multiply_cols)
In [245]: np.allclose(out1, out2)
Out[245]: True
In [246]: %timeit foo1(mat,F,values,multiply_cols)
1 loops, best of 3: 641 ms per loop
In [247]: %timeit foo_vectorized(mat,F,values,multiply_cols)
10 loops, best of 3: 40.3 ms per loop
In [248]: 641/40.3
Out[248]: 15.905707196029779
我们有 15x+
加速!
答案 1 :(得分:1)
我找到了
Sparse matrix slicing using list of int
sparse
矩阵的列(或行)索引本质上是一个矩阵乘法任务 - 构造一个具有1和0的正确混合的稀疏矩阵,并乘以。行(和列)总和也用乘法完成。
这个功能实现了这个想法。 M
是1列稀疏矩阵,values
位中有multiply_cols
:
def wghtsum(sA, values, multiply_cols):
cols = np.zeros_like(multiply_cols)
M=sparse.csc_matrix((values,(multiply_cols,cols)),shape=(sA.shape[1],1))
return (sA*M).A
测试:
In [794]: F1=wghtsum(sA,values,multiply_cols)
In [800]: F2=(sA[:,multiply_cols]*values)[:,None] # Divaker's
In [802]: np.allclose(F1,F2)
Out[802]: True
与@Divakar's
解决方案相比节省了适度的时间:
In [803]: timeit F2=(sA[:,multiply_cols]*values)[:,None]
100 loops, best of 3: 18.3 ms per loop
In [804]: timeit F1=wghtsum(sA,values,multiply_cols)
100 loops, best of 3: 6.57 ms per loop
=======
创建的 sA
是密集的 - 它是密集随机数组的稀疏表达。 sparse.rand
可用于创建具有定义稀疏度的稀疏随机矩阵。
在测试foo1
getcol
时,我遇到了问题:
In [818]: sA.getcol(multiply_cols[0])
...
TypeError: an integer is required
In [819]: sA.getcol(multiply_cols[0].item())
Out[819]:
<1000x1 sparse matrix of type '<class 'numpy.float64'>'
with 1000 stored elements in Compressed Sparse Column format>
In [822]: sA[:,multiply_cols[0]]
Out[822]:
<1000x1 sparse matrix of type '<class 'numpy.float64'>'
with 1000 stored elements in Compressed Sparse Column format>
我怀疑这是由scipy
版本差异引起的。
In [821]: scipy.__version__
Out[821]: '0.17.0'
这个问题确实在0.18中消失了;但我找不到相关问题/ pullrequest。