In [321]: vec1=np.array([0,0.5,1,0.5]); vec2=np.array([2,0.5,1,0.5])
...: vec=np.transpose(np.stack((vec1,vec2)))
In [322]: vec1.shape
Out[322]: (4,)
In [323]: vec.shape
Out[323]: (4, 2)
stack 函数的一个好处是我们可以指定一个轴,跳过转置:
In [324]: np.stack((vec1,vec2), axis=1).shape
Out[324]: (4, 2)
为什么要混合使用 np. 和 n.? NameError: name 'n' is not defined。这种事情差点把我吓跑了。
In [326]: mat = np.moveaxis(np.array([[[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,2,3]],[[-1,2.,0
...: ,1.],[0,0,-1,2.],[0,1,-1,2.],[1,0.1,1,1]]]),0,2)
In [327]: mat.shape
Out[327]: (4, 4, 2)
In [328]: outvec=np.zeros((4,2))
...: for i in range(2):
...: outvec[:,i]=np.dot(mat[:,:,i],vec[:,i])
...:
In [329]: outvec
Out[329]:
array([[ 4. , -0.5 ],
[ 4. , 0. ],
[ 4. , 0.5 ],
[ 4. , 3.55]])
In [330]: # (4,4,2) (4,2) 'kji,ji->ki'
从您的循环中,i 轴(大小 2)的位置很清楚 - 在所有 3 个数组中的最后一个。这为vec 留下了一个轴,我们称之为j。它与最后一个配对(在mat 的i 旁边)。 k 从 mat 延续到 outvec。
In [331]: np.einsum('kji,ji->ki', mat, vec)
Out[331]:
array([[ 4. , -0.5 ],
[ 4. , 0. ],
[ 4. , 0.5 ],
[ 4. , 3.55]])
einsum 字符串通常会自行写入。例如,如果mat 被描述为 (m,n,k),vec 被描述为 (n,k),结果为 (m,k)
在这种情况下,只有 j 维度被求和 - 它出现在左侧,但在右侧。最后一个维度,i 在我的符号中,没有求和,因为 if 出现在两边,就像它在你的迭代中一样。我认为这是“随心所欲”。它不是dot 产品的积极组成部分。
实际上,你是在最后一个维度上堆叠,尺寸为 2。通常我们堆叠在第一个,但你将两者都调换到最后。
您的“失败”尝试运行,并且可以重现为:
In [332]: np.einsum('ijk,il->ik', mat, vec)
Out[332]:
array([[12. , 4. ],
[ 6. , 1. ],
[12. , 4. ],
[ 6. , 3.1]])
In [333]: mat.sum(axis=1)*vec.sum(axis=1)[:,None]
Out[333]:
array([[12. , 4. ],
[ 6. , 1. ],
[12. , 4. ],
[ 6. , 3.1]])
j 和 l 维度未出现在右侧,因此将它们相加。它们可以在相乘之前求和,因为它们每个只出现在一个术语中。我添加了None 以启用广播(将ik 与i 相乘)。
np.einsum('ik,i->ik', mat.sum(axis=1), vec.sum(axis=1))
如果您堆叠在第一个上,并为 vec (2,4,1) 添加了一个维度,那么它会在 matmul 上添加一个 (2,4,4) 垫子。 mat @ vec[...,None].
In [337]: m1 = mat.transpose(2,0,1)
In [338]: m1@v1[...,None]
Out[338]:
array([[[ 4. ],
[ 4. ],
[ 4. ],
[ 4. ]],
[[-0.5 ],
[ 0. ],
[ 0.5 ],
[ 3.55]]])
In [339]: _.shape
Out[339]: (2, 4, 1)