为什么numpy.dot会以这种方式运行?

时间:2015-11-29 11:39:44

标签: python numpy linear-algebra matrix-multiplication numpy-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)表现得像 预期。但对于V3V4,结果会令人惊讶 我:

>>> 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 个答案:

答案 0 :(得分:6)

来自the docs for np.dot

  

对于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)

原因如何:

dotmatmult都是2D * 2D矩阵乘法的推广。但根据数学属性,广播规则,......它们有很多可能的选择......

dotmatmul的选择非常不同: enter image description here

对于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,:])

so

现代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'。