有效地乘以Numpy/Scipy稀疏矩阵和密集矩阵

Wil*_*uks 16 python performance numpy scipy sparse-matrix

我正在努力实现以下等式:

X =(Y.T * Y + Y.T * C * Y) ^ -1
Run Code Online (Sandbox Code Playgroud)

Y是(nxf)矩阵,C是(nxn)对角线; n约为300k,f在100到200之间变化.作为优化过程的一部分,这个等式将使用近1亿次,因此必须非常快速地处理.

Y是随机初始化的,C是一个非常稀疏的矩阵,对角线上300k中只有少数数字与0不同.由于Numpy的对角线函数创建了密集矩阵,我创建了C作为稀疏csr矩阵.但是当试图解决方程式的第一部分时:

r = dot(C, Y)
Run Code Online (Sandbox Code Playgroud)

由于内存限制,计算机崩溃.我决定然后尝试将Y转换为csr_matrix并进行相同的操作:

r = dot(C, Ysparse)
Run Code Online (Sandbox Code Playgroud)

这种方法耗时1.38毫秒.但是这个解决方案有点"棘手",因为我使用稀疏矩阵来存储密集的矩阵,我想知道它真的有多高效.

所以我的问题是,是否有一些方法可以将稀疏的C和密集的Y相乘,而不必将Y变为稀疏并提高性能?如果不知何故C可以表示为对角线密集而不消耗大量内存,这可能会导致非常有效的性能,但我不知道这是否可行.

我感谢您的帮助!

M.H*_*.H. 24

当计算r = dot(C,Y)时,点积运行到内存问题的原因是因为numpy的点函数不具有处理稀疏矩阵的本机支持.正在发生的是numpy认为稀疏矩阵C是一个python对象,而不是一个numpy数组.如果您进行小规模检查,您可以直接看到问题:

>>> from numpy import dot, array
>>> from scipy import sparse
>>> Y = array([[1,2],[3,4]])
>>> C = sparse.csr_matrix(array([[1,0], [0,2]]))
>>> dot(C,Y)
array([[  (0, 0)    1
  (1, 1)    2,   (0, 0) 2
  (1, 1)    4],
  [  (0, 0) 3
  (1, 1)    6,   (0, 0) 4
  (1, 1)    8]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

显然,上述内容并不是您感兴趣的结果.相反,您要做的是使用scipy的sparse.csr_matrix.dot函数进行计算:

r = sparse.csr_matrix.dot(C, Y)
Run Code Online (Sandbox Code Playgroud)

或者更紧凑

r = C.dot(Y)
Run Code Online (Sandbox Code Playgroud)

  • 由于我当天不得不处理同样的问题并且没有看到解释为什么点(稀疏,密集)函数没有返回您期望的结果,因此我主要是发帖.如果他们自己遇到这个问题,只希望给予他人帮助. (2认同)

pv.*_*pv. 8

尝试:

import numpy as np
from scipy import sparse

f = 100
n = 300000

Y = np.random.rand(n, f)
Cdiag = np.random.rand(n) # diagonal of C
Cdiag[np.random.rand(n) < 0.99] = 0

# Compute Y.T * C * Y, skipping zero elements
mask = np.flatnonzero(Cdiag)
Cskip = Cdiag[mask]

def ytcy_fast(Y):
    Yskip = Y[mask,:]
    CY = Cskip[:,None] * Yskip  # broadcasting
    return Yskip.T.dot(CY)

%timeit ytcy_fast(Y)

# For comparison: all-sparse matrices
C_sparse = sparse.spdiags([Cdiag], [0], n, n)
Y_sparse = sparse.csr_matrix(Y)
%timeit Y_sparse.T.dot(C_sparse * Y_sparse)
Run Code Online (Sandbox Code Playgroud)

我的时间:

In [59]: %timeit ytcy_fast(Y)
100 loops, best of 3: 16.1 ms per loop

In [18]: %timeit Y_sparse.T.dot(C_sparse * Y_sparse)
1 loops, best of 3: 282 ms per loop
Run Code Online (Sandbox Code Playgroud)