【问题标题】:Dot-multiplying large dense matrices of different dtype (float x boolean)点乘不同 dtype 的大型密集矩阵(浮点 x 布尔)
【发布时间】:2018-06-23 01:02:44
【问题描述】:

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

A = 1 x n 矩阵,dtype 浮点数

B = n x n 矩阵,dtype 布尔

我正在为较大的 n 执行此计算,并且内存很快耗尽(大约 n=14000 失败)。 A 和 B 是稠密的。

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

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

以下是基准解决方案的可重复数据:

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

【问题讨论】:

  • 如果您在使用dot 之前将B 转换为浮动,是否有任何区别,即A.dot(B.astype(float) 是否显示相同的行为?
  • 内存和计算非常接近:A.dot(B.astype(float)A.dot(B) 如果我事先将 B 转换为浮点数,计算时间会显着下降。我的目标是针对更大的 n(至少 n=30,000)执行此计算,因此内存改进至关重要,即使在具有更多内存的机器上也是如此。
  • 你不能使用稀疏矩阵吗?
  • 这仍然是关于 64 位的问题。老实说:用后者代替乘法+求和更有效。不确定这是否可以通过单独的索引来利用,但您可以使用 C 扩展来实现。
  • 当数组变得非常大时,在较小的块上进行几次迭代可以节省时间。内存管理时间的节省超过了在迭代中花费的额外时间的补偿。

标签: python numpy matrix type-conversion sparse-matrix


【解决方案1】:

您可以使用np.packbitsnp.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

【讨论】:

  • 出于某种原因,我看到A @ BA @ B.view(np.int8) 的性能相同。我正在使用 python 3.6.2 和 numpy 1.13.1。
  • 我使用的是 3.6.0 和 1.13.3。没有线索 :( 对我来说似乎很一致。
  • @jp_data_analysis 我发现我的机器上的速度又快了 2-3 倍。你能看看吗?
  • 你最新的方法确实快多了,我会接受的。关于泛化的最后一个问题。对于任何 n,您如何概括以下函数? [您使用的一些 numpy 功能超出了我的理解范围,但我打算更好地理解它。]def vecmat_mult(A, B): DC = np.array(np.unravel_index(np.arange(256), 8*(2,))) return np.concatenate([(DC * np.bincount(row, A)).sum(1) for row in np.packbits(B.ravel()).reshape(len(A), -1).T])
猜你喜欢
  • 1970-01-01
  • 2016-06-02
  • 1970-01-01
  • 2019-01-07
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多