我最近转到Python 3.5并注意到new matrix multiplication operator (@)有时与numpy dot运算符的行为不同。例如,对于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)
如何用numpy dot重现相同的结果?还有其他重大差异吗?
答案 0 :(得分:101)
@
运算符调用数组的__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)
来自文档:
matmul
在两个重要方面与dot
不同。
- 不允许使用标量进行乘法。
- 矩阵堆栈一起广播,好像矩阵是元素一样。
最后一点清楚地表明,dot
和matmul
方法在传递3D(或更高维)数组时表现不同。引用文档更多:
matmul
:
如果任一参数是N-D,则N> 2,它被视为驻留在最后两个索引中的一堆矩阵并相应地进行广播。
对于2-D阵列,它相当于矩阵乘法,对于1-D阵列相当于向量的内积(没有复共轭)。 对于N维,它是a的最后一个轴上的和积,b的倒数第二个
答案 1 :(得分:8)
@ajcr的答案解释了dot
和matmul
(由@
符号调用)的不同之处。通过一个简单的例子,可以清楚地看到当两个基质堆栈上运行时两者的行为方式不同。或张量。
为了澄清差异,需要使用4x4数组,并将dot
产品和matmul
产品与2x4x3'堆叠的matricies'或张量。
import numpy as np
fourbyfour = np.array([
[1,2,3,4],
[3,2,1,4],
[5,4,6,7],
[11,12,13,14]
])
twobyfourbythree = 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*4x2x3 dot:\n {}\n'.format(np.dot(fourbyfour,twobyfourbythree)))
print('4x4*4x2x3 matmul:\n {}\n'.format(np.matmul(fourbyfour,twobyfourbythree)))
每项操作的产品如下所示。注意点积是怎样的,
... a的最后一个轴上的和积和b的倒数第二个
以及如何通过将矩阵一起广播来形成矩阵产品。
4x4*4x2x3 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*4x2x3 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]]]
答案 2 :(得分:2)
在数学方面,我认为numpy中的 dot 更有意义
点(a,b)_ {i,j,k,a,b,c} = \ sum_m a_ {i,j,k,m} b_ {a,b,m ,C}
因为当a和b是向量时它给出点积,或者当a和b是矩阵时给出矩阵乘法
对于numpy中的 matmul 操作,它由点结果的部分组成,可以定义为
matmul (a,b)_ {i,j,k,c} = \ sum_m a_ {i,j,k,m} b_ {i,j,m,c} < / p>
所以,你可以看到 matmul(a,b)返回一个小形状的数组, 它具有较小的内存消耗,在应用程序中更有意义。 特别是,与broadcasting结合,您可以获得
matmul (a,b)_ {i,j,k,l} = \ sum_m a_ {i,j,k,m} b_ {j,m,l}
例如。
从以上两个定义中,您可以看到使用这两个操作的要求。假设 a.shape =(s1,s2,s3,s4)和 b.shape =(t1,t2,t3,t4)
要使用点(a,b),您需要
1. **t3=s4**;
要使用 matmul(a,b),您需要
使用以下代码来说服自己。
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
答案 3 :(得分:2)
FYI,@
及其等效的dot
和matmul
都差不多快。 (使用我的项目perfplot创建的图。)
用于复制情节的代码:
import perfplot
import numpy
def setup(n):
A = numpy.random.rand(n, n)
x = numpy.random.rand(n)
return A, x
def at(data):
A, x = data
return A @ x
def numpy_dot(data):
A, x = data
return numpy.dot(A, x)
def numpy_matmul(data):
A, x = data
return numpy.matmul(A, x)
perfplot.show(
setup=setup,
kernels=[at, numpy_dot, numpy_matmul],
n_range=[2 ** k for k in range(12)],
logx=True,
logy=True,
)
答案 4 :(得分:2)
这里是与https://stackoverflow.com/users/5316090/py-j的比较,以显示索引的投影方式
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
答案 5 :(得分:0)
我对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)"
使用DOT
YY = np.dot(X,w)
# no error message
YY
>>>array([ 2.59206877, 1.06842193, 2.18533396, 2.11366346, 0.28505879, …
YY.shape
>>> (200, )