Скалярный продукт для массивов NumPy произвольной формы

Учитывая два объекта numpy.ndarray, A и B, произвольной формы, я хотел бы вычислить numpy.ndarray C со свойством, что C[i] == np.dot(A[i], B[i]) для всех i. Как я могу это сделать?

Пример 1: A.shape==(2,3,4) и B.shape==(2,4,5), тогда у нас должно быть C.shape==(2,3,5).

Пример 2: A.shape==(2,3,4) и B.shape==(2,4), тогда должно быть C.shape==(2,3).


person dshin    schedule 20.06.2016    source источник
comment
Таким образом, формы A и B не являются полностью произвольными. Они должны иметь совместимые формы на каждом i. А именно, A.shape[0] == B.shape[0] должно быть True   -  person piRSquared    schedule 20.06.2016
comment
einsum('ijk,ik...->ij...',A,B) занимается вашими двумя делами. Он просто ограничивает A значением 3d, B может быть 2,3 и т. д.   -  person hpaulj    schedule 21.06.2016


Ответы (2)


Вот общее решение для всех случаев/произвольных форм с использованием некоторых reshaping и np.einsum. Здесь помогает einsum, так как нам нужно выравнивание по первой оси и уменьшение по последним осям входных массивов. Реализация будет выглядеть примерно так -

def dotprod_axis0(A,B):
    N,nA,nB = A.shape[0], A.shape[-1], B.shape[1]
    Ar = A.reshape(N,-1,nA)
    Br = B.reshape(N,nB,-1)
    return np.squeeze(np.einsum('ijk,ikl->ijl',Ar,Br))

Случаи

И. А : 2D, Б : 2D

In [119]: # Inputs
     ...: A = np.random.randint(0,9,(3,4))
     ...: B = np.random.randint(0,9,(3,4))
     ...: 

In [120]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
33
86
48

In [121]: dotprod_axis0(A,B)
Out[121]: array([33, 86, 48])

II. А : 3D, Б : 3D

In [122]: # Inputs
     ...: A = np.random.randint(0,9,(2,3,4))
     ...: B = np.random.randint(0,9,(2,4,5))
     ...: 

In [123]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
[[ 74  70  53 118  43]
 [ 47  43  29  95  30]
 [ 41  37  26  23  15]]
[[ 50  86  33  35  82]
 [ 78 126  40 124 140]
 [ 67  88  35  47  83]]

In [124]: dotprod_axis0(A,B)
Out[124]: 
array([[[ 74,  70,  53, 118,  43],
        [ 47,  43,  29,  95,  30],
        [ 41,  37,  26,  23,  15]],

       [[ 50,  86,  33,  35,  82],
        [ 78, 126,  40, 124, 140],
        [ 67,  88,  35,  47,  83]]])

III. А : 3D, Б : 2D

In [125]: # Inputs
     ...: A = np.random.randint(0,9,(2,3,4))
     ...: B = np.random.randint(0,9,(2,4))
     ...: 

In [126]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
[ 87 105  53]
[152 135 120]

In [127]: dotprod_axis0(A,B)
Out[127]: 
array([[ 87, 105,  53],
       [152, 135, 120]])

IV. А : 2D, Б : 3D

In [128]: # Inputs
     ...: A = np.random.randint(0,9,(2,4))
     ...: B = np.random.randint(0,9,(2,4,5))
     ...: 

In [129]: for i in range(A.shape[0]):
     ...:     print np.dot(A[i], B[i])
     ...:     
[76 93 31 75 16]
[ 33  98  49 117 111]

In [130]: dotprod_axis0(A,B)
Out[130]: 
array([[ 76,  93,  31,  75,  16],
       [ 33,  98,  49, 117, 111]])
person Divakar    schedule 20.06.2016
comment
В общем, np.dot(x,y) может быть четко определен, даже когда len(x.shape)›2 и даже когда len(x.shape)!=len(y.shape). На практике я, вероятно, могу просто if/else на основе формы A и B для нескольких случаев, которые меня сейчас волнуют, но я надеялся этого не делать. - person dshin; 20.06.2016
comment
@dshin Да! Некоторая перестройка может помочь. - person Divakar; 20.06.2016
comment
@dshin Ознакомьтесь с отредактированным кодом, чтобы охватить все случаи! - person Divakar; 21.06.2016

Предполагая, что вам нужно обычное матричное умножение для dot (а не, скажем, матрица-вектор или странная хрень, которую dot делает для более высоких измерений), тогда достаточно последние версии NumPy (1.10+) позволяют вам сделать

C = numpy.matmul(A, B)

и достаточно свежие версии Python (3.5+) позволяют вам написать это как

C = A @ B

предполагая, что ваш NumPy также достаточно новый.

person user2357112 supports Monica    schedule 20.06.2016
comment
Я добавил в OP пару примеров, один из которых имеет матрицу-вектор. - person dshin; 20.06.2016