Numpy 浮点非决定论的原因?包括努比?

Numpy 浮点非决定论的原因?包括努比?,numpy,blas,intel-mkl,atlas,non-deterministic,Numpy,Blas,Intel Mkl,Atlas,Non Deterministic,IEEE浮点运算是确定性的,但整体浮点计算可能是非确定性的,请参见: 。。。就浮点计算的执行顺序而言,并行计算是不确定的,这可能导致跨运行的非位精确结果 问题分两部分: 除此之外,整个浮点计算怎么可能是非确定性的,产生不完全相等的结果 考虑一个调用NumPy、CVXOPT和SciPy子例程(如SciPy.optimize.fsolve())的单线程Python程序,这些子例程依次调用本机库(如MINPACK和GLPK)和优化线性代数子例程(如BLAS、ATLAS和MKL)。“” 这些本机库是否

IEEE浮点运算是确定性的,但整体浮点计算可能是非确定性的,请参见:

。。。就浮点计算的执行顺序而言,并行计算是不确定的,这可能导致跨运行的非位精确结果

问题分两部分:

  • 除此之外,整个浮点计算怎么可能是非确定性的,产生不完全相等的结果
  • 考虑一个调用NumPy、CVXOPT和SciPy子例程(如
    SciPy.optimize.fsolve()
    )的单线程Python程序,这些子例程依次调用本机库(如MINPACK和GLPK)和优化线性代数子例程(如BLAS、ATLAS和MKL)。“”

    这些本机库是否以引入非确定性结果的方式进行过并行化

假设:

  • 相同的软件,相同的输入,在相同的硬件上。多次运行的输出应相等。
    • 如果这是可行的,那么最好测试代码重构后的输出是否相等。(是的,对操作顺序的某些更改可能导致某些输出不相等。)
  • 程序中的所有随机数都是psuedo随机数,在所有运行中以一致的方式使用相同的种子
  • 没有未初始化的值。Python通常以这种方式是安全的,但是
    numpy.empty()
    在不初始化条目的情况下返回一个新数组。现在还不清楚它在实践中是否快得多。所以当心

    • @PaulPanzer的测试表明,
      numpy.empty()
      确实返回未初始化的数组,并且它可以轻松快速地回收最近的数组:

      将numpy导入为np
      阿兰奇(100);np.空(100,int);np.empty(100,int)
      np.arange(100200.0);np.空(100,浮动);np.空(100,浮动)
      

    • 为这些例程获取有用的计时度量是很棘手的!在
      timeit
      循环中,
      numpy.empty()
      可以继续重新分配相同的一个或两个内存节点。时间与数组大小无关。为防止循环再造:

      从timeit导入timeit
      timeit('l.append(numpy.empty(100000))','import numpy;l=[]))
      timeit('l.append(numpy.zeros(100000)),'import numpy;l=[]))
      

      但是将数组大小减少到
      numpy.zero(10000)
      需要15倍的时间;将其减少到
      numpy.zero(1000)
      需要1.3倍的时间(在我的MBP上)。令人费解

另请参见: . 这可能会改变不同运行的操作顺序。[我正在Python 2.7.15中讨论这个问题。]

我发现我遇到的大多数(并非全部)非确定性问题似乎在OpenBLAS 0.3.5的代码中得到了修复

早期版本的OpenBLAS中存在大量线程问题,但该版本有一个macOS兼容性错误,该错误在0.3.5版的代码中已修复。苹果的Accelerate framework 1.1版和英特尔的MKL
MKL==2019.0
也会出现这些漏洞

也许我遇到的其余问题是由于其他链接到Accelerate的库造成的


注意:对于这个问题,我仍然有更多的答案。

你可能会感兴趣,而且“在我的测量中,空()大约需要零()[…]的时间”,这要视情况而定。在频繁的dealloc/realloc(例如简单的
timeit
empty
可以比
零快几个数量级。
empty
有一些有效的用法,排列的反转只是一个例子,只是一个简短的轶事。最近,我遇到了这个确切的问题,一些代码正在用C语言进行确定性最小化。在我认为应该只是一个表面上的改变之后,我得到了稍微不同的结果。原来问题是我把一行如
a*=b/c
改成了
a=a*b/c
,结果略有不同。由于这导致了一个稍有不同的结果,它导致最小值尝试了不同的下一个点,结果滚雪球般地扩大,因此我在两次提交之间得到了明显不同的结果。@palpanzer结果是
numpy.empty()
正在将我的MBP上的数组归零。这就解释了时间的相似性。也许这是NumPy调用Apple的Accelerate框架的结果,该框架是默认框架,而不是openblas。我将用MKL重新测试。我的猜测是,
zeros
调用
calloc
calloc
作为决定是否回收或请求新内存的人,确实会知道它是否必须自己进行调零,或者可以假设已经进行了调零。请注意,我不是受过训练的计算机科学家。