我正在处理一个非常大的稀疏矩阵乘法(matmul)问题。举个例子,我们说:
A是二进制(75 x 200,000)矩阵。它很稀疏,所以我使用csc进行存储。我需要进行以下matmul操作:
B = A.transpose()* A
输出将是一个大小为200Kx200K的稀疏对称矩阵。
不幸的是,B将以方式大到存储在我的笔记本电脑上的RAM(或“核心”)中。另一方面,我很幸运,因为B有一些属性可以解决这个问题。
由于B将沿着对角线和稀疏对称,我可以使用三角矩阵(上/下)来存储matmul操作的结果,稀疏矩阵存储格式可以进一步减小尺寸。
我的问题是......可以提前告知numpy或scipy,输出存储要求会是什么样子,以便我可以使用numpy选择存储解决方案并避免“矩阵太大”运行时计算几分钟(小时)后出错?
换句话说,通过使用近似计数算法分析两个输入矩阵的内容,可以近似地估算矩阵的存储要求吗?
如果没有,我正在寻找一个强力解决方案。以下网站链接涉及地图/缩小,核外存储或matmul细分解决方案(strassens算法)的内容:
一对Map / Reduce问题细分解决方案
核心外(PyTables)存储解决方案
matmul细分解决方案:
提前感谢任何建议,评论或指导!
答案 0 :(得分:2)
由于您使用的是转置矩阵的乘积,因此[m, n]
处的值基本上将是原始矩阵中m
列和n
列的点积。
我将使用以下矩阵作为玩具示例
a = np.array([[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]])
>>> np.dot(a.T, a)
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 2]])
形状为(3, 12)
,有7个非零项。它的转置产品当然是形状(12, 12)
,有16个非零项,其中6个在对角线上,所以它只需要存储11个元素。
您可以通过以下两种方式之一了解输出矩阵的大小:
CSR FORMAT
如果您的原始矩阵有C
个非零列,则您的新矩阵最多会有C**2
个非零条目,其中C
位于对角线中,并且可以保证不要为零,其余的条目只需要保留一半,因此最多只有(C**2 + C) / 2
个非零元素。当然,其中许多也将为零,所以这可能是一个高估。
如果您的矩阵以csr
格式存储,则相应indices
对象的scipy
属性具有一个数组,其列索引为所有非零元素,因此您可以轻松地将上述估算计算为:
>>> a_csr = scipy.sparse.csr_matrix(a)
>>> a_csr.indices
array([ 2, 11, 1, 7, 10, 4, 11])
>>> np.unique(a_csr.indices).shape[0]
6
因此有6列具有非零条目,因此估计最多36个非零条目,超过真实16条。
CSC FORMAT
如果我们有行索引而不是非零元素的列索引,我们实际上可以做更好的估计。对于两列的点积非零,它们必须在同一行中具有非零元素。如果给定行中存在R
个非零元素,则它们会向产品提供R**2
个非零元素。当你对所有行求和时,你必须多次计算一些元素,所以这也是一个上限。
矩阵的非零元素的行索引位于稀疏csc矩阵的indices
属性中,因此可以按如下方式计算此估计值:
>>> a_csc = scipy.sparse.csc_matrix(a)
>>> a_csc.indices
array([1, 0, 2, 1, 1, 0, 2])
>>> rows, where = np.unique(a_csc.indices, return_inverse=True)
>>> where = np.bincount(where)
>>> rows
array([0, 1, 2])
>>> where
array([2, 3, 2])
>>> np.sum(where**2)
17
这是真正的16接近!而这个估计实际上与以下相同并不是巧合:
>>> np.sum(np.dot(a.T,a),axis=None)
17
在任何情况下,以下代码都应该让您看到估算非常好:
def estimate(a) :
a_csc = scipy.sparse.csc_matrix(a)
_, where = np.unique(a_csc.indices, return_inverse=True)
where = np.bincount(where)
return np.sum(where**2)
def test(shape=(10,1000), count=100) :
a = np.zeros(np.prod(shape), dtype=int)
a[np.random.randint(np.prod(shape), size=count)] = 1
print 'a non-zero = {0}'.format(np.sum(a))
a = a.reshape(shape)
print 'a.T * a non-zero = {0}'.format(np.flatnonzero(np.dot(a.T,
a)).shape[0])
print 'csc estimate = {0}'.format(estimate(a))
>>> test(count=100)
a non-zero = 100
a.T * a non-zero = 1065
csc estimate = 1072
>>> test(count=200)
a non-zero = 199
a.T * a non-zero = 4056
csc estimate = 4079
>>> test(count=50)
a non-zero = 50
a.T * a non-zero = 293
csc estimate = 294