16

1-D numpy 配列の場合、この 2 つの式は (理論的には) 同じ結果をもたらすはずです。

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

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

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

numpy を使用してサンプルの加重分散を計算したかったのです。私は別の回答dot()で表現を見つけ、より正確であるべきであるというコメントを付けました。しかし、そこには何の説明もありません。

4

1 に答える 1

9

Numpy dot は、コンパイル時にリンクする (または独自にビルドする) 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=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)約半分の数を使用するため、2 つのうちより正確になります(a*a).sum()

Nvidia の本には、4 桁の精度の次の例があります。rnは、最も近い 4 桁に四捨五入することを表します。

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 位に丸められませんが、おわかりでしょう。

np.dot(a,a)上記の表記法に追加の疑似コードを配置すると、次のようになります。

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)

(a*a).sum()このことから、と比較してを使用すると、数値が 2 倍丸められることが簡単にわかりnp.dot(a,a)ます。これらの小さな違いを合計すると、答えが微妙に変わる可能性があります。追加の例はここにあります。

于 2013-08-07T01:33:20.913 に答える