【问题标题】:Efficient element-wise matrix division when elements in denominator may be zero当分母中的元素可能为零时,有效的逐元素矩阵除法
【发布时间】:2014-05-27 07:28:41
【问题描述】:

我正在使用 numpy 使用 Python 2.7.6 进行编程。我在两个 numpy 矩阵 V/np.dot(W,H) 之间进行了划分。有时会发生分母的某些单元格值等于 0,所以我得到一个运行时错误。我想以有效的方式实施安全划分。如何编写执行矩阵除法的代码,并且对于分母等于 0 的元素将 0 放入输出矩阵?

【问题讨论】:

  • 先计算分母,做你需要做的任何检查,并使用 if 处理不同的情况?
  • 例如:假设V=[[1 2 3][4 5 6]]np.dot(W,H)=[[1 0 3][0 5 6]]。我想得到[[1 0 1][0 1 1]]。我不知道如何处理 seterr。

标签: python performance numpy matrix division


【解决方案1】:

Numpy 实际上允许您设置在除以零错误的情况下想要执行的操作 - 请参阅 seterr。不过,我相信这是一个全局标志 - 我不知道更本地化的解决方案 - 如果这是一个问题,我想你可以在安全划分之前和之后设置 seterr。

【讨论】:

  • seterr 设置为 ignore 会将除以零元素设置为 inf。如果您可以根据 OP 的问题添加一个将这些值更改为零的 sn-p,那就太好了。
  • @Daryl seterr 是全局性的,但 errstate 是一个上下文管理器,可让您为代码补丁设置相同的内容。
【解决方案2】:

只需搜索分母中为零的元素并将其替换为infinity

D = np.dot(W,H)
D[D==0] = float('inf')
result = V / D

这种方法比不使用D[D==0] = float('inf') 检查零的普通result = V / D 慢,但随着矩阵大小的增加它会变得更好。使用 30x30 矩阵需要三倍的时间,使用 250x250 矩阵需要两倍的时间,随着 n 的进一步增加,它接近 1.8 倍。而且它似乎比根据 Daryl's answerAdrian's answer 更改处理浮点异常的方式快 10% 左右。

要记住的一点是,对于浮点数和缺乏精度,分母中的元素可能应该为零但不完全,并且很容易将其合并如下

epsilon = 1e-8
D[np.abs(D)<epsilon] = float('inf')

【讨论】:

  • np.place(D, D==0, float('inf'))D[D==0] = float('inf') 快吗?
  • @Adrian Ratnapala 好点!它的内容不多,但如果有的话,D[D==0] 的方式会快一点。这当然更简单,我会改变我的答案:谢谢。
  • 我只是想知道为什么.place 存在。我想它可能是“真正的”方法,并且运算符重载只是语法糖,只要索引是布尔数组,它就会调用.place
  • @AdrianRatnapala 它允许您将一系列值分配给数组中不连续的一组元素(如有必要,循环遍历这些值)。它比设置单个值更通用——我不应该为此使用它。
  • 我认为您也可以使用二进制数组下标来做到这一点。至少你可以使用 MATLAB,我可能会对 numpy 做出毫无根据的假设。
【解决方案3】:

虽然你说的是“矩阵”,但我假设你真的想要数组,因为你想要元素方面 分配。我只会在抑制 div0 错误。然后我会修复结果。

# Assume V and D are arrays of the same shape
with np.errstate(divide='ignore'): 
    # division errors suppressed only within this block
    quot = V / D
    quot[D == 0] = 0

我的直觉告诉我这很快,因为它主要将数据保持在其原始形状。但我从未将它与其他方法进行比较。

【讨论】:

  • 这是一个很好的方法,因为它解决了全局seterr 问题。尽管如此,在我的测试中,这种方法(以及在全球范围内使用seterr)比我的答案中的方法慢10%多一点(包括按照您的建议将np.place 替换为D[D==0]=)。我的猜测是浮点异常仍然必须在硬件中被捕获和丢弃。
  • @TooTone 我怀疑您对设置 D 更快的原因是正确的。更重要的区别是我的解决方案保持 D 不变,而您的解决方案将其纠正为与最终结果一致。取决于应用程序,任何一种方法都可能更好。
  • 这是一个很好的观点。在这种情况下,可以更改D,因为它是由dot 计算的临时值。但总的来说不会。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 2014-11-13
  • 2018-12-20
  • 1970-01-01
  • 2016-06-04
  • 2016-08-11
  • 1970-01-01
相关资源
最近更新 更多