Разница между numpy dot() и умножением матрицы Python 3.5+ @
Недавно я перешел на Python 3.5 и заметил, что новый оператор умножения матриц (@) иногда ведет себя иначе, чем numpy dot. В примере, для 3D-массивов:
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? Существуют ли другие существенные различия?
Ответы
Ответ 1
Оператор @
вызывает метод массива __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
:
Если любой из аргументов N-D, N > 2, он рассматривается как стек матриц, находящихся в последних двух индексах и передаваемых соответственно.
Для np.dot
:
Для двухмерных массивов это эквивалентно матричному умножению, а для 1-D массивов - скалярному произведению векторов (без комплексного сопряжения). Для размерностей N это суммирующий продукт по последней оси а и второй по последнему из b
Ответ 2
Ответ @ajcr объясняет, как отличаются dot
и matmul
(вызываемые символом @
). Рассматривая простой пример, ясно видно, как они ведут себя по-разному при работе на "стеках матриц" или тензорах.
Чтобы прояснить различия, возьмите массив 4x4 и верните продукт dot
и matmul
с помощью стека матриц 2х4х3 или тензора.
import numpy as np
fourbyfour = np.array([
[1,2,3,4],
[3,2,1,4],
[5,4,6,7],
[11,12,13,14]
])
twobyfourbythree = 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*4x2x3 dot:\n {}\n'.format(np.dot(fourbyfour,twobyfourbythree)))
print('4x4*4x2x3 matmul:\n {}\n'.format(np.matmul(fourbyfour,twobyfourbythree)))
Ниже приведены продукты каждой операции. Обратите внимание, как работает точка-точка,
... произведение суммы на последнюю ось а и второе на второе из b
и как матричный продукт формируется путем трансляции матрицы вместе.
4x4*4x2x3 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*4x2x3 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]]]
Ответ 3
В математике я думаю, что точка в numpy имеет больше смысла
точка (a, b) _ {i, j, k, a, b, c} =\sum_m a_ {i, j, k, m} b_ {a, b, m, с}
так как он дает точечный продукт, когда a и b являются векторами, или матричное умножение, когда a и b являются матрицами
Что касается операции matmul в numpy, она состоит из частей результата dot и может быть определена как
matmul (a, b) _ {i, j, k, c} =\sum_m a_ {i, j, k, m} b_ {i, j, m, c}
Итак, вы можете видеть, что matmul (a, b) возвращает массив с малой формой,
который имеет меньшее потребление памяти и имеет больше смысла в приложениях.
В частности, в сочетании с broadcasting вы можете получить
matmul (a, b) _ {i, j, k, l} =\sum_m a_ {i, j, k, m} b_ {j, m, l}
например.
Из приведенных выше двух определений вы можете увидеть требования к использованию этих двух операций. Предположим a.shape = (s1, s2, s3, s4) и b.shape = (t1, t2, t3, t4)
-
Чтобы использовать точку (a, b), вам нужно
1. **t3=s4**;
-
Чтобы использовать matmul (a, b), вам нужно
- t3 = s4
- t2 = s2, или один из t2 и s2 равен 1
- 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
Ответ 4
Только FYI, @
и его обалденные эквиваленты dot
и matmul
все примерно одинаково быстро. (Сюжет создан с помощью perfplot, моего проекта.)
![enter image description here]()
Код для воспроизведения сюжета:
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,
)