关于python:小数点对对称乘法太聪明了

Numpy dot too clever about symmetric multiplications

有人知道有关此行为的文档吗?

1
2
3
4
5
6
7
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对于对称产品很聪明,可以节省翻牌并确保对称输出...

但是我处理的是混乱的系统,调试时这种小的差异很快就变得很明显。所以我想确切地知道发生了什么。


此行为是在请求请求#6932中为NumPy 1.11.0引入的更改的结果。从1.11.0的发行说明中:

Previously, gemm BLAS operations were used for all matrix products.
Now, if the matrix product is between a matrix and its transpose, it
will use syrk BLAS operations for a performance boost. This
optimization has been extended to @, numpy.dot, numpy.inner, and
numpy.matmul.

在该PR的更改中,找到了以下注释:

1
2
3
4
/*
 * 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数组只是原始数组上的视图,具有颠倒的形状和跨度,因此只需检查数组中的一些元数据(而不是必须比较实际的数组数据)。

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

1
2
3
4
5
6
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())

我猜想这种特殊shell的一个优势,除了明显的提速潜力外,还可以保证(我希望,但实际上,这将取决于BLAS的实现),以获得完美的性能。使用syrk时得到对称结果,而不是仅根据数值误差而对称的矩阵。作为对此(当然不是很好)的测试,我尝试了:

1
2
3
4
5
6
7
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())

我的机器上的结果:

1
2
Sym1 symmetric:  True
Sym2 symmetric:  False


我怀疑这与将中间浮点寄存器提升到80位精度有关。某种程度上证实了这一假设的是,如果我们使用更少的浮点数,我们的结果将始终为0,ala

1
2
3
4
5
6
7
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)