在 Numpy 中编写向量矩阵乘积的紧凑自然方式
Compact and natural way to write matrix product of vectors in Numpy
在科学计算中,我经常想做向量乘法,比如
a x b^T
其中 a 和 b 是行向量并且 b^T 是转置向量。因此,如果 a 和 b 的形状为 [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 Weckesser 和
heltonbiker)
为了完整起见,我以前已经工作的示例:
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
自己编译的代码。
在科学计算中,我经常想做向量乘法,比如
a x b^T
其中 a 和 b 是行向量并且 b^T 是转置向量。因此,如果 a 和 b 的形状为 [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 Weckesser 和 heltonbiker)
为了完整起见,我以前已经工作的示例:
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
自己编译的代码。