Как выполнить умножение матриц между двумя 3D-тензорами вдоль первого измерения?

#python #numpy #numpy-einsum

Вопрос:

Я хочу вычислить точечное произведение между двумя 3D-тензорами вдоль первого измерения. Я попробовал следующую нотацию einsum:

 import numpy as np

a = np.random.randn(30).reshape(3, 5, 2)
b = np.random.randn(30).reshape(3, 2, 5)

# Expecting shape: (3, 5, 5)
np.einsum("ijk,ikj->ijj", a, b)
 

К сожалению, он возвращает эту ошибку:

 ValueError: einstein sum subscripts string includes output subscript 'j' multiple times
 

Я пошел с суммой Эйнштейна после того, как потерпел неудачу в этом np.tensordot . Идеи и последующие вопросы приветствуются!

Комментарии:

1. Умножение матрицы вдоль первой оси/измерения звучит так , как вы просто хотите a @ b , что позволит выполнить умножение матрицы (5, 2) x (2, 5) для каждой из трех пар из a и b .

Ответ №1:

Ваши два измерения размера 5 и 5 не соответствуют одним и тем же осям. Таким образом, вам нужно использовать два разных индекса для их обозначения. Например, вы можете сделать:

 >>> res = np.einsum('ijk,ilm->ijm', a, b)

>>> res.shape
(3, 5, 5)
 

Обратите внимание, что вам также необходимо изменить индекс для осей размера 2 и 2 . Это связано с тем, что вы вычисляете пакетированный внешний продукт (т. Е. Мы выполняем итерацию по двум осям одновременно), а не точечный продукт (т. Е. мы выполняем итерацию одновременно по двум осям).

  • Наружный продукт:
     >>> np.einsum('ijk,ilm->ijm', a, b)
     
  • Точечный продукт над индексом k , который имеет axis=2 a и axis=1 b :
     >>> np.einsum('ijk,ikm->ijm', a, b)
     

    что эквивалентно a@b .

Комментарии:

1. Что это нужно оператору? Я думала, он хочет a@b . Ваш первый пример приобретает правильную форму, но делает это, сначала уменьшая a и b до (3,5,1) и (3,1,5), и делая транслируемую внешнюю.

Ответ №2:

dot product ... along the first dimension это немного неясно. Является ли первое измерение «пакетным» измерением, с 3 точками на остальных? Или что-то еще?

 In [103]: a = np.random.randn(30).reshape(3, 5, 2)
     ...: b = np.random.randn(30).reshape(3, 2, 5)
In [104]: (a@b).shape
Out[104]: (3, 5, 5)
In [105]: np.einsum('ijk,ikl->ijl',a,b).shape
Out[105]: (3, 5, 5)
 

@Ivan's ответ другой:

 In [106]: np.einsum('ijk,ilm->ijm', a, b).shape
Out[106]: (3, 5, 5)
In [107]: np.allclose(np.einsum('ijk,ilm->ijm', a, b), a@b)
Out[107]: False

In [108]: np.allclose(np.einsum('ijk,ikl->ijl', a, b), a@b)
Out[108]: True
 

Иван суммирует k размерность одного и l другого, а затем выполняет трансляцию по элементам. Это не умножение матриц:

 In [109]: (a.sum(axis=-1,keepdims=True)* b.sum(axis=1,keepdims=True)).shape
Out[109]: (3, 5, 5)
In [110]: np.allclose((a.sum(axis=-1,keepdims=True)* b.sum(axis=1,keepdims=True)),np.einsum('ijk,ilm->ijm', a,
     ...:  b))
Out[110]: True
 

Еще один тест пакетной обработки:

 In [112]: res=np.zeros((3,5,5))
     ...: for i in range(3):
     ...:    res[i] = a[i]@b[i]
     ...: np.allclose(res, a@b)
Out[112]: True