numpy dot() 和 Python 3.5+ 矩阵乘法之间的区别 @
- 2025-03-05 09:17:00
- admin 原创
- 5
问题描述:
我最近换到了 Python 3.5,发现新的矩阵乘法运算符 (@)有时与numpy 点运算符的行为不同。例如,对于 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 重现相同的结果?还有其他显著差异吗?
解决方案 1:
运算@
符调用数组的__matmul__
方法,而不是dot
。此方法也以函数的形式存在于 API 中np.matmul
。
>>> a = np.random.rand(8,13,13)
>>> b = np.random.rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)
来自文档:
matmul
在两个重要方面有所不同dot
。
不允许与标量相乘。
矩阵堆栈被一起广播,就好像矩阵是元素一样。
最后一点明确指出,当传递 3D(或更高维度)数组时,dot
和matmul
方法的行为会有所不同。更多引用文档内容:
为了matmul
:
如果任一参数是 ND,N > 2,则它将被视为驻留在最后两个索引中的矩阵堆栈并进行相应的广播。
为了np.dot
:
对于二维数组,它相当于矩阵乘法;对于一维数组,它相当于向量的内积(无复共轭)。对于 N 维数组,它是 a 的最后一个轴与 b 的倒数第二个轴的和积
解决方案 2:
仅供参考,@
它的 numpy 等效项dot
都同样快。(使用perfplotmatmul
创建的图表,这是我的一个项目。)
重现情节的代码:
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)],
)
解决方案 3:
@ajcr 的回答解释了dot
和matmul
(由符号调用@
)的不同之处。通过查看一个简单的示例,可以清楚地看到两者在对“矩阵堆栈”或张量进行操作时的行为有何不同。
为了阐明差异,采用 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:
{}
'.format(np.dot(fourbyfour,threebyfourbytwo)))
print('4x4*3x4x2 matmul:
{}
'.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]]]
解决方案 4:
在数学中,我认为numpy 中的点更有意义
点(a,b)_{i,j,k,a,b,c} =
因为当 a 和 b 为向量时,它会给出点积;当 a 和 b 为矩阵时,它会给出矩阵乘法
numpy 中的matmul运算由各部分点运算结果组成,可以定义为
matmul (a,b)_{i,j,k,c} =
因此,你可以看到matmul(a,b)返回一个形状较小的数组,它消耗的内存较少,在应用中更有意义。特别是,结合广播,你可以得到
matmul (a,b)_{i,j,k,l} =
例如。
从以上两个定义可以看出使用这两个操作的要求。假设a.shape=(s1,s2,s3,s4)和b.shape=(t1,t2,t3,t4)
要使用点(a,b),您需要
t3=s4;
要使用matmul(a,b)您需要
t3=s4
t2=s2,或者 t2 和 s2 其中一个为 1
t1=s1,或者 t1 和 s1 其中一个为 1
使用下面一段代码来确认一下。
import numpy as np
for it in range(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 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
解决方案 5:
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
解决方案 6:
我对 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, )
- 2025年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)