任意形状的NumPy数组的点积

3

给定两个任意形状的numpy.ndarray对象AB,我想计算一个numpy.ndarrayC,使得对于所有的i,C[i] == np.dot(A[i], B[i])。如何做到这一点?

例1:A.shape==(2,3,4)B.shape==(2,4,5),那么我们应该有C.shape==(2,3,5)

例2:A.shape==(2,3,4)B.shape==(2,4),那么我们应该有C.shape==(2,3)


3
因此,A和B的形状并不是完全任意的。它们需要在每个“i”处具有兼容的形状。换句话说,必须满足A.shape[0] == B.shape[0]为True。 - piRSquared
einsum('ijk,ik...->ij...',A,B) 处理您的两种情况。它只限制 A 为三维,而 B 可以是2、3等。 - hpaulj
2个回答

4
这里有一个通用的解决方案,可以涵盖各种情况/任意形状,使用一些“重塑”和np.einsumeinsum 在这里很有帮助,因为我们需要沿着输入数组的第一个轴对齐并减少最后轴上的值。实现看起来会像这样 -
def dotprod_axis0(A,B):
    N,nA,nB = A.shape[0], A.shape[-1], B.shape[1]
    Ar = A.reshape(N,-1,nA)
    Br = B.reshape(N,nB,-1)
    return np.squeeze(np.einsum('ijk,ikl->ijl',Ar,Br))

案例

I. A:2D,B:2D

In [119]: # Inputs
     ...: A = np.random.randint(0,9,(3,4))
     ...: B = np.random.randint(0,9,(3,4))
     ...: 

In [120]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
33
86
48

In [121]: dotprod_axis0(A,B)
Out[121]: array([33, 86, 48])

II. A : 3D,B : 3D

In [122]: # Inputs
     ...: A = np.random.randint(0,9,(2,3,4))
     ...: B = np.random.randint(0,9,(2,4,5))
     ...: 

In [123]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
[[ 74  70  53 118  43]
 [ 47  43  29  95  30]
 [ 41  37  26  23  15]]
[[ 50  86  33  35  82]
 [ 78 126  40 124 140]
 [ 67  88  35  47  83]]

In [124]: dotprod_axis0(A,B)
Out[124]: 
array([[[ 74,  70,  53, 118,  43],
        [ 47,  43,  29,  95,  30],
        [ 41,  37,  26,  23,  15]],

       [[ 50,  86,  33,  35,  82],
        [ 78, 126,  40, 124, 140],
        [ 67,  88,  35,  47,  83]]])

III. A : 3D,B : 2D

In [125]: # Inputs
     ...: A = np.random.randint(0,9,(2,3,4))
     ...: B = np.random.randint(0,9,(2,4))
     ...: 

In [126]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
[ 87 105  53]
[152 135 120]

In [127]: dotprod_axis0(A,B)
Out[127]: 
array([[ 87, 105,  53],
       [152, 135, 120]])

IV. A:2D,B:3D

In [128]: # Inputs
     ...: A = np.random.randint(0,9,(2,4))
     ...: B = np.random.randint(0,9,(2,4,5))
     ...: 

In [129]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
[76 93 31 75 16]
[ 33  98  49 117 111]

In [130]: dotprod_axis0(A,B)
Out[130]: 
array([[ 76,  93,  31,  75,  16],
       [ 33,  98,  49, 117, 111]])

通常情况下,即使len(x.shape)>2,甚至len(x.shape)!=len(y.shape),np.dot(x,y)也可以被很好地定义。在实践中,我可能只需要根据A和B的形状进行if/else判断,针对我目前关心的几种情况,但希望不必这样做。 - dshin
@dshin 是的!一些重塑可能会有所帮助。 - Divakar
@dshin请查看已编辑的代码以涵盖所有情况! - Divakar

0
假设你想要普通的矩阵乘法用于dot(而不是矩阵-向量或者dot在更高维度上做的奇怪的东西),那么足够新的NumPy版本(1.10+)可以让你这样做。
C = numpy.matmul(A, B)

而且足够新的 Python 版本(3.5+)可以让您将其编写为:

C = A @ B

假设您的NumPy也足够新。


我在原帖中添加了一些例子,其中一个包含矩阵向量。 - dshin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接