Разница между numpy dot () и умножением матрицы Python 3.5+ @


119

Недавно я перешел на Python 3.5 и заметил, что новый оператор умножения матриц (@) иногда ведет себя иначе, чем оператор numpy dot . Например, для трехмерных массивов:

import numpy as np

a = np.random.rand(8,13,13)
b = np.random.rand(8,13,13)
c = a @ b  # Python 3.5+
d = np.dot(a, b)

@Оператор возвращает массив формы:

c.shape
(8, 13, 13)

пока np.dot()функция возвращает:

d.shape
(8, 13, 8, 13)

Как я могу воспроизвести тот же результат с помощью numpy dot? Есть ли другие существенные отличия?


5
Вы не можете получить такой результат из точки. Я думаю, что люди в целом согласились с тем, что использование dot обработки входных данных большой размерности было неправильным дизайнерским решением.
user2357112 поддерживает Монику,

Почему они не реализовали эту matmulфункцию много лет назад? @как инфиксный оператор является новым, но функция работает так же хорошо и без него.
hpaulj

Ответы:


140

@Оператор называет массива в __matmul__метод, а не dot. Этот метод также присутствует в API как функция np.matmul.

>>> a = np.random.rand(8,13,13)
>>> b = np.random.rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)

Из документации:

matmulотличается от dotдвух важных аспектов.

  • Умножение на скаляры не допускается.
  • Стеки матриц транслируются вместе, как если бы матрицы были элементами.

Последний пункт становится ясно , что dotи matmulметоды ведут себя по- разному при передаче 3D (или многомерные) массивов. Еще цитата из документации:

Для matmul:

Если любой из аргументов равен ND, N> 2, он обрабатывается как стек матриц, находящихся в последних двух индексах, и транслируется соответственно.

Для np.dot:

Для двумерных массивов это эквивалентно умножению матриц, а для одномерных массивов - внутреннему произведению векторов (без комплексного сопряжения). Для N измерений это сумма произведения по последней оси a и предпоследней оси b.


13
Путаница здесь, вероятно, из-за примечаний к выпуску, которые прямо приравнивают символ «@» к функции dot () numpy в примере кода.
Alex K

13

Ответ @ajcr объясняет, чем отличаются dotи matmul(вызываемые @символом). Глядя на простой пример, можно ясно увидеть, как они по-разному ведут себя при работе со «стопками матриц» или тензорами.

Чтобы прояснить различия, возьмите массив 4x4 и верните dotпродукт и matmulпродукт со стеком матриц 3x4x2 или тензором.

import numpy as np
fourbyfour = np.array([
                       [1,2,3,4],
                       [3,2,1,4],
                       [5,4,6,7],
                       [11,12,13,14]
                      ])


threebyfourbytwo = np.array([
                             [[2,3],[11,9],[32,21],[28,17]],
                             [[2,3],[1,9],[3,21],[28,7]],
                             [[2,3],[1,9],[3,21],[28,7]],
                            ])

print('4x4*3x4x2 dot:\n {}\n'.format(np.dot(fourbyfour,threebyfourbytwo)))
print('4x4*3x4x2 matmul:\n {}\n'.format(np.matmul(fourbyfour,threebyfourbytwo)))

Ниже представлены продукты каждой операции. Обратите внимание на то, как выглядит скалярное произведение,

... произведение суммы по последней оси a и предпоследней оси b

и как матричный продукт формируется путем совместного распространения матрицы.

4x4*3x4x2 dot:
 [[[232 152]
  [125 112]
  [125 112]]

 [[172 116]
  [123  76]
  [123  76]]

 [[442 296]
  [228 226]
  [228 226]]

 [[962 652]
  [465 512]
  [465 512]]]

4x4*3x4x2 matmul:
 [[[232 152]
  [172 116]
  [442 296]
  [962 652]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]]

2
dot (a, b) [i, j, k, m] = sum (a [i, j ,:] * b [k,:, m]) ------- как в документации сказано: это произведение суммы по последней оси a и предпоследней оси b:
Ronak Agrawal

Однако неплохой улов, это 3х4х2. Другой способ построить матрицу - a = np.arange(24).reshape(3, 4, 2)создать массив размером 3x4x2.
Натан

8

Просто FYI @и его многочисленные эквиваленты, dotи matmulвсе они примерно одинаково быстры. (Сюжет создан с помощью моего проекта perfplot .)

введите описание изображения здесь

Код для воспроизведения сюжета:

import perfplot
import numpy


def setup(n):
    A = numpy.random.rand(n, n)
    x = numpy.random.rand(n)
    return A, x


def at(data):
    A, x = data
    return A @ x


def numpy_dot(data):
    A, x = data
    return numpy.dot(A, x)


def numpy_matmul(data):
    A, x = data
    return numpy.matmul(A, x)


perfplot.show(
    setup=setup,
    kernels=[at, numpy_dot, numpy_matmul],
    n_range=[2 ** k for k in range(12)],
    logx=True,
    logy=True,
)

7

В математике я думаю, что точка в numpy имеет больше смысла

точка (a, b) _ {i, j, k, a, b, c} =формула

поскольку он дает скалярное произведение, когда a и b - векторы, или умножение матриц, когда a и b - матрицы


Что касается операции matmul в numpy, она состоит из частей результата точки , и ее можно определить как

> matmul (a, b) _ {i, j, k, c} =формула

Итак, вы можете видеть, что matmul (a, b) возвращает массив небольшой формы, который потребляет меньше памяти и имеет больше смысла в приложениях. В частности, совмещая с трансляцией , можно получить

matmul (a, b) _ {i, j, k, l} =формула

например.


Из двух приведенных выше определений вы можете увидеть требования для использования этих двух операций. Предположим, что a.shape = (s1, s2, s3, s4) и b.shape = (t1, t2, t3, t4)

  • Чтобы использовать точку (a, b), вам нужно

    1. t3 = s4 ;
  • Для использования matmul (a, b) вам необходимо

    1. t3 = s4
    2. t2 = s2 , или одно из t2 и s2 равно 1
    3. t1 = s1 , или одно из t1 и s1 равно 1

Используйте следующий фрагмент кода, чтобы убедить себя.

Пример кода

import numpy as np
for it in xrange(10000):
    a = np.random.rand(5,6,2,4)
    b = np.random.rand(6,4,3)
    c = np.matmul(a,b)
    d = np.dot(a,b)
    #print 'c shape: ', c.shape,'d shape:', d.shape

    for i in range(5):
        for j in range(6):
            for k in range(2):
                for l in range(3):
                    if not c[i,j,k,l] == d[i,j,k,j,l]:
                        print it,i,j,k,l,c[i,j,k,l]==d[i,j,k,j,l] #you will not see them

np.matmulтакже дает скалярное произведение векторов и матричное произведение матриц.
Subhaneil Lahiri

2

Вот сравнение, np.einsumчтобы показать, как прогнозируются индексы

np.allclose(np.einsum('ijk,ijk->ijk', a,b), a*b)        # True 
np.allclose(np.einsum('ijk,ikl->ijl', a,b), a@b)        # True
np.allclose(np.einsum('ijk,lkm->ijlm',a,b), a.dot(b))   # True

0

Мой опыт работы с MATMUL и DOT

Я постоянно получал «ValueError: форма переданных значений (200, 1), индексы подразумевают (200, 3)» при попытке использовать MATMUL. Я хотел найти быстрое решение и обнаружил, что DOT предоставляет ту же функциональность. Я не получаю ошибок при использовании DOT. Я получаю правильный ответ

с MATMUL

X.shape
>>>(200, 3)

type(X)

>>>pandas.core.frame.DataFrame

w

>>>array([0.37454012, 0.95071431, 0.73199394])

YY = np.matmul(X,w)

>>>  ValueError: Shape of passed values is (200, 1), indices imply (200, 3)"

с DOT

YY = np.dot(X,w)
# no error message
YY
>>>array([ 2.59206877,  1.06842193,  2.18533396,  2.11366346,  0.28505879, 

YY.shape

>>> (200, )
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.