我环顾四周寻找一些关于numpy / scipy函数在数值稳定性方面表现如何的文档,例如:是否有任何改善数值稳定性的方法,或者是否存在其他稳定的实施方式。
我特别感兴趣的是浮点数组{(1}},+
和numpy.sum()
的加法(numpy.cumsum()
运算符)。在所有情况下,我基本上总结了大量的浮点数,我担心这种计算的准确性。
是否有人知道在numpy / scipy文档或其他来源中对此类问题的任何引用?
答案 0 :(得分:2)
短语“稳定性”是指算法。如果你的算法开始时不稳定,那么提高精度或减少组件步骤的舍入误差就不会有太大的好处。
更复杂的numpy例程,如“solve”,是ATLAS / BLAS / LAPACK例程的包装器。您可以参考那里的文档,例如“dgesv”使用具有部分旋转和行交换的LU分解来求解实值线性方程组:LAPACK的基础Fortran代码文档可以在这里看到http://www.netlib.org/lapack/explore-html/但是{{3指出许多不同版本的标准例程实现都可用 - 速度优化和精度会因它们而异。
您的示例不会引入太多的舍入,“+”没有不必要的舍入,当较小的数字具有无法在答案中表示的低位时,精度完全取决于浮点数据类型中隐式的舍入。求和点仅取决于评估顺序。 Cumsum在输出数组时不能轻易重新排序。
对于“cumsum”或“dot”函数中的累积舍入,您可以选择:
在Linux 64bit上,numpy提供了对高精度“long double”类型float128的访问,您可以使用它来降低中间计算中的精度损失,但代价是性能和内存。
然而,在我的Win64安装“numpy.longdouble”映射到“numpy.float64”一个普通的C double类型,所以你的代码不是跨平台的,请检查“finfo”。 (Canopy Express Win64上不存在具有真正更高精度的float96或float128)
log2(finfo(float64).resolution)
> -49.828921423310433
actually 53-bits of mantissa internally ~ 16 significant decimal figures
log2(finfo(float32).resolution)
> -19.931568 # ~ only 7 meaningful digits
由于sum()
和dot()
将数组减少为单个值,因此使用内置函数可以轻松实现最大化精度:
x = arange(1, 1000000, dtype = float32)
y = map(lambda z : float32(1.0/z), arange(1, 1000000))
sum(x) # 4.9994036e+11
sum(x, dtype = float64) # 499999500000.0
sum(y) # 14.357357
sum(y, dtype = float64) # 14.392725788474309
dot(x,y) # 999999.0
einsum('i,i', x, y) # * dot product = 999999.0
einsum('i,i', x, y, dtype = float64) # 999999.00003965141
优化舍入取决于你加入的东西 - 首先添加许多小数字可以帮助延迟舍入,但不会避免大数字存在但由于中间计算仍然导致精度损失而相互抵消的问题
示例显示评估顺序依赖...
x = array([ 1., 2e-15, 8e-15 , -0.7, -0.3], dtype=float32)
# evaluates to
# array([ 1.00000000e+00, 2.00000001e-15, 8.00000003e-15,
# -6.99999988e-01, -3.00000012e-01], dtype=float32)
sum(x) # 0
sum(x,dtype=float64) # 9.9920072216264089e-15
sum(random.permutation(x)) # gives 9.9999998e-15 / 2e-15 / 0.0