繁体   English   中英

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

[英]How to do efficient matrix multiplication between a dense numpy matrix and a sparse scipy vector?

使用@将稀疏的矢量向量与稀疏的numpy矩阵相乘效率极低。 似乎它根本没有利用向量的稀疏性。

说我们有

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

然后使用@产生乘法:

%timeit A @ x
8 ms ± 78.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

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

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向量有效相乘? 这样的基本操作当然是科学的一部分吗?

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

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

在响应程序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%的时间。

在您的示例中, A的类型为np.ndarrayx的类型为scipy.sparse.coo_matrix

如果您正在寻找最简单的答案来加快速度,请按以下步骤进行:

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存在并且支持与密集的NumPy数组相乘。 因此,我们使用属性A @ B = (BT @ AT).T以及csc_matrix.T生成csr_matrix来生成上述优化代码的事实。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM