Python 为什么numpy.dot会有这种行为?
我试图理解为什么numpy的Python 为什么numpy.dot会有这种行为?,python,numpy,linear-algebra,matrix-multiplication,array-broadcasting,Python,Numpy,Linear Algebra,Matrix Multiplication,Array Broadcasting,我试图理解为什么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来说,结果令人惊讶 我: 我分别期望(2,9,5)和(3,2,9,5)。另一方面,np.matmul 我所期望的是:矩阵乘法是广播的
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
来说,结果令人惊讶
我:
我分别期望(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
它的行为是什么?它有什么特殊用途吗,
还是应用了一些一般规则的结果?来自以下文件:
matmul
在两个重要方面与dot
不同
- 不允许用标量乘法
- 矩阵堆栈一起广播,就像矩阵是元素一样
dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
[来源:文件资料]
这类似于内部(点)积。对于向量,numpy.dot
返回点积。数组被认为是向量的集合,并返回向量的点积 来自:
对于二维数组,它等价于矩阵乘法,对于一维数组,它等价于向量的内积(无复共轭)。对于N维,它是a
的最后一个轴和b的倒数第二个轴上的和积:
dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
对于np.dot(M,V3)
贯穿线表示已求和的维度,因此不存在于结果中
相反,将N维数组视为二维矩阵的“堆栈”:
行为取决于以下方式的参数
- 如果两个参数都是二维的,则它们将像常规矩阵一样相乘
- 如果任一参数为N-D,N>2,将其视为驻留在最后两个索引中的矩阵堆栈,并相应地进行广播
在这两种情况下执行相同的减少,但轴的顺序不同<代码>np.matmul本质上等同于:
for ii in range(V3.shape[0]):
out1[ii, :, :] = np.dot(M[:, :], V3[ii, :, :])
及
等效的einsum
表达式为:
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
等价物“ij,lkjm->ilkm”看起来就像“matmul”等价物“ij,lkjm->lkim”一样自然 关于原因:
dot
和matmult
都是2D*2D矩阵乘法的推广。但根据数学性质、广播规则等,它们是许多可能的选择
dot
和matmul
的选择非常不同:
对于点
,某些维度(此处为绿色)专用于第一个数组,
其他(蓝色)为第二个
matmul
需要一个关于广播规则的堆栈对齐
Numpy诞生于图像分析环境中,dot
可以通过out=dot(图像、变换)的方式轻松管理一些任务。(参见早期版本的第92页中的dot文档)
举个例子:
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])
here)
毫无疑问,它在其他环境中更好。这实际上在dot和MATMUL的numpy文档中得到了解释。还有一个例子:a1=np.random.rand(3,4);a2=np.rand.rand(10,4,1);A=(a1@a2);B=np.数组([a1@a2[i,:,0]表示范围(10)]内的i);断言A.shape==B.shape;断言np.allclose(A.squence(),B)
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, :, :])
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)
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,:])