Компактный и естественный способ записи матричного произведения векторов в Numpy - PullRequest
0 голосов
/ 15 мая 2018

В научных вычислениях я часто хочу делать векторные умножения, такие как

a x b ^ T

, где a и b - векторы строк, а b ^ T - транспонирование вектора. Поэтому, если a и b имеют форму [n, 1] и [m, 1], результирующая матрица имеет форму [n, m]

Есть ли хороший и прямой способ написать это умножение в numpy?

Пример:

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

Работает добавление осей вручную:

a[:,np.newaxis] @ b[np.newaxis,:]

и дает правильный результат:

[[ 4  5  6  7]
 [ 8 10 12 14]
 [12 15 18 21]]

Запись Эйнштейна могла бы быть иным способом, но все же несколько странной.

np.einsum('a,b->ab', a,b)

То, на что я надеялся работать, , но не работает , это следующее:

a @ b.T

Есть ли другие способы сделать это?

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

В матрице MATLAB умножение является нормой, используя *.Элементное умножение использует оператор .*.Также матрицы имеют как минимум 2d.

In numpy, для поэлементного умножения используется *.Умножение матриц выполняется с помощью np.dot (или его метода), а в последнее время - с помощью оператора @ (np.matmul).numpy добавляет широковещательную передачу, которая придает умножению по элементам намного большую выразительность.

С вашими 2 примерами массивов формы (3,) и (4,) можно создать (3,4) outer product https://en.wikipedia.org/wiki/Outer_product включает в себя:

np.outer(a,b)     

np.einsum('i,j->ij, a, b)  # matching einstein index notation

a[:,None] * b       # the most idiomatic numpy expression

Последнее работает из-за вещания.a[:, None], подобно a.reshape(-1,1), превращает массив (3,) в (3,1).b[None, :] превращает (4,) в (1,4).Но вещание может выполнить это обновление автоматически (и однозначно).

(3,1) * (4,) => (3,1) * (1,4) => (3,4)

Вещание не работает с np.dot.Поэтому нам нужно

a[:, None].dot(b[None, :])   #  (3,1) dot with (1,4)

Ключ с dot состоит в том, что последний дим из a пар со вторым до последнего из b.(np.dot также работает с 2 совпадающими одномерными массивами, выполняя обычное произведение векторов-точек).

@ (matmul) представляет оператор, который работает как dot, по крайней мере, в 2d с2-й случай.С массивами более высокой размерности они работают по-разному.

a[:,None].dot(b[None,:])
np.dot(a[:,None], b[None,:])
a[:,None] @ b[None,:]
a[:,None] @ b[:,None].T

и эквиваленты reshape создают требуемый (3,4) массив.

np.tensordot может обрабатывать комбинации других размеров, но работает путем изменения формы и транспонирования входных данныхтак что в итоге он может передать их dot.Затем он преобразует результат обратно в желаемую форму.

Быстрые временные тесты показывают, что версии np.dot имеют тенденцию быть самыми быстрыми - потому что они делегируют действие быстрым BLAS-подобным библиотекам.Для других версий делегирование немного более косвенное, либо они используют numpy's собственный скомпилированный код.

0 голосов
/ 15 мая 2018

В комментариях было предложено несколько решений, которые я суммирую здесь:

  • np.outer(a,b), который в основном переформулирует это мультипликат как заданную проблему (благодаря Brenlla )
  • a[:,np.newaxis]*b (спасибо Divakar )
  • a.reshape((-1,1)) @ b.reshape((-1,1)).T или точно так же
    a.reshape((-1,1)) @ b.reshape((1,-1)). Это немного долго, но показывает, что эти операции с матрицей на самом деле нужны матрицы как входы, а не только векторы (благодаря Уоррену Векессеру и heltonbiker )

Для полноты, мои предыдущие уже работающие примеры:

  • a[:,np.newaxis] @ b[np.newaxis,:]
  • np.einsum('a,b->ab', a,b)

Примечание: Чтобы еще больше уменьшить количество символов, можно использовать None вместо np.newaxis.

...