numpy 了解两个Tensor的einsum应用细节

v6ylcynt  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(79)

我们有两个Tensor:

a = np.arange(8.).reshape(4,2,1)
 b = np.arange(16.).reshape(2,4,2)

字符串
我们将实施

np.einsum('ijk,jil->kl', a, b)


虽然我们可以得到它的结果,但我们一直在坚持了解Tensor元素求和的过程细节。
首先,我们知道如何

np.einsum('jil', b)


改变bTensor的元素顺序。
但我们无法理解np.einsum('ijk,jil->kl', a, b)如何合并组合(求和)Tensor元素。
为了跟踪这个过程,我们使用了字符串:

aa=[[['e'],
  ['r']],

 [['t'],
  ['y']],

 [['u'],
  ['o']],

 [['p'],
  ['q']]]


bb=[[[ 'x', 'c'],
  [ 'v' , 'n'],
  [ 'm',  'h'],
  [ 'f' , 'd']],

 [[ 's',  'w'],
  [ 'a','z'],
  ['j', 'k'],
  ['l', 'b']]]


因为我们想看看不同的元素如何组合合并来获得np.einsum('ijk,jil->kl', aa, bb)
然而np.einsum('jil', bb)工作正常,但它没有向我展示元素求和的细节。

p1iqtdky

p1iqtdky1#

有几种方法可以理解这一点。
一个是使用@Onyambu建议的例子。

>>> np.einsum('ijk,jil->ijkl', a, b)

array([[[[  0.,   0.]],
        [[  8.,   9.]]],

       [[[  4.,   6.]],
        [[ 30.,  33.]]],

       [[[ 16.,  20.]],
        [[ 60.,  65.]]],

       [[[ 36.,  42.]],
        [[ 98., 105.]]]])

字符串
通过将i和j作为输出的索引,输出数组不再具有形状(k,l),而是(i,j,k,l)。此外,没有相乘的元素被求和在一起。输出数组的每个元素是每个原始数组中的一个元素的乘积。
为了回到最初的行为,我们可以通过轴1求和:

>>> np.einsum('ijk,jil->ijkl', a, b).sum(axis=1)
array([[[  8.,   9.]],
       [[ 34.,  39.]],
       [[ 76.,  85.]],
       [[134., 147.]]])


然后按轴0求和:

>>> np.einsum('ijk,jil->ijkl', a, b).sum(axis=1).sum(axis=0)

array([[252., 280.]])


理解这一点的另一种方法是将其转换为显式循环。
下面的代码与此einsum等效,但速度较慢。(它也不检查A和B的形状是否兼容。)

def sum_array(A, B):
    i_len, j_len, k_len = A.shape
    _, _, l_len = B.shape
    
    ret = np.zeros((k_len, l_len))
    for i in range(i_len):
        for j in range(j_len):
            for k in range(k_len):
                for l in range(l_len):
                    ret[k, l] += A[i, j, k] * B[j, i, l]
    return ret


这给了我们同样的结果,array([[252., 280.]])
请注意,循环的内部行ret[k, l] += A[i, j, k] * B[j, i, l]与einsum下标'ijk,jil->kl'相似,只是kl被移到了开头,ijk用于索引A,jil用于索引B。

更多信息

Understanding NumPy's einsum

相关问题