Python 点乘不同数据类型的大型密集矩阵(浮点x布尔)

Python 点乘不同数据类型的大型密集矩阵(浮点x布尔),python,numpy,matrix,type-conversion,sparse-matrix,Python,Numpy,Matrix,Type Conversion,Sparse Matrix,我将两个矩阵相乘,A.dot(B),其中: A=1 x n矩阵,数据类型浮点 B=n x n矩阵,数据类型为布尔型 我正在对大n执行此计算,内存很快就会用完(大约n=14000失败)。A和B是稠密的 原因似乎是numpy在执行矩阵乘法之前将B转换为dtype float,因此产生了巨大的内存开销。事实上,%timeit表明它将B转换为float比执行乘法花费更多的时间 有办法解决这个问题吗?这里的重点是减少内存尖峰/浮点转换,同时仍然允许通用矩阵功能(矩阵加法/乘法) 以下是基准测试解决方案的可

我将两个矩阵相乘,
A.dot(B)
,其中:

A=1 x n矩阵,数据类型浮点

B=n x n矩阵,数据类型为布尔型

我正在对大n执行此计算,内存很快就会用完(大约n=14000失败)。A和B是稠密的

原因似乎是numpy在执行矩阵乘法之前将B转换为dtype float,因此产生了巨大的内存开销。事实上,%timeit表明它将B转换为float比执行乘法花费更多的时间

有办法解决这个问题吗?这里的重点是减少内存尖峰/浮点转换,同时仍然允许通用矩阵功能(矩阵加法/乘法)

以下是基准测试解决方案的可复制数据:

np.random.seed(999)
n = 30000
A = np.random.random(n)
B = np.where(np.random.random((n, n)) > 0.5, True, False)

使用
np.packbits
然后在行上使用
np.bincount
来同时计算8个标量积的块,可以节省将布尔数组压缩到位字段的空间和时间

import numpy as np

def setup_data(M, N):
    return {'B': np.random.randint(0, 2, (M, N), dtype=bool),
            'A': np.random.random((M,))}

def f_vecmat_mult(A, B, decode=np.array(np.unravel_index(np.arange(256), 8*(2,)))):
    M, N = B.shape
    out = [(decode * np.bincount(row, A, minlength=256)).sum(axis=1) for row in np.packbits(B, axis=1).T]
    if N & 7:
        out[-1] = out[-1][:N & 7]
    return np.concatenate(out)

def f_direct(A, B):
    return A @ B

import types
from timeit import timeit

for M, N in [(99, 80), (999, 777), (9999, 7777), (30000, 30000)]:
    data = setup_data(M, N)
    ref = f_vecmat_mult(**data)
    print(f'M, N = {M}, {N}')
    for name, func in list(globals().items()):
        if not name.startswith('f_') or not isinstance(func, types.FunctionType):
            continue
        try:
            assert np.allclose(ref, func(**data))
            print("{:16s}{:16.8f} ms".format(name[2:], timeit(
                'f(**data)', globals={'f':func, 'data':data}, number=100)*10))
        except:
            print("{:16s} apparently failed".format(name[2:]))
样本输出:

M, N = 99, 80
vecmat_mult           0.12248290 ms
direct                0.03647798 ms
M, N = 999, 777
vecmat_mult           1.67854790 ms
direct                5.68286091 ms
M, N = 9999, 7777
vecmat_mult          68.74523309 ms
direct              571.34140913 ms
M, N = 30000, 30000
vecmat_mult        1345.18991556 ms
direct           apparently failed

如果您在使用
dot
之前将
B
转换为float本身,即
A.dot(B.astype(float)
是否显示相同的行为?内存和计算非常接近:
A.dot(B.astype(float)
A.dot(B)
如果我事先将B转换为float,计算时间会显著减少。我的目标是对更大的n(至少n=30000)执行此计算,因此,即使在内存较多的机器上,内存的改善也是至关重要的。难道你不能使用稀疏矩阵吗?这仍然留下了64位的问题。老实说,用后者代替乘法+求和更有效。不确定是否可以单独通过索引来利用这一点,但你可以使用C扩展。当日变得非常大,在较小的块上进行几次迭代可以节省时间。节省的内存管理时间足以弥补迭代中花费的额外时间。出于某种原因,我看到
a@B
a@B.view(np.int8)具有相同的性能
。我使用的是python 3.6.2和numpy 1.13.1。我使用的是3.6.0和1.13.3。没有线索:(在我这方面似乎很一致。@jp_data_analysis我在我的机器上发现了另一个速度快2-3倍的东西。你能看一下吗?你最新的方法确实快得多,我会接受的。最后一个关于泛化的问题。你如何将下面的函数泛化为任何n?[code>def vecmat_mult(A,B):DC=np.array(np.unlavel_index(np.arange(256),8*(2),)返回np.concatenate([(DC*np.bincount(row,A)).sum(1)表示np.packbits(B.ravel())。重塑(len(A),-1.T)]/code>