Diferencia entre numpy dot () y Python 3.5+ matriz de multiplicación @

119

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 puntos 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 forma:

c.shape
(8, 13, 13)

mientras que la np.dot()función devuelve:

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

¿Cómo puedo reproducir el mismo resultado con numpy dot? ¿Existen otras diferencias significativas?

blaz
fuente
5
No puedes sacar ese resultado fuera de punto. Creo que la gente generalmente estuvo de acuerdo en que el manejo de dot de entradas de alta dimensión fue una decisión de diseño incorrecta.
user2357112 apoya a Monica el
¿Por qué no implementaron la matmulfunción hace años? @como operador infijo es nuevo, pero la función funciona igual de bien sin él.
hpaulj

Respuestas:

140

El @operador llama al __matmul__método de la matriz , no dot. Este método también está presente en la API como 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:

matmulse diferencia doten dos aspectos importantes.

  • No se permite la multiplicación por escalares.
  • Las pilas de matrices se difunden juntas como si las matrices fueran elementos.

El último punto deja en claro que los métodos doty se matmulcomportan de manera diferente cuando se pasan matrices 3D (o de mayor dimensión). Citando algo más de la documentación:

Para matmul:

Si cualquiera de los argumentos es ND, N> 2, se trata como una pila de matrices que residen en los dos últimos índices y se difunde en consecuencia.

Para np.dot:

Para matrices 2-D es equivalente a la multiplicación de matrices, y para matrices 1-D al producto interno de vectores (sin conjugación compleja). Para N dimensiones es un producto de suma sobre el último eje de ay el penúltimo de b

Alex Riley
fuente
13
La confusión aquí probablemente se deba a las notas de la versión, que equiparan directamente el símbolo "@" con la función dot () de numpy en el código de ejemplo.
Alex K
12

La respuesta de @ajcr explica cómo difieren doty matmul(invocados por el @símbolo). Al observar un ejemplo simple, se 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 4x4 y devuelva el dotproducto y el matmulproducto con una 'pila de matrices' o tensor de 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,twobyfourbythree)))
print('4x4*3x4x2 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 ay el penúltimo de b

y cómo se forma el producto de la matriz al difundir la matriz en conjunto.

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]]]
Nathan
fuente
2
punto (a, b) [i, j, k, m] = suma (a [i, j ,:] * b [k,:, m]) ------- documentación similar dice: es un Producto de suma sobre el último eje de ay el penúltimo eje de b:
Ronak Agrawal
Buena captura sin embargo, es un 3x4x2. Otra forma de construir la matriz a = np.arange(24).reshape(3, 4, 2)sería crear una matriz con las dimensiones 3x4x2.
Nathan
8

Para su información, @y sus equivalentes numpy doty matmulson todos más o menos igual de rápido. (Parcela creada con perfplot , un proyecto mío).

ingrese la descripción de la imagen aquí

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,
)
Nico Schlömer
fuente
7

En matemáticas, creo que el punto en numpy tiene más sentido

punto (a, b) _ {i, j, k, a, b, c} =fórmula

ya que da el producto escalar cuando ayb son vectores, o la multiplicación de matrices cuando ayb son matrices


En cuanto a la operación matmul en numpy, consta de partes del resultado del punto , y se puede definir como

> matmul (a, b) _ {i, j, k, c} =fórmula

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, combinándolo con la radiodifusión , puede obtener

matmul (a, b) _ {i, j, k, l} =fórmula

por ejemplo.


De las dos definiciones anteriores, puede ver los requisitos para utilizar esas dos operaciones. Suponga 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

    1. t3 = s4
    2. t2 = s2 , o uno de t2 y s2 es 1
    3. t1 = s1 , o uno de t1 y s1 es 1

Utilice 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
Yong Yang
fuente
np.matmultambién da el producto escalar en vectores y el producto matricial en matrices.
Subhaneil Lahiri
2

Aquí hay una comparación con np.einsumpara mostrar cómo se proyectan los índices

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
Friedrich
fuente
0

Mi experiencia con MATMUL y DOT

Constantemente recibía "ValueError: La forma de los valores pasados ​​es (200, 1), los índices implican (200, 3)" cuando intentaba usar MATMUL. Quería una solución rápida y encontré que DOT ofrecía la misma funcionalidad. No obtengo ningún error al usar DOT. Obtengo la respuesta correcta

con 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)"

con DOT

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

YY.shape

>>> (200, )
Sambath Parthasarathy
fuente