我正在努力实现以下等式:
X =(Y.T * Y + Y.T * C * Y) ^ -1
Y是(n×f)矩阵,C是(n×n)对角线; n约为300k,f在100到200之间变化。作为优化过程的一部分,这个等式将使用近1亿次,因此必须非常快速地处理。
Y是随机初始化的,C是一个非常稀疏的矩阵,对角线上的300k中只有少数数字与0不同。由于Numpy的对角函数创建了密集矩阵,我创建了C作为稀疏csr矩阵。但是当试图解决方程式的第一部分时:
r = dot(C, Y)
由于内存限制,计算机崩溃。我决定然后尝试将Y转换为csr_matrix并进行相同的操作:
r = dot(C, Ysparse)
这种方法 1.38 ms 。但是这个解决方案有点“棘手”,因为我使用稀疏矩阵来存储密集的矩阵,我想知道它的效率有多高。
所以我的问题是,是否有某种方法可以将稀疏的C和密集的Y相乘,而不必将Y变为稀疏并提高性能?如果不知何故C可以表示为对角线密集而不消耗大量内存,这可能会导致非常有效的性能,但我不知道这是否可行。
感谢您的帮助!
答案 0 :(得分:23)
当计算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)
显然,上述内容并不是您感兴趣的结果。相反,您要做的是使用scipy的sparse.csr_matrix.dot函数进行计算:
r = sparse.csr_matrix.dot(C, Y)
或更紧凑
r = C.dot(Y)
答案 1 :(得分:7)
尝试:
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)
我的时间:
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
答案 2 :(得分:2)
首先,您确定需要在问题中执行完整矩阵求逆吗?大多数情况下,只需要计算x = A ^ -1 y,这是一个更容易解决的问题。
如果确实如此,我会考虑计算逆矩阵的近似值而不是全矩阵求逆。由于矩阵求逆真的成本高昂。例如,参见Lanczos algorithm以获得逆矩阵的有效近似。近似可以稀疏地存储为奖励。此外,它只需要矩阵向量运算,因此您甚至不必将完整矩阵存储为逆矩阵。
作为替代方案,使用pyoperator,您还可以使用.todense方法使用有效的矩阵向量运算来计算矩阵以进行逆运算。对角矩阵有一个特殊的稀疏容器。
对于Lanczos算法的实现,您可以查看pyoperators(免责声明:我是此软件的共同作者之一)。
答案 3 :(得分:0)
问这个问题的时候不知道有没有可能;但如今,广播是您的朋友。一个 n*n 对角矩阵只需要是一个矩阵乘积中使用的对角元素数组:
>>> n, f = 5, 3
>>> Y = np.random.randint(0, 10, (n, f))
>>> C = np.random.randint(0, 10, (n,))
>>> Y.shape
(5, 3)
>>> C.shape
(5,)
>>> np.all(Y.T @ np.diag(C) @ Y == Y.T*C @ Y)
True
请注意 Y.T*C @ Y
是非关联的:
>>> Y.T*(C @ Y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (3,5) (3,)
但是 Y.T @ (C[:, np.newaxis]*Y)
会产生预期的结果:
>>> np.all(Y.T*C @ Y == Y.T@(C[:, np.newaxis]*Y))
True