我知道transpose
上的ndarray
旨在等同于matlab的permute
功能但是我有一个特定的用例并不能简单地工作。在matlab中我有以下内容:
C = @bsxfun(@times, permute(A,[4,2,5,1,3]), permute(B, [1,6,2,7,3,4,5])
其中A是形状NxNxM的3D张量,B是形状NxNxMxPxP的5D张量。上述功能旨在对循环kronecker产品进行矢量化。我假设Matlab为A和B添加了2个单独维度,这就是为什么它能够重新排列它们的原因。我希望将此代码移植到Python ,但我认为它没有能力添加这些额外的维度。。我发现this成功添加了额外的尺寸,但是广播并没有使用相同的matlab bsxfun
。我尝试了明显的翻译(是的,我正在为这些ndarray
和函数使用numpy):
A = A[...,None,None]
B = B[...,None,None]
C = transpose(A,[3,1,4,0,2])*transpose(B,[0,5,1,6,2,3,4])
,我收到以下错误:
return transpose(axes)
ValueError: axes don't match array
我的第一个猜测是在A和B上做 reshape
以添加这些单例尺寸?
我现在收到以下错误:
mults = transpose(rho_in,[3,1,4,0,2])*transpose(proj,[0,5,1,6,2,3,4])
ValueError: operands could not be broadcast together with shapes (1,9,1,9,8) (9,1,9,1,8,40,40)
编辑:修改了我的问题,不仅仅是添加单例维度,而是更多关于在python中正确地广播这个matlab乘法。
答案 0 :(得分:6)
MATLAB和numpy之间的巨大差异在于前者使用列主要格式作为其数组,而后者使用行主要格式。推论是隐式单例维度的处理方式不同。
具体来说,MATLAB明确忽略尾随单例维度:rand(3,3,1,1,1,1,1)
实际上是3x3
矩阵。除此之外,如果前导维度匹配,您可以使用bsxfun
对两个数组进行操作:NxNxM
隐式NxNxMx1x1
与{{1}兼容}。
Numpy allows implicit singletons up front。您需要以尾随尺寸匹配的方式NxNxMxPxP
数组,例如形状为permute
的形状(40,40,9,1,9,1,8)
,结果应该是形状的(1,9,1,9,8)
。
虚拟例子:
(40,40,9,9,9,9,8)
请注意,您尝试执行的操作可能可以使用numpy.einsum
完成。我建议仔细看看。我的意思的一个例子:根据您的问题,我收集了您要执行的操作:获取元素>>> import numpy as np
>>> (np.random.rand(40,40,9,1,9,1,8)+np.random.rand(1,9,1,9,8)).shape
(40, 40, 9, 9, 9, 9, 8)
和A[1:N,1:N,1:M]
并构建新数组B[1:N,1:N,1:M,1:P,1:P]
,以便
C[1:N,1:N,1:N,1:N,1:M,1:P,1:P]
(您的具体索引顺序可能会有所不同)。如果这是正确的,您确实可以使用C[i1,i2,i3,i4,i5,i6,i7] = A[i2,i4,i5]*B[i1,i3,i5,i6,i7]
:
numpy.einsum()
但是应该注意两件事。首先,上面的操作将非常耗费内存,这应该适用于矢量化的情况(你通常以牺牲内存需求为代价来赢得CPU时间)。其次,您应该认真考虑在移植代码时重新安排数据模型。广播在两种语言中的工作方式不同的原因是与列主要/行主要区别错综复杂地联系在一起。这也意味着在MATLAB中你应该首先使用前导索引,因为>>> a = np.random.rand(3,3,2)
>>> b = np.random.rand(3,3,2,4,4)
>>> np.einsum('ijk,lmkno->limjkno',a,b).shape
(3, 3, 3, 3, 2, 4, 4)
对应于一个连续的内存块,而A(:,i2,i3)
则没有。相反,numpy A(i1,i2,:)
是连续的,而A[i1,i2,:]
则不是。
这些考虑建议您应该设置数据的后勤,使得向量化操作最好与MATLAB中的前导索引和numpy中的尾随索引一起使用。您仍然可以使用A[:,i2,i3]
来执行操作本身,但是与MATLAB相比,您的维度应该是不同的(可能是反向的)顺序,至少如果我们假设两个版本的代码都使用最佳设置。
答案 1 :(得分:2)
查看您的MATLAB代码,您有 -
C = bsxfun(@times, permute(A,[4,2,5,1,3]), permute(B, [1,6,2,7,3,4,5])
所以,实质上 -
B : 1 , 6 , 2 , 7 , 3 , 4 , 5
A : 4 , 2 , 5 , 1 , 3
现在,在MATLAB中,我们不得不从较高的维度中借用单个维度,这就是为6
和7
引入变暗B
,4
的所有麻烦的原因。 5
的{{1}}。
在NumPy中,我们使用np.newaxis
/None明确地引入了这些内容。因此,对于NumPy,我们可以这样说 -
A
,其中B : 1 , N , 2 , N , 3 , 4 , 5
A : N , 2 , N , 1 , 3 , N , N
代表新轴。请注意,我们需要在N
的末尾添加新轴以推进其他维度以进行对齐。相反,默认情况下会在MATLAB中发生这种情况。
使A
看起来很容易,因为尺寸似乎是有序的,我们只需要在适当的位置添加新轴 - B
。
创建这样的B[:,None,:,None,:,:,:]
看起来并不直截了当。忽略A
中的N's
,我们会有 - A
。因此,起点是置换维度,然后添加被忽略的两个新轴 - A : 2 , 1 , 3
。
到目前为止,我们有 -
A.transpose(1,0,2)[None,:,None,:,:,None,None]
在NumPy中,我们可以跳过领先的新轴和尾随非单一的dims。所以,我们可以像这样简化 -
B (new): B[:,None,:,None,:,:,:]
A (new): A.transpose(1,0,2)[None,:,None,:,:,None,None]
最终输出将是这两个扩展版本之间的乘法 -
B (new): B[:,None,:,None]
A (new): A.transpose(1,0,2)[:,None,...,None,None]
运行时测试
我相信@Andras的帖子意味着等效的C = A.transpose(1,0,2)[:,None,...,None,None]*B[:,None,:,None]
实现类似于:np.einsum
。
np.einsum('ijk,lmkno->ljmikno',A,B)