带3D阵列的Numpy点积

时间:2020-06-25 18:22:22

标签: numpy

我有两个数组:

  • 形状为data的{​​{1}},其中尺寸为(2466, 2498, 9)
  • (asset, date, returns)的形状correlation_matrix(对角线为0)

我想得到等于预期收益的点积,即每个(2466, 2466)的{​​{1}}乘以returns。它的形状应与asset相同。

我尝试过:

correlation_matrix

但这只是挂起了我的PC(经过10分钟并开始计数)。

我也尝试过:

data

但是我对data.transpose([1, 2, 0]) @ correlation_matrix 不太熟悉,并且它也挂起了。

我在做什么错了?

2 个答案:

答案 0 :(得分:2)

使用您的.transpose((1, 2, 0))数据,正确的格式为:

"ijs,sk"  # -> ijk

由于有了张量AB,我们可以这样写:

C_{ijk} = Σ_s A_{ijs} * B_{sk}

如果您想避免事先转置数据,则可以对索引进行置换:

"sij,sk"  # -> ijk

要验证:

p, q, r = 2466, 2498, 9

a = np.random.randint(255, size=(p, q, r))
b = np.random.randint(255, size=(p, p))

c1 = a.transpose((1, 2, 0)) @ b
c2 = np.einsum("sij,sk", a, b)

>>> np.all(c1 == c2)
True

计算(p, q, r)data所需的乘法的数量为p * np.prod(c.shape) == p * (q * r * p) == p**2 * q * r。在您的情况下,就是136_716_549_192乘法。您还需要大约相同数量的加法,因此使我们的操作量接近2700亿次。如果您想提高速度,可以考虑通过cupy使用GPU进行计算。

def with_np():
    p, q, r = 2466, 2498, 9
    a = np.random.randint(255, size=(p, q, r))
    b = np.random.randint(255, size=(p, p))
    c1 = a.transpose((1, 2, 0)) @ b
    c2 = np.einsum("sij,sk", a, b)

def with_cp():
    p, q, r = 2466, 2498, 9
    a = cp.random.randint(255, size=(p, q, r))
    b = cp.random.randint(255, size=(p, p))
    c1 = a.transpose((1, 2, 0)) @ b
    c2 = cp.einsum("sij,sk", a, b)

>>> timeit(with_np, number=1)
513.066

>>> timeit(with_cp, number=1)
0.197

那是2600的加速,包括内存分配,初始化和CPU / GPU复制时间在内的! (更现实的基准将提供更大的加速。)

答案 1 :(得分:1)

有多种方法可以制作此产品:

# as you already suggested:
data.transpose([1, 2, 0]) @ correlation_matrix

# using einsum
np.einsum('ijk,il', data, correlation_matrix)

# using tensordot to explicitly specify the axes to sum over
np.tensordot(data, correlation_matrix, axes=(0,0))

所有这些都应给出相同的结果。对于我来说,一些小型矩阵的时间大致相同。因此,您的问题在于大量数据,而不是效率低下的实现。

A=np.arange(100*120*9).reshape((100, 120, 9))
B=np.arange(100**2).reshape((100,100))

timeit('A.transpose([1,2,0])@B', globals=globals(), number=100)
# 0.747475513999234
timeit("np.einsum('ijk,il', A, B)", globals=globals(), number=100)
# 0.4993825999990804
timeit('np.tensordot(A, B, axes=(0,0))', globals=globals(), number=100)
# 0.5872082839996438