Numpy - return ndarray 的矩阵乘法,而不是总和
Numpy - Matrix multiplication to return ndarray, not sum
全部,我有一个应用程序在将两个矩阵相乘时需要 returning 一个 numpy ndarray,而不是一个简单的求和;例如:
import numpy as np
x = np.array([[1, 1, 0], [0, 1, 1]])
y = np.array([[1, 0, 0, 1], [1, 0, 1, 0], [0, 0, 0, 0]])
w = x @ y
>>> array([[2, 0, 1, 1],
[1, 0, 1, 0]])
但是,要求是 return 一个 ndarray(在这种情况下..):
array([[[1,1,0], [0,0,0], [0,1,0], [1,0,0]],
[[0,1,0], [0,0,0], [0,1,0], [0,0,0]]])
注意矩阵乘法运算可能会重复;输出将用作下一次矩阵乘法运算的ndarrays的左侧矩阵,这将在第二次矩阵乘法运算后产生更高阶的ndarray等..
有什么方法可以实现吗?我看过通过子类化 np.ndarray as discussed here 来重载 __add__
和 __radd__
,但大多数情况下都会出现维度不兼容错误。
想法?
更新:
解决@Divakar 的回答例如,对于链式操作,添加
z = np.array([[1, 1, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0]])
s1 = x[...,None] * y
s2 = s1[...,None] * z
导致不需要的输出。
我怀疑问题是从 s1 开始的,在上面的例子中 returns s1.shape = (2,3,4)。它应该是 (2,4,3),因为 [2x3][3x4] = [2x4],但我们并不是真的在这里求和,只是 return 一个长度为 3 的数组。
类似地,s2.shape 应该是 (2,3,4,3),[顺便说一句] 它是,但是有不希望的输出(它不是 'wrong',只是不是我们想要的寻找)。
详细来说,s1*z 应该是 [2x4][4x3] = [2x3] 矩阵。矩阵的每个元素本身就是一个 [4x3] 的 ndarray,因为我们在 z 中有 4 行来乘以 s1 中的元素,并且 s1 中的每个元素本身都是 3 个元素长(同样,我们不是在算术上添加元素,而是return ndarrays,扩展维度是操作的 R 矩阵中的行数。
最终,所需的输出将是:
s2 = array([[[[1, 1, 0],
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]],
[[1, 1, 0],
[0, 0, 0],
[0, 0, 0],
[1, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]],
[[[0, 1, 0],
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]],
[[0, 1, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]]])
将它们扩展到 3D
并利用 broadcasting
-
x[:,None] * y.T
或者用 np.einsum
-
np.einsum('ij,jk->ikj',x,y)
按照 和问题的引述:
... matrix multiplication operation may be repeated; the output will
be used as the left-side matrix of ndarrays for the next matrix
multiplication operation, which would yield a higher-order ndarray
after the second matrix multiplication operation, etc..
看来,我们需要沿着这些思路做点什么了-
s1 = x[...,None] * y
s2 = s1[...,None] * z # and so on.
尽管在这种情况下轴的顺序会有所不同,但这似乎是将解决方案扩展到通用数量的传入 2D
数组的最简单方法。
在问题中进行编辑之后,您似乎正在将传入数组从第一个轴开始放置以进行逐元素乘法。所以,如果我做对了,你可以交换轴以获得正确的顺序,就像这样 -
s1c = (x[...,None] * y).swapaxes(1,-1)
s2c = (s1c.swapaxes(1,-1)[...,None] * z).swapaxes(1,-1) # and so on.
如果您只对最终输出感兴趣,请仅在最后阶段交换轴并跳过中间阶段的轴。
全部,我有一个应用程序在将两个矩阵相乘时需要 returning 一个 numpy ndarray,而不是一个简单的求和;例如:
import numpy as np
x = np.array([[1, 1, 0], [0, 1, 1]])
y = np.array([[1, 0, 0, 1], [1, 0, 1, 0], [0, 0, 0, 0]])
w = x @ y
>>> array([[2, 0, 1, 1],
[1, 0, 1, 0]])
但是,要求是 return 一个 ndarray(在这种情况下..):
array([[[1,1,0], [0,0,0], [0,1,0], [1,0,0]],
[[0,1,0], [0,0,0], [0,1,0], [0,0,0]]])
注意矩阵乘法运算可能会重复;输出将用作下一次矩阵乘法运算的ndarrays的左侧矩阵,这将在第二次矩阵乘法运算后产生更高阶的ndarray等..
有什么方法可以实现吗?我看过通过子类化 np.ndarray as discussed here 来重载 __add__
和 __radd__
,但大多数情况下都会出现维度不兼容错误。
想法?
更新:
解决@Divakar 的回答例如,对于链式操作,添加
z = np.array([[1, 1, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0]])
s1 = x[...,None] * y
s2 = s1[...,None] * z
导致不需要的输出。
我怀疑问题是从 s1 开始的,在上面的例子中 returns s1.shape = (2,3,4)。它应该是 (2,4,3),因为 [2x3][3x4] = [2x4],但我们并不是真的在这里求和,只是 return 一个长度为 3 的数组。
类似地,s2.shape 应该是 (2,3,4,3),[顺便说一句] 它是,但是有不希望的输出(它不是 'wrong',只是不是我们想要的寻找)。 详细来说,s1*z 应该是 [2x4][4x3] = [2x3] 矩阵。矩阵的每个元素本身就是一个 [4x3] 的 ndarray,因为我们在 z 中有 4 行来乘以 s1 中的元素,并且 s1 中的每个元素本身都是 3 个元素长(同样,我们不是在算术上添加元素,而是return ndarrays,扩展维度是操作的 R 矩阵中的行数。
最终,所需的输出将是:
s2 = array([[[[1, 1, 0],
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]],
[[1, 1, 0],
[0, 0, 0],
[0, 0, 0],
[1, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]],
[[[0, 1, 0],
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]],
[[0, 1, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]]])
将它们扩展到 3D
并利用 broadcasting
-
x[:,None] * y.T
或者用 np.einsum
-
np.einsum('ij,jk->ikj',x,y)
按照
... matrix multiplication operation may be repeated; the output will be used as the left-side matrix of ndarrays for the next matrix multiplication operation, which would yield a higher-order ndarray after the second matrix multiplication operation, etc..
看来,我们需要沿着这些思路做点什么了-
s1 = x[...,None] * y
s2 = s1[...,None] * z # and so on.
尽管在这种情况下轴的顺序会有所不同,但这似乎是将解决方案扩展到通用数量的传入 2D
数组的最简单方法。
在问题中进行编辑之后,您似乎正在将传入数组从第一个轴开始放置以进行逐元素乘法。所以,如果我做对了,你可以交换轴以获得正确的顺序,就像这样 -
s1c = (x[...,None] * y).swapaxes(1,-1)
s2c = (s1c.swapaxes(1,-1)[...,None] * z).swapaxes(1,-1) # and so on.
如果您只对最终输出感兴趣,请仅在最后阶段交换轴并跳过中间阶段的轴。