2013-08-07 11 views
16

違い、この2つの式は(theorically)同じ結果が得られるはず:numpyの1-Dのnumpyのアレイのドット(b)および(* B).SUM()

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

後者はdot()を使用し、高速です。しかし、どちらがより正確ですか?どうして?

いくつかのコンテキストが続きます。

numpyを使用してサンプルの加重分散を計算したかったのです。 という表現がanother answerに見つかりました。より正確である旨のコメントがありました。しかし説明はありません。

+1

これは加重平均です。 ['np.average'](http://docs.scipy.org/doc/numpy/reference/generated/numpy.average.html)を使うだけでいいかもしれません。 – user2357112

+0

"数値的に正確な"部分は、ドットを使用するのではなく、値から平均を差し引くことを指していたと思います。 – user2357112

答えて

9

ナンシードットは、コンパイル時にリンクするBLASライブラリを呼び出すルーチンの1つです(または独自にビルドします)。 BLASライブラリでは、計算が実行する丸めの回数を制限する乗算累積演算(通常はFused-Multiply Add)を使用できます。

は以下ください:

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

ない正確な、しかし十分に近いです。それはナイーブ(a*a).sum()がないこと約浮動小数点丸め数の半分を使用するように

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

np.dot(a, a)は、二つの、より正確であろう。

Nvidiaの書籍には、4桁の精度の例があります。最寄りの4桁の数字に4ラウンドのrnスタンド:もちろん浮動小数点数の

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 

は、ベース10に16小数点第2位を四捨五入していますが、アイデアを得るされていません。

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

(a*a).sum()であるが:このから

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) 

数を使用して2倍の数倍に丸められていることを確認するために、その簡単にいくつかの追加の擬似コードで上記表記でnp.dot(a,a)を配置

(a*a).sum()と比較してnp.dot(a,a)となります。これらの小さな差は、答えを微妙に変えることができます。追加のexmaplesはhereを見つけることができます。

+5

numpyがユーザマシンに最適化されたblasを使用していて、fmaを持つプロセッサを持っている場合。 "blas * can *はそれに基づいて..."に基づいてあまりにも多くの仮定をするべきではありません。 –

+0

はい、Intel Ivy Bridgeの場合でも 'a + b * c'は' mulss'の後に 'addss'をコンパイルします。 –

+0

適切なコンパイラオプションを追加しましたか? (-march = core-avx2 -mavx -mfma ... gccと同じように) –

関連する問題