Numpy dot ()和 Python 3.5 + 矩阵乘法@的区别

我最近转移到 Python 3.5,注意到 新的矩阵乘法操作员(@)有时与 小麻点操作符的行为不同。例如,对于3d 数组:

import numpy as np


a = np.random.rand(8,13,13)
b = np.random.rand(8,13,13)
c = a @ b  # Python 3.5+
d = np.dot(a, b)

@操作符返回一个形状数组:

c.shape
(8, 13, 13)

np.dot()函数返回:

d.shape
(8, 13, 8, 13)

我怎样才能重现同样的结果与麻点? 有没有任何其他重大的差异?

169803 次浏览

@操作符调用数组的 __matmul__方法,而不是 dot。这个方法也作为函数 np.matmul出现在 API 中。

>>> a = np.random.rand(8,13,13)
>>> b = np.random.rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)

根据文件:

matmuldot有两个重要的区别。

  • 不允许使用标量进行乘法。
  • 矩阵的堆栈被广播在一起,就好像矩阵是元素一样。

最后一点清楚地表明,当传递3D (或更高维度)数组时,dotmatmul方法的行为是不同的。引用文件中的更多内容:

对于 matmul:

如果任何一个参数为 N-D,N > 2,则将其视为位于最后两个索引中的矩阵堆栈,并相应地进行广播。

对于 np.dot:

对于二维阵列,它等同于矩阵乘法,对于一维阵列,它等同于向量的内积(不需要复杂的共轭)。对于 N 维,它是 a 的最后一个轴和 b 的倒数第二个轴的和乘积

@ ajcr 给出的答案解释了 dotmatmul(由 @符号调用)的区别。通过观察一个简单的例子,我们可以清楚地看到,当操作“矩阵堆叠”或张量时,两者的行为是如何不同的。

为了澄清差异采取4x4阵列和返回的 dot积和 matmul积与3x4x2“叠加的矩阵”或张量。

import numpy as np
fourbyfour = np.array([
[1,2,3,4],
[3,2,1,4],
[5,4,6,7],
[11,12,13,14]
])




threebyfourbytwo = np.array([
[[2,3],[11,9],[32,21],[28,17]],
[[2,3],[1,9],[3,21],[28,7]],
[[2,3],[1,9],[3,21],[28,7]],
])


print('4x4*3x4x2 dot:\n {}\n'.format(np.dot(fourbyfour,threebyfourbytwo)))
print('4x4*3x4x2 matmul:\n {}\n'.format(np.matmul(fourbyfour,threebyfourbytwo)))

每个操作的乘积出现在下面。请注意点乘是什么样的,

... a 的最后一个轴和 b 的倒数第二个轴的和乘积

以及矩阵乘积是如何通过将矩阵一起广播而形成的。

4x4*3x4x2 dot:
[[[232 152]
[125 112]
[125 112]]


[[172 116]
[123  76]
[123  76]]


[[442 296]
[228 226]
[228 226]]


[[962 652]
[465 512]
[465 512]]]


4x4*3x4x2 matmul:
[[[232 152]
[172 116]
[442 296]
[962 652]]


[[125 112]
[123  76]
[228 226]
[465 512]]


[[125 112]
[123  76]
[228 226]
[465 512]]]

在数学方面,我认为麻木的 更有意义

(a,b) _ { i,j,k,a,b,c } = formula

因为它给出了 a 和 b 是向量时的点积,或者 a 和 b 是矩阵时的矩阵乘法


对于在 numpy 中的 Matmul运算,它由 结果的一部分组成,可以定义为

Matmul (a,b) _ { i,j,k,c } = formula


可以看到 Matmul (a,b)返回一个小形状的数组, 它具有较小的内存消耗,并且在应用程序中更有意义。 特别是,结合 广播,您可以得到

Matmul (a,b) _ { i,j,k,l } = formula

比如说。


从上面的两个定义中,您可以看到使用这两个操作的要求

  • 要使用 点(a,b),您需要
  1. T3 = s4 ;
  • 要使用 Matmul (a,b),您需要
  1. T3 = s4
  2. T2 = s2 ,或者 t2和 s2之一是1
  3. T1 = s1 ,或者 t1和 s1之一是1

用下面的代码说服你自己。

import numpy as np
for it in xrange(10000):
a = np.random.rand(5,6,2,4)
b = np.random.rand(6,4,3)
c = np.matmul(a,b)
d = np.dot(a,b)
#print 'c shape: ', c.shape,'d shape:', d.shape
    

for i in range(5):
for j in range(6):
for k in range(2):
for l in range(3):
if not c[i,j,k,l] == d[i,j,k,j,l]:
print it,i,j,k,l,c[i,j,k,l]==d[i,j,k,j,l]  # you will not see them

仅供参考,@及其麻木的等价物 dotmatmul都同样快。(用我的一个项目 完美情节创建的情节)

enter image description here

重现情节代码:

import perfplot
import numpy




def setup(n):
A = numpy.random.rand(n, n)
x = numpy.random.rand(n)
return A, x




def at(A, x):
return A @ x




def numpy_dot(A, x):
return numpy.dot(A, x)




def numpy_matmul(A, x):
return numpy.matmul(A, x)




perfplot.show(
setup=setup,
kernels=[at, numpy_dot, numpy_matmul],
n_range=[2 ** k for k in range(15)],
)

我使用 MATMUL 和 DOT 的经验

在尝试使用 MATMUL 时,我不断得到“ ValueError: 传递值的形状是(200,1) ,索引意味着(200,3)”。我想要一个快速的解决方案,并发现 DOT 提供相同的功能。我没有得到任何错误使用 DOT。我得到了正确的答案

用 MATMUL

X.shape
>>>(200, 3)


type(X)


>>>pandas.core.frame.DataFrame


w


>>>array([0.37454012, 0.95071431, 0.73199394])


YY = np.matmul(X,w)


>>>  ValueError: Shape of passed values is (200, 1), indices imply (200, 3)"

交通部

YY = np.dot(X,w)
# no error message
YY
>>>array([ 2.59206877,  1.06842193,  2.18533396,  2.11366346,  0.28505879, …


YY.shape


>>> (200, )

下面是与 np.einsum的比较,以显示指数是如何预测的

np.allclose(np.einsum('ijk,ijk->ijk', a,b), a*b)        # True
np.allclose(np.einsum('ijk,ikl->ijl', a,b), a@b)        # True
np.allclose(np.einsum('ijk,lkm->ijlm',a,b), a.dot(b))   # True