scipy 稀疏矩阵与非稀疏numpy矩阵之间的存储器高效点积

pftdvrlh  于 2022-12-23  发布在  其他
关注(0)|答案(2)|浏览(198)

我经历过以前被问过的类似问题(例如1(https://stackoverflow.com/questions/41942115/numpy-efficient-large-dot-products) 2(https://stackoverflow.com/questions/20983882/efficient-dot-products-of-large-memory-mapped-arrays))。但是,没有一个与我的问题完全相关。
我试图计算两个大矩阵之间的点积,我有一些内存限制,我必须满足。
我有一个numpy稀疏矩阵,形状为(10000,600000)。例如,

from scipy import sparse as sps
x = sps.random(m=10000, n=600000, density=0.1).toarray()

第二个numpy矩阵的大小为(600000,256),它仅由(-1,1)组成。

import numpy as np
y = np.random.choice([-1,1], size=(600000, 256))

我需要xy的点积在尽可能低的内存要求。速度不是主要关注。
以下是我迄今为止尝试过的方法:

Scipy稀疏格式:

当然,我将numpy稀疏矩阵转换为scipy csr_matrix。但是,由于内存问题,任务仍然被杀死。没有错误,我只是在终端上被杀死。

from scipy import sparse as sps
sparse_x = sps.csr_matrix(x, copy=False)
z = sparse_x.dot(y)
# killed

减小dtype精度+Scipy稀疏格式:

from scipy import sparse as sps

x = x.astype("float16", copy=False)
y = y.astype("int8", copy=False)

sparse_x = sps.csr_matrix(x, copy=False)
z = sparse_x.dot(y)
# Increases the memory requirement for some reason and dies

名词单数

不确定它是否对稀疏矩阵有帮助。在这个answer中发现了一些有趣的东西。但是,以下内容也没有帮助:

z = np.einsum('ij,jk->ik', x, y)
# similar memory requirement as the scipy sparse dot

建议?
如果你有任何改善的建议,请告诉我。此外,我正在考虑以下方向:
1.如果我能摆脱点积本身就太好了。我的第二个矩阵(即y)是随机生成的,它只有[-1,1]。我希望有什么方法可以利用它的特性。
1.可将点积分成几个小的点积,然后聚合。

yyhrrdl8

yyhrrdl81#

M@x的最深层Python代码(其中M是稀疏csr矩阵,x是密集数组)是:

In [28]: M._mul_multivector??
Signature: M._mul_multivector(other)
Docstring: <no docstring>
Source:   
    def _mul_multivector(self, other):
        M, N = self.shape
        n_vecs = other.shape[1]  # number of column vectors

        result = np.zeros((M, n_vecs),
                          dtype=upcast_char(self.dtype.char, other.dtype.char))

        # csr_matvecs or csc_matvecs
        fn = getattr(_sparsetools, self.format + '_matvecs')
        fn(M, N, n_vecs, self.indptr, self.indices, self.data,
           other.ravel(), result.ravel())

        return result

这里的fn是经过编译的代码,它获取csr矩阵的属性数组,密集数组作为一维数组,预分配的result数组也作为扁平的view
我想知道在创建result时会发生内存错误。因为我怀疑fn使用该内存作为其工作空间,并且没有尝试使用任何更多的内存。
我知道sparse@sparse矩阵乘法在两个编译步骤中完成,第一步确定结果的维数和nnz,第二步实际执行计算,两步之间的python代码创建了result数组,与这里所做的差不多,并且@的内存错误发生在result分配中。

v9tzhpje

v9tzhpje2#

如果你创建的数组没有任何内存问题(我为准备好的例子大小设置了内存问题),为什么不使用迭代呢?点积等价循环可能是一个解决方案,可以使用numba加速:

@nb.njit  # ("float64[:, ::1](float64[:, ::1], int32[:, ::1])")
def dot(a, b):

    dot_ = np.zeros((a.shape[0], b.shape[1]))
    for i in range(a.shape[1]):
        for j in range(a.shape[0]):
            for k in range(b.shape[1]):
                dot_[j, k] += a[j, i] * b[i, k]
    return dot_

对于注解中提到的Warren这样的示例,结果将很容易放入内存中。

相关问题