Numpy dot对于对称乘法太聪明了

时间:2017-04-17 14:46:28

标签: python numpy floating-point linear-algebra floating-accuracy

有人知道这种行为的文档吗?

import numpy as np
A  = np.random.uniform(0,1,(10,5))
w  = np.ones(5)
Aw = A*w
Sym1 = Aw.dot(Aw.T)
Sym2 = (A*w).dot((A*w).T)
diff = Sym1 - Sym2

diff.max()接近机器精度非零,例如4.4E-16

这(与0的差异)通常很好......在有限精度的世界里,我们不应该感到惊讶。

此外,我猜想numpy对于对称产品很聪明,可以节省触发器并确保对称输出......

但是我处理混乱的系统,当调试时,这种小的差异很快变得明显。因此,我想知道究竟发生了什么。

2 个答案:

答案 0 :(得分:21)

此行为是在pull request #6932中为NumPy 1.11.0引入的更改的结果。来自release notes for 1.11.0:

  

以前,gemm BLAS操作用于所有基质产品。   现在,如果矩阵乘积在矩阵和它的转置之间,那么   将使用syrk BLAS操作来提升性能。这个   优化已扩展到@,numpy.dot,numpy.inner和   numpy.matmul。

在该PR的更改中,找到this comment

/*
 * Use syrk if we have a case of a matrix times its transpose.
 * Otherwise, use gemm for all other cases.
 */

因此NumPy正在明确检查矩阵的时间及其转置,并在这种情况下调用不同的底层BLAS函数。正如@hpaulj在评论中指出的那样,这样的检查对于NumPy来说是便宜的,因为转置的2d数组只是原始数组上的视图,具有反转的形状和步幅,因此足以检查数组上的几个元数据(而不是必须比较实际的数组数据。)

这是一个稍微简单的案例,显示了这种差异。请注意,在.copy的其中一个参数上使用dot足以击败NumPy的特殊套管。

import numpy as np
random = np.random.RandomState(12345)
A = random.uniform(size=(10, 5))
Sym1 = A.dot(A.T)
Sym2 = A.dot(A.T.copy())
print(abs(Sym1 - Sym2).max())

我认为这种特殊外壳的一个优点是,除了显而易见的加速潜力之外,还有你的保证(我希望,但在实践中它依赖于BLAS)实现)在使用syrk时获得完全对称的结果,而不是仅仅对称于数值误差的矩阵。作为一个(不可否认的不是很好)测试,我尝试了:

import numpy as np
random = np.random.RandomState(12345)
A = random.uniform(size=(100, 50))
Sym1 = A.dot(A.T)
Sym2 = A.dot(A.T.copy())
print("Sym1 symmetric: ", (Sym1 == Sym1.T).all())
print("Sym2 symmetric: ", (Sym2 == Sym2.T).all())

我的机器上的结果:

Sym1 symmetric:  True
Sym2 symmetric:  False

答案 1 :(得分:1)

我怀疑这与将中间浮点寄存器提升到80位精度有关。有点证实这个假设是,如果我们使用更少的浮点数,我们的结果中一直得到0,ala

A  = np.random.uniform(0,1,(4,2))
w  = np.ones(2)
Aw = A*w
Sym1 = Aw.dot(Aw.T)
Sym2 = (A*w).dot((A*w).T)
diff = Sym1 - Sym2
# diff is all 0's (ymmv)