Взятие точечных произведений многомерных массивов numpy

Я пытаюсь взять точечный продукт между тремя массивами numpy. Тем не менее, я изо всех сил пытаюсь обернуть голову вокруг этого.

Проблема заключается в следующем:

У меня есть два (4) массива numpy в форме a и b соответственно, а также массив numpy с формой (4, 4, 3), c.

import numpy as np

a = np.array([0, 1, 2, 3])
b = np.array([[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
          [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],
          [[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]],
          [[4, 4, 4], [4, 4, 4], [4, 4, 4], [4, 4, 4]]])
c = np.array([4, 5, 6, 7])

Я хочу вычислить точечный продукт таким образом, чтобы мой результат был 3-кортежным. То есть сначала расставьте точки a с b, а затем расставьте точки с c, при необходимости транспонируя. Другими словами, я хочу вычислить скалярное произведение между a, b и c, как если бы c имело форму (4, 4), но в результате я хочу получить тройной кортеж.

Я пытался:

Изменение формы a и c, а затем вычисление скалярного произведения:

a = np.reshape(a, (4, 1))
c = np.reshape(c, (4, 1))

tmp = np.dot(a.T, b) # now has shape (1, 4, 3)
result = np.dot(tmp, c) 

В идеале у меня теперь должно быть:

print(result.shape)
>> (1, 1, 3) 

но я получаю ошибку

ValueError: фигуры (1,4,3) и (4,1) не выровнены: 3 (тусклый 2)! = 4 (тусклый 0)

Я также пытался использовать функцию tensordot из numpy, но безуспешно.


person Ivar Stangeby    schedule 02.04.2017    source источник
comment
Можете ли вы разбить строку результата на операторы? Будет легче увидеть, какой скалярный продукт терпит неудачу.   -  person David Ehrmann    schedule 02.04.2017
comment
Ошибка довольно ясна: вы пытаетесь вычислить скалярное произведение для объектов разных размеров.   -  person David Ehrmann    schedule 02.04.2017
comment
Вы ищете что-то подобное? np.dot(np.dot(a, b).T, c) ; результат array([360, 360, 360])   -  person kmario23    schedule 02.04.2017
comment
@DavidEhrmann Я знаю, что они разных размеров. Я надеялся, что numpy автоматически распределит по последней оси. То есть скалярное произведение будет проходить по двум первым осям, если это имеет смысл. (1, 4) (4, 1) @kmario23 np.dot(np.dot(a, b).T, c) не работает, так как a и b имеют форму (1, 4)   -  person Ivar Stangeby    schedule 02.04.2017


Ответы (3)


Основное правило dot(A,B): последняя ось A со 2-й до последней из B

In [965]: a.shape
Out[965]: (4,)
In [966]: b.shape
Out[966]: (4, 4, 3)

ac) равно 1d. Это (4,) может поставить точку со 2-м (4) из b с:

In [967]: np.dot(a,b).shape
Out[967]: (4, 3)

Использование c на выходе дает массив (3,)

In [968]: np.dot(c, np.dot(a,b))
Out[968]: array([360, 360, 360])

Эта комбинация может быть более понятной с эквивалентом einsum:

In [971]: np.einsum('i,jik,j->k',a,b,c)
Out[971]: array([360, 360, 360])

Но что, если мы хотим, чтобы a воздействовал на 1-ю ось b? С einsum это легко сделать:

In [972]: np.einsum('i,ijk,j->k',a,b,c)
Out[972]: array([440, 440, 440])

Чтобы сделать то же самое с dot, мы могли бы просто поменять местами a и c:

In [973]: np.dot(a, np.dot(c,b))
Out[973]: array([440, 440, 440])

Или транспонировать оси b:

In [974]: np.dot(c, np.dot(a,b.transpose(1,0,2)))
Out[974]: array([440, 440, 440])

Этот вопрос о перестановке был бы понятнее, если бы a и c имели разную длину. например (2,) и (4,) с (2,4,3) или (4,2,3).


In

tmp = np.dot(a.T, b) # now has shape (1, 4, 3)

у вас есть (1,4a) с точками (4,4a,3). Результат (1,4,3). Я добавил a, чтобы определить, когда оси были объединены.

Чтобы применить (4,1) c, мы должны сделать то же самое транспонирование:

In [977]: np.dot(c[:,None].T, np.dot(a[:,None].T, b))
Out[977]: array([[[360, 360, 360]]])
In [978]: _.shape
Out[978]: (1, 1, 3)

np.dot(c[None,:], np.dot(a[None,:], b)) сделал бы то же самое без транспонирования.


Я надеялся, что numpy автоматически распределит по последней оси. То есть скалярное произведение будет проходить по двум первым осям, если это имеет смысл.

Учитывая правило dot, которое я привел в начале, это не имеет смысла. Но если мы транспонируем b так, чтобы ось (3) была первой, она может «переносить это», используя последнюю и 2-ю в последнюю.

In [986]: b.transpose(2,0,1).shape
Out[986]: (3, 4, 4)
In [987]: np.dot(a, b.transpose(2,0,1)).shape
Out[987]: (3, 4)
In [988]: np.dot(np.dot(a, b.transpose(2,0,1)),c)
Out[988]: array([440, 440, 440])

(4a).(3, 4a, 4c) -> (3, 4c)
(3, 4c). (4c) -> 3
person hpaulj    schedule 02.04.2017

Не автоматический, но выполняет свою работу:

np.einsum('i,ijk,j->k',a,b,c)
# array([440, 440, 440])

Это вычисляет d формы (3,), так что d_k = sum_{ij} a_i b_{ijk} c_j.

person Paul Panzer    schedule 02.04.2017

Вы умножаете матрицу (1,4,3) на матрицу (4,1), поэтому это невозможно, потому что у вас есть 3 страницы матриц (1,4) в b. Если вы хотите умножить каждую страницу матрицы b на c, просто умножьте каждую страницу отдельно.

a = np.array([0, 1, 2, 3])
b = np.array([[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
      [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],
      [[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]],
      [[4, 4, 4], [4, 4, 4], [4, 4, 4], [4, 4, 4]]])
c = np.array([4, 5, 6, 7])

a = np.reshape(a, (4, 1))
c = np.reshape(c, (4, 1))

tmp = np.dot(a.T, b) # now has shape (1, 4, 3)
result = np.dot(tmp[:,:,0], c)
for i in range(1,3):
    result = np.dstack((result, np.dot(tmp[:,:,i], c)))
print np.shape(result)

Итак, у вас есть результат размера (1,1,3)

person BatyaGG    schedule 02.04.2017