我有一个非常稀疏的结构矩阵。我的矩阵每列只有一个非零条目。但它的巨大(10k * 1M)并以下面的形式给出(例如uisng随机值)
rows = np.random.randint(0, 10000, 1000000)
values = np.random.randint(0,10,1000000)
其中rows为我们提供了每列中非零条目的行号。我希望用S快速矩阵乘法,我现在正在跟随 - 我将这个形式转换为稀疏矩阵(S)并做S.dot(X)与矩阵X(可以是稀疏或密集)的乘法。
S=scipy.sparse.csr_matrix( (values, (rows, scipy.arange(1000000))), shape = (10000,1000000))
对于大小为1M * 2500且nnz(X)= 8M的X,创建S需要178ms,应用它需要255 ms。所以我的问题是这是什么是做SX的最佳方式(其中X可能是稀疏的或密集的),因为我的S如上所述。因为创建S本身非常耗时,所以我想到了一些特别的东西。我确实尝试使用循环创建一些东西,但它甚至没有关闭 简单的循环过程看起来像这样
SX = np.zeros((rows.size,X.shape[1]))
for i in range(X.shape[0]):
SX[rows[i],:]+=values[i]*X[i,:]
return SX
我们可以提高效率吗?
非常感谢任何建议。感谢
答案 0 :(得分:3)
方法#1
鉴于第一个输入中每列只有一个条目,我们可以使用np.bincount
,rows
,values
和X
来使用S
,从而避免创建稀疏矩阵def sparse_matrix_mult(rows, values, X):
nrows = rows.max()+1
ncols = X.shape[1]
nelem = nrows * ncols
ids = rows + nrows*np.arange(ncols)[:,None]
sums = np.bincount(ids.ravel(), (X.T*values).ravel(), minlength=nelem)
out = sums.reshape(ncols,-1).T
return out
-
In [746]: import numpy as np
...: from scipy.sparse import csr_matrix
...: import scipy as sp
...:
In [747]: np.random.seed(1234)
...: m,n = 3,4
...: rows = np.random.randint(0, m, n)
...: values = np.random.randint(2,10,n)
...: X = np.random.randint(2, 10, (n,5))
...:
In [748]: S = csr_matrix( (values, (rows, sp.arange(n))), shape = (m,n))
In [749]: S.dot(X)
Out[749]:
array([[42, 27, 45, 78, 87],
[24, 18, 18, 12, 24],
[18, 6, 8, 16, 10]])
In [750]: sparse_matrix_mult(rows, values, X)
Out[750]:
array([[ 42., 27., 45., 78., 87.],
[ 24., 18., 18., 12., 24.],
[ 18., 6., 8., 16., 10.]])
示例运行 -
np.add.reduceat
方法#2
使用np.bincount
替换def sparse_matrix_mult_v2(rows, values, X):
nrows = rows.max()+1
ncols = X.shape[1]
scaled_ar = X*values[:,None]
sidx = rows.argsort()
rows_s = rows[sidx]
cut_idx = np.concatenate(([0],np.flatnonzero(rows_s[1:] != rows_s[:-1])+1))
sums = np.add.reduceat(scaled_ar[sidx],cut_idx,axis=0)
out = np.empty((nrows, ncols),dtype=sums.dtype)
row_idx = rows_s[cut_idx]
out[row_idx] = sums
return out
-
In [149]: m,n = 1000, 100000
...: rows = np.random.randint(0, m, n)
...: values = np.random.randint(2,10,n)
...: X = np.random.randint(2, 10, (n,2500))
...:
In [150]: S = csr_matrix( (values, (rows, sp.arange(n))), shape = (m,n))
In [151]: %timeit csr_matrix( (values, (rows, sp.arange(n))), shape = (m,n))
100 loops, best of 3: 16.1 ms per loop
In [152]: %timeit S.dot(X)
1 loop, best of 3: 193 ms per loop
In [153]: %timeit sparse_matrix_mult(rows, values, X)
1 loop, best of 3: 4.4 s per loop
In [154]: %timeit sparse_matrix_mult_v2(rows, values, X)
1 loop, best of 3: 2.81 s per loop
运行时测试
我无法按照问题中提到的尺寸运行它,因为那些对我来说太大了。因此,在减少的数据集上,这是我得到的 -
numpy.dot
因此,所提议的方法似乎并没有超出X
的性能,但它们应该在内存效率方面做得很好。
适用于稀疏X
对于稀疏from scipy.sparse import find
def sparse_matrix_mult_sparseX(rows, values, Xs): # Xs is sparse
nrows = rows.max()+1
ncols = Xs.shape[1]
nelem = nrows * ncols
scaled_vals = Xs.multiply(values[:,None])
r,c,v = find(scaled_vals)
ids = rows[r] + c*nrows
sums = np.bincount(ids, v, minlength=nelem)
out = sums.reshape(ncols,-1).T
return out
,我们需要进行一些修改,如下面列出的修改方法所列 -
cat
答案 1 :(得分:1)
受到这篇文章Fastest way to sum over rows of sparse matrix的启发,我发现最好的方法是编写循环并将内容移植到numba。这是
`
@njit
def sparse_mul(SX,row,col,data,values,row_map):
N = len(data)
for idx in range(N):
SX[row_map[row[idx]],col[idx]]+=data[idx]*values[row[idx]]
return SX
X_coo=X.tocoo()
s=row_map.max()+1
SX = np.zeros((s,X.shape[1]))
sparse_mul(SX,X_coo.row,X_coo.col,X_coo.data,values,row_map)`
这里的row_map是问题中的行。在X大小(1M * 1K),1%稀疏度和s = 10K时,它的性能是从row_map形成稀疏矩阵并执行S.dot(A)的两倍。
答案 2 :(得分:0)
我记得,Knuth TAOP谈到将稀疏矩阵表示为(对于你的app)非零值的链表。也许是这样的?然后按每个维度遍历链表而不是整个数组。