16

对于一维 numpy 数组,这两个表达式应该产生相同的结果(理论上):

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

后者使用dot()速度更快。但是哪一个更准确呢?为什么?

一些上下文如下。

我想使用 numpy 计算样本的加权方差。我在另一个答案dot()中找到了该表达式,并附有评论指出它应该更准确。但是那里没有给出解释。

4

1 回答 1

9

Numpy dot 是调用您在编译(或构建自己的)时链接的 BLAS 库的例程之一。这一点的重要性在于 BLAS 库可以利用乘法累加操作(通常是融合乘加),这限制了计算执行的舍入次数。

采取以下措施:

>>> 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)将是两者中更准确的,因为它使用的浮点舍入数大约是天真的算法的一半(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()由此很容易看出,与 相比,该数字被舍入了两倍np.dot(a,a)。这些微小的差异总和可以微小地改变答案。可以在此处找到其他示例。

于 2013-08-07T01:33:20.913 回答