【问题标题】:Theano sqrt returning NaN valuesTheano sqrt 返回 NaN 值
【发布时间】:2015-08-10 12:39:20
【问题描述】:

在我的代码中,我使用 theano 来计算欧几里得距离矩阵(代码来自 here):

import theano
import theano.tensor as T
MAT = T.fmatrix('MAT')
squared_euclidean_distances = (MAT ** 2).sum(1).reshape((MAT.shape[0], 1)) + (MAT ** 2).sum(1).reshape((1, MAT.shape[0])) - 2 * MAT.dot(MAT.T)
f_euclidean = theano.function([MAT], T.sqrt(squared_euclidean_distances))
def pdist_euclidean(mat):
    return f_euclidean(mat)

但是下面的代码导致矩阵的一些值是NaN。我读到在计算theano.tensor.sqrt()here 时会发生这种情况,建议

在 sqrt(或 max(x,EPs))内添加一个 eps

所以我在我的代码中添加了一个 eps:

import theano
import theano.tensor as T

eps = 1e-9

MAT = T.fmatrix('MAT')

squared_euclidean_distances = (MAT ** 2).sum(1).reshape((MAT.shape[0], 1)) + (MAT ** 2).sum(1).reshape((1, MAT.shape[0])) - 2 * MAT.dot(MAT.T)

f_euclidean = theano.function([MAT], T.sqrt(eps+squared_euclidean_distances))

def pdist_euclidean(mat):
    return f_euclidean(mat)

我在执行sqrt 之前添加它。我得到的NaNs 越来越少,但我仍然得到它们。问题的正确解决方案是什么?我还注意到,如果 MATT.dmatrix() 则没有 NaN

【问题讨论】:

  • 添加负值检查并打印数据,以便追踪它们的来源。
  • @stark 我在squared_euclidean_distances 中有负值,我应该使用T.abs_ 吗?
  • 没有。你应该找出你的函数出了什么问题。
  • @stark 应该没问题,我是从stackoverflow.com/questions/25886374/pdist-for-theano-tensor 拿的,用dmatrix 代替fmatrix 没有NaN
  • abs 仍然没有意义。如果为负数,只需使用 0.0。

标签: python theano


【解决方案1】:

在计算欧几里得距离时,有两个可能的 NaN 来源。

  1. 浮点表示近似问题导致负距离实际上只是零。负数的平方根未定义(假设您对复杂解不感兴趣)。

    想象MAT 有值

    [[ 1.62434536 -0.61175641 -0.52817175 -1.07296862  0.86540763]
     [-2.3015387   1.74481176 -0.7612069   0.3190391  -0.24937038]
     [ 1.46210794 -2.06014071 -0.3224172  -0.38405435  1.13376944]
     [-1.09989127 -0.17242821 -0.87785842  0.04221375  0.58281521]]
    

    现在,如果我们分解计算,我们会发现 (MAT ** 2).sum(1).reshape((MAT.shape[0], 1)) + (MAT ** 2).sum(1).reshape((1, MAT.shape[0])) 具有价值

    [[ 10.3838024   -9.92394296  10.39763039  -1.51676099]
     [ -9.92394296  18.16971188 -14.23897281   5.53390084]
     [ 10.39763039 -14.23897281  15.83764622  -0.65066204]
     [ -1.51676099   5.53390084  -0.65066204   4.70316652]]
    

    2 * MAT.dot(MAT.T) 有价值

    [[ 10.3838024   14.27675714  13.11072431   7.54348446]
     [ 14.27675714  18.16971188  17.00367905  11.4364392 ]
     [ 13.11072431  17.00367905  15.83764622  10.27040637]
     [  7.54348446  11.4364392   10.27040637   4.70316652]]
    

    这两个值的对角线应该相等(向量与其自身之间的距离为零),从这个文本表示看来这是真的,但实际上它们略有不同——差异太小,无法当我们像这样打印浮点值时显示出来

    当我们打印完整表达式的值时,这一点变得很明显(上面的第二个矩阵从第一个矩阵中减去)

    [[  0.00000000e+00   2.42007001e+01   2.71309392e+00   9.06024545e+00]
     [  2.42007001e+01  -7.10542736e-15   3.12426519e+01   5.90253836e+00]
     [  2.71309392e+00   3.12426519e+01   0.00000000e+00   1.09210684e+01]
     [  9.06024545e+00   5.90253836e+00   1.09210684e+01   0.00000000e+00]]
    

    对角线几乎由零组成,但第二行第二列中的项目现在是一个非常小的负值。然后,当您计算所有这些值的平方根时,您会在该位置得到NaN,因为负数的平方根是未定义的(对于实数)。

    [[ 0.          4.91942071  1.64714721  3.01002416]
     [ 4.91942071         nan  5.58951267  2.42951402]
     [ 1.64714721  5.58951267  0.          3.30470398]
     [ 3.01002416  2.42951402  3.30470398  0.        ]]
    
  2. 计算欧几里得距离表达式相对于函数输入内部变量的梯度。这不仅会发生在由于浮点近似而产生负数的情况下,如上所述,而且如果任何输入的长度为零。

    如果y = sqrt(x)dy/dx = 1/(2 * sqrt(x))。因此,如果 x=0 或出于您的目的,如果 squared_euclidean_distances=0 则渐变将是 NaN 因为 2 * sqrt(0) = 0 并且除以零是未定义的。

第一个问题的解决方案可以通过强制平方距离不小于零来确保平方距离永远不会为负:

T.sqrt(T.maximum(squared_euclidean_distances, 0.))

要解决这两个问题(如果您需要渐变),那么您需要确保平方距离永远不会是负数或零,因此必须使用一个小的正 epsilon:

T.sqrt(T.maximum(squared_euclidean_distances, eps))

第一个解决方案是有意义的,因为问题仅来自近似表示。第二个更值得怀疑,因为真实距离为零,所以从某种意义上说,梯度应该是未定义的。您的特定用例可能会产生一些替代解决方案,该解决方案在没有人为限制的情况下保持语义(例如,通过确保从不计算/用于零长度向量的梯度)。但是NaN 值可能是有害的:它们可以像杂草一样蔓延。

【讨论】:

  • "T.sqrt(T.maximum(squared_euclidean_distances, eps))" 为我解决了这个问题。交易
【解决方案2】:

只是检查

squared_euclidian_distances 中,您要添加一列、一行和一个矩阵。你确定这是你想要的吗?

更准确地说,如果 MAT 的形状为 (n, p),那么您将添加形状为 (n, 1)、(1, n) 和 (n, n) 的矩阵。

Theano 似乎默默地重复每个一维成员的行(分别是列)以匹配点积的行数和列数。

如果这是你想要的

在reshape中,你可能应该根据basic tensor functionality : reshape指定ndim=2

如果形状是一个变量参数,那么您可能需要使用可选的 ndim 参数来声明该形状有多少元素,因此重新调整的变量将有多少维。

此外,squared_euclidean_distances 似乎应该始终为正,除非差异中的不精确误差将零值变为小的负值。如果这是真的,并且如果负值是您看到的 NaN 的原因,那么您确实可以通过将 squared_euclidean_distancesabs(...) 括起来来摆脱它们而不会破坏您的结果。

【讨论】:

猜你喜欢
  • 2012-03-25
  • 2018-04-29
  • 2019-06-20
  • 1970-01-01
  • 2020-05-27
  • 2014-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多