向量与csr_矩阵的左乘法

2024-03-28 22:17:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个大小为50*10000的csr格式稀疏矩阵A,和一个大小为50的np.array v,我想为其计算乘积v.dot(A)。我如何有效地做到这一点

当然,运行v.dot(A)不是一个好主意,因为scipy.sparse不支持np操作。不幸的是,据我所知,scipy.sparse没有矩阵与向量左乘的函数

我尝试过以下方法,但所有这些方法似乎都花费了大量时间:

  1. 转座A并使用标准.dot

我转置A,然后使用.dot方法。它将A.Tv相乘作为列向量

```
>>> A = sparse.csr_matrix([[1, 2, 0, 0],
...                        [0, 0, 3, 4]])
>>> v = np.array([1, 1])
>>> A.T.dot(v)
array([1, 2, 3, 4], dtype=int32)
```
  1. 对v进行转置,并使用乘法和求和方法

我正在使用csr_matrix.multiply()方法,它执行逐点乘法。我将对这些行求和

>>> vt = v[np.newaxis].T
>>> A.multiply(vt).sum(axis=0)
matrix([[1, 2, 3, 4]], dtype=int32)
  1. 将v转换为稀疏矩阵并使用.dot方法

我尝试了不同的施工方法,似乎都很昂贵。这是最具可读性的示例(可能不是最有效的示例):

>>> sparse_v = sparse.csr_matrix(v)
>>> sparse_v.dot(A).todense()
matrix([[1, 2, 3, 4]], dtype=int32)

方法1是目前最快的,但是.T方法仍然非常耗时。难道没有更好的方法在稀疏矩阵上执行左乘法吗


Tags: 方法np矩阵scipyarray向量matrixdot
2条回答

@operator也适用于scipy稀疏矩阵,与您的方法进行简短而粗略的性能比较表明@operator的性能与您的方法1类似

In [746]: A = sparse.csr_matrix([[1, 2, 0, 0], 
     ...: ...                        [0, 0, 3, 4]])                                                    
In [747]: A                                                                                            
Out[747]: 
<2x4 sparse matrix of type '<class 'numpy.longlong'>'
    with 4 stored elements in Compressed Sparse Row format>
In [748]: print(A)                                                                                     
  (0, 0)    1
  (0, 1)    2
  (1, 2)    3
  (1, 3)    4
In [749]: v = np.array([1, 1])                                                                         

A.T返回一个新的矩阵,但为csc格式。否则,变化很小。但它不像密集转置那样是一种view

In [750]: A.T                                                                                          
Out[750]: 
<4x2 sparse matrix of type '<class 'numpy.longlong'>'
    with 4 stored elements in Compressed Sparse Column format>
In [755]: timeit A.T                                                                                   
82.9 µs ± 542 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

实际上,与制作原始稀疏矩阵所需的时间相比,该时间相当不错:

In [759]: timeit A = sparse.csr_matrix([[1, 2, 0, 0],   [0, 0, 3, 4]])                                 
349 µs ± 356 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

稀疏优化用于大型非常稀疏矩阵的矩阵乘法。使用密集阵列(只要它们适合内存),大多数其他操作都会更快

稀疏矩阵的默认乘法是矩阵

In [752]: A.T*v                                                                                        
Out[752]: array([1, 2, 3, 4], dtype=int64)
In [753]: A.T.dot(v)                                                                                   
Out[753]: array([1, 2, 3, 4], dtype=int64)
In [754]: A.T@(v)                                                                                      
Out[754]: array([1, 2, 3, 4], dtype=int64)

@__matmul__委托给__mul__。稀疏dot也可以

*dot时间相同,@稍微慢一点。 In[758]:timeit A.T*(v)
每个回路95.6µs±424 ns(7次运行的平均值±标准偏差,每个10000个回路)

从时间中删除转置:

In [760]: %%timeit A1=A.T 
     ...: A1*v                                                                                              
7.77 µs ± 22.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

第二种方法使用稀疏元素乘法:

In [774]: A.multiply(v[:,None]).sum(axis=0)                                                            
Out[774]: matrix([[1, 2, 3, 4]], dtype=int64)

这不如矩阵乘法有效

In [775]: A.multiply(v[:,None])                                                                        
Out[775]: 
<2x4 sparse matrix of type '<class 'numpy.longlong'>'
    with 4 stored elements in COOrdinate format>
In [776]: timeit A.multiply(v[:,None])                                                                 
147 µs ± 1.14 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

结果是另一个稀疏矩阵sum(axis=0)实际上是作为矩阵乘法实现的。这个和的extractor矩阵是sparse.csr_matrix([1,1])。但这只是上一个例子中的sparse_v

In [787]: A.sum(axis=0)                                                                                
Out[787]: matrix([[1, 2, 3, 4]], dtype=int64)
In [788]: timeit A.sum(axis=0)                                                                         
242 µs ± 191 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

应谨慎看待所有这些时间安排A很小,而且不是很稀疏。将此行和与更大的稀疏矩阵进行比较:

In [799]: Ab = sparse.random(1000,1000,.001,'csr')                                                     
In [800]: timeit Ab.sum(axis=0)                                                                        
269 µs ± 12.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [801]: timeit Ab.T*np.ones(1000)                                                                    
118 µs ± 216 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

相关问题 更多 >