tamaño - Diferencia entre numpy dot() y Python 3.5+ matrix multiplication @
que es un array en python (4)
Recientemente me mudé a Python 3.5 y noté que el nuevo operador de multiplicación de matrices (@) a veces se comporta de manera diferente al operador de punto numpy . Por ejemplo, para matrices 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)
El operador
@
devuelve una matriz de formas:
c.shape
(8, 13, 13)
mientras que la función
np.dot()
devuelve:
d.shape
(8, 13, 8, 13)
¿Cómo puedo reproducir el mismo resultado con numpy dot? ¿Hay alguna otra diferencia significativa?
El operador
@
llama al método
__matmul__
la matriz, no al
dot
.
Este método también está presente en la API como la función
np.matmul
.
>>> a = np.random.rand(8,13,13)
>>> b = np.random.rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)
De la documentación:
matmul
difiere dedot
en dos formas importantes.
- La multiplicación por escalares no está permitida.
- Las pilas de matrices se transmiten juntas como si las matrices fueran elementos.
El último punto deja en claro que los métodos de
dot
y
matmul
comportan de manera diferente cuando se pasan matrices 3D (o de dimensiones más altas).
Citando de la documentación un poco más:
Para
matmul
:
Si cualquiera de los argumentos es ND, N> 2, se trata como una pila de matrices que residen en los últimos dos índices y se transmite en consecuencia.
Para
np.dot
:
Para las matrices en 2-D es equivalente a la multiplicación de matrices, y para las matrices en 1-D al producto interno de los vectores (sin conjugación compleja). Para N dimensiones es un producto suma sobre el último eje de a y el penúltimo de b
En matemáticas, creo que el punto numpy tiene más sentido
punto (a, b) _ {i, j, k, a, b, c} = / sum_m a_ {i, j, k, m} b_ {a, b, m, c}
dado que da el producto escalar cuando a y b son vectores, o la multiplicación matricial cuando a y b son matrices
En cuanto a la operación matmul en numpy, consta de partes del resultado de punto , y se puede definir como
matmul (a, b) _ {i, j, k, c} = / sum_m a_ {i, j, k, m} b_ {i, j, m, c}
Entonces, puede ver que matmul (a, b) devuelve una matriz con una forma pequeña, que tiene un menor consumo de memoria y tiene más sentido en las aplicaciones. En particular, combinando con la broadcasting , puede obtener
matmul (a, b) _ {i, j, k, l} = / sum_m a_ {i, j, k, m} b_ {j, m, l}
por ejemplo.
De las dos definiciones anteriores, puede ver los requisitos para usar esas dos operaciones. Suponga que a.shape = (s1, s2, s3, s4) y b.shape = (t1, t2, t3, t4)
-
Para usar el punto (a, b) necesitas
1. **t3=s4**;
-
Para usar matmul (a, b) necesitas
- t3 = s4
- t2 = s2 , o uno de t2 y s2 es 1
- t1 = s1 , o uno de t1 y s1 es 1
Use el siguiente código para convencerse.
Muestra de código
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
La respuesta de @ajcr explica cómo difieren el
dot
y el
matmul
(invocado por el símbolo
@
).
Al observar un ejemplo simple, uno ve claramente cómo los dos se comportan de manera diferente cuando operan en ''pilas de matrices'' o tensores.
Para aclarar las diferencias, tome una matriz de 4x4 y devuelva el producto de
dot
y el producto
matmul
con una ''pila de matrices'' o tensor de 2x4x3.
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)))
Los productos de cada operación aparecen a continuación. Observe cómo es el producto escalar,
... un producto de suma sobre el último eje de a y el penúltimo de b
y cómo se forma el producto de la matriz transmitiendo la matriz juntos.
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]]]
Solo para su información,
@
y sus equivalentes numpy
dot
y
matmul
son casi igual de rápidos.
(Parcela creada con
perfplot
, un proyecto mío).
Código para reproducir la trama:
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, )