Unterschied zwischen numpy dot () und Python 3.5+ Matrixmultiplikation @


119

Ich bin kürzlich zu Python 3.5 gewechselt und habe festgestellt, dass sich der neue Matrixmultiplikationsoperator (@) manchmal anders verhält als der Numpy-Punkt- Operator. Beispiel für 3D-Arrays:

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)

Der @Operator gibt ein Array von Formen zurück:

c.shape
(8, 13, 13)

während die np.dot()Funktion zurückgibt:

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

Wie kann ich das gleiche Ergebnis mit numpy dot reproduzieren? Gibt es noch andere signifikante Unterschiede?


5
Sie können dieses Ergebnis nicht aus dem Punkt herausholen. Ich denke, die Leute waren sich im Allgemeinen einig, dass der Umgang von dot mit hochdimensionalen Eingaben die falsche Entwurfsentscheidung war.
user2357112 unterstützt Monica

Warum haben sie die matmulFunktion vor Jahren nicht implementiert ? @als Infix-Operator ist neu, aber die Funktion funktioniert auch ohne.
hpaulj

Antworten:


140

Der @Operator ruft die __matmul__Methode des Arrays nicht auf dot. Diese Methode ist auch in der API als Funktion vorhanden np.matmul.

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

Aus der Dokumentation:

matmulunterscheidet sich dotin zwei wichtigen Punkten.

  • Die Multiplikation mit Skalaren ist nicht zulässig.
  • Stapel von Matrizen werden zusammen gesendet, als ob die Matrizen Elemente wären.

Der letzte Punkt macht deutlich, dass sich dotund matmulMethoden anders verhalten, wenn 3D-Arrays (oder höherdimensionale Arrays) übergeben werden. Aus der Dokumentation noch etwas zitieren:

Für matmul:

Wenn eines der Argumente ND, N> 2 ist, wird es als ein Stapel von Matrizen behandelt, die sich in den letzten beiden Indizes befinden, und entsprechend gesendet.

Für np.dot:

Für 2-D-Arrays entspricht dies der Matrixmultiplikation und für 1-D-Arrays dem inneren Produkt von Vektoren (ohne komplexe Konjugation). Für N Dimensionen ist es ein Summenprodukt über der letzten Achse von a und der vorletzten von b


13
Die Verwirrung hier ist wahrscheinlich auf die Versionshinweise zurückzuführen, die das "@" - Symbol direkt mit der dot () - Funktion von numpy im Beispielcode gleichsetzen.
Alex K

12

Die Antwort von @ajcr erklärt, wie sich die dotund matmul(durch das @Symbol aufgerufen ) unterscheiden. Wenn man sich ein einfaches Beispiel ansieht, sieht man deutlich, wie sich die beiden unterschiedlich verhalten, wenn man mit „Matrizenstapeln“ oder Tensoren arbeitet.

Um die Unterschiede zu verdeutlichen, nehmen Sie ein 4x4-Array und geben Sie das dotProdukt und das matmulProdukt mit einem 3x4x2-Stapel Matrizen oder Tensor zurück.

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

Die Produkte jeder Operation werden unten angezeigt. Beachten Sie, wie das Punktprodukt ist,

... ein Summenprodukt über der letzten Achse von a und der vorletzten von b

und wie das Matrixprodukt durch gemeinsames Senden der Matrix gebildet wird.

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
Punkt (a, b) [i, j, k, m] = Summe (a [i, j,:] * b [k,:, m]) ------- wie die Dokumentation sagt: es ist a
Summenprodukt

Guter Fang, es ist ein 3x4x2. Eine andere Möglichkeit, die Matrix zu a = np.arange(24).reshape(3, 4, 2)erstellen, besteht darin, ein Array mit den Abmessungen 3x4x2 zu erstellen.
Nathan

8

Nur zu @Ihrer Information , und seine numpy Äquivalente dotund matmulsind alle ungefähr gleich schnell. (Handlung erstellt mit perfplot , einem Projekt von mir.)

Geben Sie hier die Bildbeschreibung ein

Code zur Reproduktion der Handlung:

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

In der Mathematik halte ich den Punkt in numpy für sinnvoller

Punkt (a, b) _ {i, j, k, a, b, c} =Formel

da es das Punktprodukt gibt, wenn a und b Vektoren sind, oder die Matrixmultiplikation, wenn a und b Matrizen sind


Wie für matmul Betrieb in numpy, es besteht aus Teilen des Punktergebnis, und es kann als definiert werden ,

> matmul (a, b) _ {i, j, k, c} =Formel

Sie sehen also, dass matmul (a, b) ein Array mit einer kleinen Form zurückgibt, das einen geringeren Speicherverbrauch aufweist und in Anwendungen sinnvoller ist. Insbesondere in Kombination mit Rundfunk können Sie erhalten

matmul (a, b) _ {i, j, k, l} =Formel

beispielsweise.


Anhand der beiden obigen Definitionen können Sie die Anforderungen für die Verwendung dieser beiden Operationen erkennen. Angenommen, a.shape = (s1, s2, s3, s4) und b.shape = (t1, t2, t3, t4)

  • Um Punkt (a, b) zu verwenden , benötigen Sie

    1. t3 = s4 ;
  • Um matmul (a, b) zu verwenden , benötigen Sie

    1. t3 = s4
    2. t2 = s2 oder einer von t2 und s2 ist 1
    3. t1 = s1 oder einer von t1 und s1 ist 1

Verwenden Sie den folgenden Code, um sich selbst zu überzeugen.

Codebeispiel

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.matmulgibt auch das Punktprodukt auf Vektoren und das Matrixprodukt auf Matrizen an.
Subhaneil Lahiri

2

Hier ist ein Vergleich mit, um np.einsumzu zeigen, wie die Indizes projiziert werden

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

Meine Erfahrung mit MATMUL und DOT

Beim Versuch, MATMUL zu verwenden, wurde ständig "ValueError: Die Form der übergebenen Werte ist (200, 1), Indizes implizieren (200, 3)" angezeigt. Ich wollte eine schnelle Problemumgehung und fand, dass DOT dieselbe Funktionalität bietet. Ich bekomme keine Fehler mit DOT. Ich bekomme die richtige Antwort

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

mit DOT

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

YY.shape

>>> (200, )
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.