2013-08-07 12 views
16

Für 1-D numpy Arrays, diese beiden Ausdrücke sollte das gleiche Ergebnis (theorically) Ausbeute:Numpy: Differenz zwischen dot (a, b) und (a * b) .sum()

(a*b).sum()/a.sum() 
dot(a, b)/a.sum() 

The Letzteres verwendet dot() und ist schneller. Aber welcher ist genauer? Warum?

Einige Kontext folgt.

Ich wollte die gewichtete Varianz einer Probe berechnen numpy verwenden. Ich fand den dot() Ausdruck in another answer, mit einem Kommentar besagt, dass es genauer sein sollte. Dort wird jedoch keine Erklärung gegeben.

+1

Dies ist ein gewichteter Durchschnitt, nicht wahr? Sie können einfach nur ['np.average'] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.average.html) verwenden. – user2357112

+0

Ich denke, der Teil über "numerisch präzise" bezog sich auf die Subtraktion des Durchschnitts von den Werten, anstatt "Punkt" zu verwenden. – user2357112

Antwort

9

Numpy dot ist einer der Routinen, die die BLAS-Bibliothek aufruft, die Sie auf der Kompilierung verknüpfen (oder baut seine eigene). Die Bedeutung davon ist, dass die BLAS-Bibliothek Multiplikations-Akkumulationsoperationen (normalerweise Fused-Multiply Add) verwenden kann, die die Anzahl der Rundungen begrenzen, die die Berechnung durchführt.

Nehmen Sie folgendes:

>>> a=np.ones(1000,dtype=np.float128)+1E-14 
>>> (a*a).sum() 
1000.0000000000199948 
>>> np.dot(a,a) 
1000.0000000000199948 

Nicht genau, aber nahe genug.

>>> a=np.ones(1000,dtype=np.float64)+1E-14 
>>> np.dot(a,a) 
1000.0000000000176 #off by 2.3948e-12 
>>> (a*a).sum() 
1000.0000000000059 #off by 1.40948e-11 

The np.dot(a, a) die genauere der beiden sein, wie es etwa die Hälfte der Anzahl von Gleitkomma Abrundungen verwenden, die die naive (a*a).sum() tut.

Ein Buch von Nvidia hat das folgende Beispiel für 4 Ziffern der Präzision. rn steht für 4 Runde auf die nächsten 4 Ziffern:

x = 1.0008 
x2 = 1.00160064     # true value 
rn(x2 − 1) = 1.6006 × 10−4   # fused multiply-add 
rn(rn(x2) − 1) = 1.6000 × 10−4  # multiply, then add 

Natürlich Gleitkommazahlen sind nicht auf dem 16. Dezimalstelle in der Basis 10, abgerundet, aber Sie bekommen die Idee.

Platzierung np.dot(a,a) in der obigen Notation mit einigen zusätzlichen Pseudocode:

out=0 
for x in a: 
    out=rn(x*x+out) #Fused multiply add 

Während (a*a).sum() ist:

arr=np.zeros(a.shape[0]) 
for x in range(len(arr)): 
    arr[x]=rn(a[x]*a[x]) 

out=0 
for x in arr: 
    out=rn(x+out) 

Daraus seine leicht zu sehen, dass die Zahl oft doppelt so gerundet verwendet (a*a).sum() im Vergleich zu np.dot(a,a). Diese kleinen addierten Differenzen können die Antwort geringfügig ändern. Weitere Beispiele finden Sie unter here.

+5

Wenn numpy eine optimierte blas für die Benutzermaschine verwendet, und er hat einen Prozessor, der fma hat. Man sollte nicht zu viele Annahmen machen, die auf dem "blas * kann * das tun ..." beruhen. –

+0

Ja, sogar für Intel Ivy Bridge 'a + b * c' kompiliert zu' mulss' gefolgt von 'addss'. –

+0

Haben Sie angemessene Compiler-Optionen hinzugefügt? (wie -march = core-avx2 -mavx -mfma ... mit gcc) –