在 Numpy 中编写向量矩阵乘积的紧凑自然方式

Compact and natural way to write matrix product of vectors in Numpy

在科学计算中,我经常想做向量乘法,比如

a x b^T

其中 ab 是行向量并且 b^T 是转置向量。因此,如果 ab 的形状为 [n, 1] 和 [m, 1],则结果矩阵的形状为 [n, m]

在 numpy 中有没有一个好的直接的方法来写这个乘法?

示例:

a = np.array([1,2,3])
b = np.array([4,5,6,7])

手动添加轴有效:

a[:,np.newaxis] @ b[np.newaxis,:]

并给出正确的结果:

[[ 4  5  6  7]
 [ 8 10 12 14]
 [12 15 18 21]]

爱因斯坦符号是另一种方式,但仍然有些奇怪。

np.einsum('a,b->ab', a,b)

我希望工作,但没有工作,如下:

a @ b.T

还有其他方法吗?

评论中提出了多种解决方案,总结如下:

  • np.outer(a,b),它基本上将这个 multiplicen 重新表述为一个集合问题(感谢 Brenlla
  • a[:,np.newaxis]*b(感谢Divakar
  • a.reshape((-1,1)) @ b.reshape((-1,1)).T 或者
    a.reshape((-1,1)) @ b.reshape((1,-1))。有点长,但是 显示这些 numpy 矩阵运算实际上需要矩阵作为 输入,不仅是向量(感谢 Warren Weckesserheltonbiker)

为了完整起见,我以前已经工作的示例:

  • a[:,np.newaxis] @ b[np.newaxis,:]
  • np.einsum('a,b->ab', a,b)

备注:为了进一步减少字符数,可以使用None代替np.newaxis

在 MATLAB 中矩阵乘法是常态,使用 *。逐元素乘法使用 .* 运算符。矩阵也至少是二维的。

numpy中,元素乘法使用*。矩阵乘法是使用 np.dot(或其方法)完成的,最近使用 @ 运算符(np.matmul)。 numpy 添加了广播,这使逐元素乘法更具表现力。

使用您的 2 个示例数组,形状为 (3,) 和 (4,),制作 (3,4) outer product https://en.wikipedia.org/wiki/Outer_product 的选项包括:

np.outer(a,b)     

np.einsum('i,j->ij, a, b)  # matching einstein index notation

a[:,None] * b       # the most idiomatic numpy expression

这最后的作品是因为广播。 a[:, None],就像 a.reshape(-1,1) 将 (3,) 数组变成 (3,1)。 b[None, :] 将 (4,) 变成 (1,4)。但是广播可以自动(并且明确地)执行此升级。

(3,1) * (4,) => (3,1) * (1,4) => (3,4)

广播不适用于 np.dot。所以我们需要

a[:, None].dot(b[None, :])   #  (3,1) dot with (1,4)

dot 的关键是 a 的最后一个 dim 与 b 的倒数第二个配对。 (np.dot 也适用于 2 个匹配的一维数组,执行传统的向量点积)。

@ (matmul) 引入了一个类似于 dot 的运算符,至少在 2d with 2d 的情况下是这样。对于更高维的数组,它们的工作方式不同。

a[:,None].dot(b[None,:])
np.dot(a[:,None], b[None,:])
a[:,None] @ b[None,:]
a[:,None] @ b[:,None].T

reshape 等价物都创建了所需的 (3,4) 数组。

np.tensordot 可以处理其他维度组合,但它通过重塑和转置输入来工作,因此最终它可以将它们传递给 dot。然后它将结果转换回所需的形状。

快速测试表明 np.dot 版本往往是最快的 - 因为它们将操作委托给像库这样的快速 BLAS。对于其他版本,委托有点间接,或者他们使用numpy's自己编译的代码。