取高维numpy数组的点积

Taking dot products of high dimensional numpy arrays

我正在尝试计算三个 numpy 数组之间的点积。但是,我正在努力解决这个问题。

问题如下:

我有两个 (4,) 形状的 numpy 数组,分别为 ab,以及一个形状为 (4, 4, 3),c 的 numpy 数组。

import numpy as np

a = np.array([0, 1, 2, 3])
b = np.array([[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
          [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],
          [[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]],
          [[4, 4, 4], [4, 4, 4], [4, 4, 4], [4, 4, 4]]])
c = np.array([4, 5, 6, 7])

我想以我的结果是一个三元组的方式计算点积。也就是说,先用 ba,然后用 c 点,必要时进行转置。换句话说,我想计算 abc 之间的点积,就好像 c 的形状是 (4, 4),但我想要 3 -结果为元组。

我试过:

重塑 ac,然后计算点积:

a = np.reshape(a, (4, 1))
c = np.reshape(c, (4, 1))

tmp = np.dot(a.T, b) # now has shape (1, 4, 3)
result = np.dot(tmp, c) 

理想情况下,我现在应该有:

print(result.shape)
>> (1, 1, 3) 

但我收到错误

ValueError: shapes (1,4,3) and (4,1) not aligned: 3 (dim 2) != 4 (dim 0)

我也尝试过使用 numpy 的 tensordot 函数,但没有成功。

不是自动的,但可以完成工作:

np.einsum('i,ijk,j->k',a,b,c)
# array([440, 440, 440])

计算形状 (3,) 的 d 使得 d_k = sum_{ij} a_i b_{ijk} c_j.

基本的dot(A,B)规则是:A的最后一个轴与B的第二个到最后一个

In [965]: a.shape
Out[965]: (4,)
In [966]: b.shape
Out[966]: (4, 4, 3)

a(和 c)是 1d。它的 (4,) 可以与 b 的第二个 (4) 点在一起:

In [967]: np.dot(a,b).shape
Out[967]: (4, 3)

在输出中使用 c 产生一个 (3,) 数组

In [968]: np.dot(c, np.dot(a,b))
Out[968]: array([360, 360, 360])

这个组合可能更清楚等同于 einsum:

In [971]: np.einsum('i,jik,j->k',a,b,c)
Out[971]: array([360, 360, 360])

但是如果我们想让a作用在b的第一个轴上呢?使用 einsum 这很容易做到:

In [972]: np.einsum('i,ijk,j->k',a,b,c)
Out[972]: array([440, 440, 440])

要对 dot 做同样的事情,我们可以切换 ac:

In [973]: np.dot(a, np.dot(c,b))
Out[973]: array([440, 440, 440])

或转置 b 的轴:

In [974]: np.dot(c, np.dot(a,b.transpose(1,0,2)))
Out[974]: array([440, 440, 440])

如果ac的长度不同,这道换位题会更清楚。例如A (2,) 和 (4,) 与 (2,4,3) 或 (4,2,3)。


tmp = np.dot(a.T, b) # now has shape (1, 4, 3)

你有一个 (1,4a) 点缀着 (4,4a,3)。结果是 (1,4,3)。我添加了 a 来标识轴何时组合。

要应用 (4,1) c,我们必须进行相同的转置:

In [977]: np.dot(c[:,None].T, np.dot(a[:,None].T, b))
Out[977]: array([[[360, 360, 360]]])
In [978]: _.shape
Out[978]: (1, 1, 3)

np.dot(c[None,:], np.dot(a[None,:], b)) 在没有转置的情况下也会做同样的事情。


I was hoping numpy would automagically distribute over the last axis. That is, that the dot product would run over the two first axes, if that makes sense.

鉴于我在开头引用的 dot 规则,这没有意义。但是如果我们转置 b 所以 (3) 轴是第一个,它可以 'carry that along',使用最后一个和第二个到最后一个。

In [986]: b.transpose(2,0,1).shape
Out[986]: (3, 4, 4)
In [987]: np.dot(a, b.transpose(2,0,1)).shape
Out[987]: (3, 4)
In [988]: np.dot(np.dot(a, b.transpose(2,0,1)),c)
Out[988]: array([440, 440, 440])

(4a).(3, 4a, 4c) -> (3, 4c)
(3, 4c). (4c) -> 3

您正在将 (1,4,3) 矩阵乘以 (4,1) 矩阵,所以这是不可能的,因为您在 b 中有 3 页的 (1,4) 矩阵。如果您想将矩阵 b 的每一页乘以 c,只需分别将每一页相乘即可。

a = np.array([0, 1, 2, 3])
b = np.array([[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
      [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],
      [[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]],
      [[4, 4, 4], [4, 4, 4], [4, 4, 4], [4, 4, 4]]])
c = np.array([4, 5, 6, 7])

a = np.reshape(a, (4, 1))
c = np.reshape(c, (4, 1))

tmp = np.dot(a.T, b) # now has shape (1, 4, 3)
result = np.dot(tmp[:,:,0], c)
for i in range(1,3):
    result = np.dstack((result, np.dot(tmp[:,:,i], c)))
print np.shape(result)

所以你得到大小为 (1,1,3) 的结果