5

我试图理解为什么 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什么?它是服务于某个特定目的,还是应用某些一般规则的结果?

4

4 回答 4

7

文档中np.dot

对于二维数组,它相当于矩阵乘法,对于一维数组,它相当于向量的内积(没有复共轭)。对于 N 维,它是的最后一个轴和 的倒数第二个轴的和积ab

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.matmulN维数组视为 2D 矩阵的“堆栈”:

行为取决于以下方式的参数。

  • 如果两个参数都是二维的,它们会像传统矩阵一样相乘。
  • 如果任一参数为 ND,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, :, :])
于 2015-11-29T12:13:58.753 回答
4

从以下文档numpy.matmul

matmuldot在两个重要方面有所不同。

  • 不允许乘以标量。
  • 矩阵堆栈一起广播,就好像矩阵是元素一样。

总之,这是您期望的标准矩阵-矩阵乘法。

另一方面,numpy.dot只等价于二维数组的矩阵-矩阵乘法。对于更大的尺寸,...

它是 a 的最后一个轴和 b 的倒数第二个轴的和积:

dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])

[来源:文档numpy.dot]

这类似于内(点)积。如果是向量,numpy.dot则返回点积。数组被认为是向量的集合,并返回它们的点积。

于 2015-11-29T12:05:27.417 回答
2

对于为什么:

dot 并且matmult都是 2D*2D 矩阵乘法的推广。但它们是很多可能的选择,根据数学性质、广播规则、...

选择是针对dot并且matmul非常不同的: 在此处输入图像描述

对于dot,某些维度(此处为绿色)专用于第一个数组 ,其他维度(蓝色)用于第二个。

matmul需要关于广播规则的堆栈对齐。

Numpy 诞生于图像分析上下文中,可以通过某种方式dot轻松管理一些任务。out=dot(image(s),transformation(s))(参见numpy book早期版本中的点文档,p92)。

作为说明:

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])这里)。

毫无疑问,它在其他情况下会更好。

于 2016-03-16T23:38:01.440 回答
1

等价的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'。

于 2015-11-29T22:16:16.000 回答