我试图理解为什么numpy的dot
函数表现得像:
M = np.ones((9, 9))
V1 = np.ones((9,))
V2 = np.ones((9, 5))
V3 = np.ones((2, 9, 5))
V4 = np.ones((3, 2, 9, 5))
现在np.dot(M, V1)
和np.dot(M, V2)
表现得像
预期。但对于V3
和V4
,结果会令人惊讶
我:
>>> np.dot(M, V3).shape
(9, 2, 5)
>>> np.dot(M, V4).shape
(9, 3, 2, 5)
我分别期待(2, 9, 5)
和(3, 2, 9, 5)
。另一方面,np.matmul
做我所期望的:矩阵乘法是广播
在第二个参数的第一个 N - 2 维度上
结果具有相同的形状:
>>> np.matmul(M, V3).shape
(2, 9, 5)
>>> np.matmul(M, V4).shape
(3, 2, 9, 5)
所以我的问题是:理由是什么?
np.dot
表现得像这样吗?它是否有特殊用途,
或者是应用一般规则的结果?
答案 0 :(得分:6)
对于2-D阵列,它相当于矩阵乘法,对于1-D阵列相当于向量的内积(没有复共轭)。对于N维,它是{strong>总和产品超过{strong>最后一个轴
a
,而倒数第二个b
强>:dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
np.dot(M, V3)
,
(9,9), (2,9, 5) --> (9, 2, 5)
np.dot(M, V4)
,
(9,9), (3, 2,9, 5) --> (9, 3, 2, 5)
透视表示总结的尺寸,因此不会出现在结果中。
相反,np.matmul
将 N - 维数组视为'堆栈'二维矩阵:
行为取决于以下方式的参数。
- 如果两个参数都是2-D,它们就像传统矩阵一样相乘。
- 如果任一参数是N-D,则N> 2,将其视为驻留在最后两个索引中的一堆矩阵并相应地进行广播。
在两种情况下都执行相同的减少,但轴的顺序是不同的。 np.matmul
基本上相当于:
for ii in range(V3.shape[0]):
out1[ii, :, :] = np.dot(M[:, :], V3[ii, :, :])
和
for ii in range(V4.shape[0]):
for jj in range(V4.shape[1]):
out2[ii, jj, :, :] = np.dot(M[:, :], V4[ii, jj, :, :])
答案 1 :(得分:3)
来自numpy.matmul
的文档:
matmul
在两个重要方面与dot
不同。
- 不允许使用标量进行乘法。
- 矩阵堆栈一起广播,好像矩阵是元素一样。
总之,这是您期望的标准矩阵 - 矩阵乘法。
另一方面,numpy.dot
仅相当于二维数组的矩阵 - 矩阵乘法。对于更大的尺寸,......
它是a的最后一个轴上的和积,b是倒数第二个:
dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
[来源:numpy.dot
的文件]
这类似于内部(点)产品。在向量的情况下,numpy.dot
返回点积。数组被认为是向量的集合,并返回它们的点积。
答案 2 :(得分:2)
原因如何:
dot
和matmult
都是2D * 2D矩阵乘法的推广。但根据数学属性,广播规则,......它们有很多可能的选择......
对于dot
,某些尺寸(此处为绿色)专用于第一个数组,
其他人(蓝色)为第二个。
matmul
需要有关广播规则的堆栈的对齐。
Numpy诞生于图像分析环境中,dot
可以通过out=dot(image(s),transformation(s))
方式轻松管理某些任务。 (参见早期版本 numpy book中的点文档,第92页)。
举例说明:
from pylab import *
image=imread('stackoverflow.png')
identity=eye(3)
NB=ones((3,3))/3
swap_rg=identity[[1,0,2]]
randoms=[rand(3,3) for _ in range(6)]
transformations=[identity,NB,swap_rg]+randoms
out=dot(image,transformations)
for k in range(9):
subplot(3,3,k+1)
imshow (out[...,k,:])
现代matmul
可以与旧dot
做同样的事情,但必须考虑矩阵的堆栈。 (matmul(image,transformations[:,None])
此处)。
毫无疑问,在其他情况下它会更好。
答案 3 :(得分:1)
等效的In [92]: np.einsum('ij,kjm->kim',M,V3).shape
Out[92]: (2, 9, 5)
In [93]: np.einsum('ij,lkjm->lkim',M,V4).shape
Out[93]: (3, 2, 9, 5)
表达式为:
dot
以这种方式表达,YAML = require('yamljs');
config = YAML.load('myfile.yml');
等价物,' ij,lkjm-> ilkm'看起来和' matmul'一样自然。相当于,' ij,lkjm-> lkim'。