如何在稠密的numpy矩阵和稀疏的scipy向量之间进行有效的矩阵乘法?

2024-10-03 17:19:58 发布

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

使用@将稠密numpy矩阵与稀疏scipy向量相乘是非常低效的。它似乎根本没有利用向量的稀疏性。在

说我们有

A = np.eye(5000)
x = np.eye(5000)[333]
x = scipy.sparse.coo_matrix(x).T # make it a sparse vector

然后使用@乘法得到:

^{pr2}$

我们自己写一个非常糟糕的稀疏乘法:

def mult_dense_with_sparse(A, x):
  return (A[:,x.nonzero()[0]] @ x.toarray()[x.nonzero()[0]]).T[0]

你瞧:

%timeit mult_dense_with_sparse(A, x)
50.3 µs ± 45.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

它的速度快得多!即使这个实现首先通过再次添加所有零来创建一个密集向量,然后再次移除所有零。。。所以我想知道,如果不是@,如何有效地将稠密numpy矩阵与稀疏scipy向量相乘?这样一个基本的手术肯定是scipy的一部分吗?在

编辑:在其他问题中提供的解决方案没有帮助,因为它与@一样低效:

%timeit scipy.sparse.csr_matrix.dot(A, x)
7.97 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

responaw Hameer Abbasi编辑2:

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   101                                                       @profile
   102                                                       def ratio(self, n):
   103        80         51.0      0.6      0.0                  s = n + 1
   104        80   11401854.0 142523.2     16.1                  self.sfpc = self.scolPCA(s) # sparse first principal component
   106        80     351898.0   4398.7      0.5                  wSums = (self.signals[:,self.sfpc.nonzero()[0]] @ self.sfpc.toarray()[self.sfpc.nonzero()[0]]).T[0]
   108        80   56487433.0 706092.9     79.7                  wSums = self.sfpc.T.dot(self.signals.T)[0]
   110        80    2521189.0  31514.9      3.6                  self.Flag, self.threshold, self.incline, self.deltaVar = self.actFunctOpt(list(wSums))
   111        80        160.0      2.0      0.0                  return  self.deltaVar / (2 + s)

在这里您可以看到我们的“hack”占用了这个函数大约0.5%的时间,而使用dot则花费了这个函数79.7%的时间。在


Tags: selfnumpydefnp矩阵scipy向量matrix
1条回答
网友
1楼 · 发布于 2024-10-03 17:19:58

在您的示例中,Anp.ndarray类型,x是{}类型。在

如果您正在寻找最简单的方法来加速这一过程,那么它是:

import numpy as np
import scipy.sparse as sps
A = np.random.rand(5000, 5000)
v = np.zeros((5000, 1))
v[333] = 1
v_s = sps.csc_matrix(v)
%timeit v_s.T.dot(A).T # 46.6 µs ± 1.11 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit A @ v # 6.75 ms ± 29.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

但是,如果您想了解这个答案背后的机制:目前,操作符@由于bug,不支持NumPy数组的重载。而且,scipy.sparse.coo_matrix甚至不尝试覆盖@,并且矩阵乘法对于scipy.sparse.csr_matrix更快(无论如何,coo_matrix将首先转换为csr_matrix进行乘法)。在

因此,NumPy将稀疏向量转换成NumPy数组,然后执行密集乘法。在

但是,csr_matrix.dot存在并支持使用密集numy数组进行乘法。因此,我们使用属性A @ B = (B.T @ A.T).T,以及{}生成{}来生成上述优化的代码。在

相关问题 更多 >