【问题标题】:Numpy dot too clever about symmetric multiplicationsNumpy dot 对对称乘法太聪明了
【发布时间】:2017-09-13 04:51:54
【问题描述】:

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

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 对对称产品很聪明,以节省失败并确保对称输出......

但我处理的是混沌系统,并且当调试时,这个小差异很快就会变得明显。所以我想知道到底发生了什么。

【问题讨论】:

  • 由于您的代码会在每次运行时提供不同的输出,请显示一个示例输出并更清楚地说明该输出的不良之处。
  • 您是否尝试强制使用双精度数 (np.float64)?
  • @TomdeGeus 怎么样?无论如何,请注意,我并不真正关心差异是非零的。我只想解释这种行为(这显然来自 numpy 的聪明)。
  • 您可以在Aw 的定义上使用.astype(np.float64)。顺便说一句,根据 NumPy,我机器上的机器精度是 print(np.finfo(np.float64).eps) = 2.2e-16。而diff.max() = 1.1e-16,即在机器精度范围内。
  • 使用B=Aw.T.copy()有什么区别?

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


【解决方案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)

【讨论】:

  • 这是可能的。但我认为你的实验也可以用我的建议来解释:numpy 以不同的方式对待对称产品。我们怎么能确定呢?
  • 我们可以确定这不是原因,因为 python 中间值永远不会存储 80 位精度,因为它们存储在内存中而不是寄存器中。这里重要的是 A*w 被计算了两次,而不是它是否作为更大表达式的一部分计算
  • @Patrick。最可靠的方法是阅读源代码。
【解决方案2】:

此行为是在 pull request #6932 中为 NumPy 1.11.0 引入的更改的结果。来自release notes 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 数组只是原始数组的视图,具有倒置的形状和步幅,因此检查数组上的一些元数据就足够了(而不必比较实际的数组数据)。

这里有一个稍微简单的案例来显示差异。请注意,在 dot 的参数之一上使用 .copy 足以击败 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

【讨论】:

  • A transpose 是一个 view,具有连续性翻转(F 连续)和跨步。所以它可以通过只比较几个属性来识别,而不需要查看任何数据(这会很昂贵)。
  • 刚刚发现此更改记录在发行说明中,至少。答案已更新。
猜你喜欢
  • 2011-10-29
  • 2010-10-29
  • 1970-01-01
  • 2021-06-29
  • 2017-05-25
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
相关资源
最近更新 更多