TensorFlow NaN 错误?

Posted

技术标签:

【中文标题】TensorFlow NaN 错误?【英文标题】:Tensorflow NaN bug? 【发布时间】:2016-02-16 04:21:56 【问题描述】:

我正在使用 TensorFlow,我修改了 tutorial 示例以获取我的 RGB 图像。

该算法在新图像集上开箱即用地完美运行,直到突然(仍在收敛,通常准确率约为 92%),它因 ReluGrad 收到非有限值的错误而崩溃。调试表明数字没有发生任何异常,直到非常突然,由于未知原因,抛出错误。添加

print "max W vales: %g %g %g %g"%(tf.reduce_max(tf.abs(W_conv1)).eval(),tf.reduce_max(tf.abs(W_conv2)).eval(),tf.reduce_max(tf.abs(W_fc1)).eval(),tf.reduce_max(tf.abs(W_fc2)).eval())
print "max b vales: %g %g %g %g"%(tf.reduce_max(tf.abs(b_conv1)).eval(),tf.reduce_max(tf.abs(b_conv2)).eval(),tf.reduce_max(tf.abs(b_fc1)).eval(),tf.reduce_max(tf.abs(b_fc2)).eval())

作为每个循环的调试代码,产生以下输出:

Step 8600
max W vales: 0.759422 0.295087 0.344725 0.583884
max b vales: 0.110509 0.111748 0.115327 0.124324
Step 8601
max W vales: 0.75947 0.295084 0.344723 0.583893
max b vales: 0.110516 0.111753 0.115322 0.124332
Step 8602
max W vales: 0.759521 0.295101 0.34472 0.5839
max b vales: 0.110521 0.111747 0.115312 0.124365
Step 8603
max W vales: -3.40282e+38 -3.40282e+38 -3.40282e+38 -3.40282e+38
max b vales: -3.40282e+38 -3.40282e+38 -3.40282e+38 -3.40282e+38

由于我的值都不是很高,因此发生 NaN 的唯一方法是处理不当的 0/0,但由于本教程代码不执行任何除法或类似操作,除此之外我看不到其他解释这来自内部 TF 代码。

我不知道该怎么做。有什么建议?该算法收敛得很好,它在我的验证集上的准确率稳步攀升,在迭代 8600 时刚刚达到 92.5%。

【问题讨论】:

【参考方案1】:

tf.log(y_conv)中,如果y_conv是sigmoid激活函数的输出,有更好的方法来计算tf.log(y_conv)

y_conv = sigmoid(x)。那么,

   log(y_conv) = log(sigmoid(x))
=  log(1 / (1 + exp(-x)))
=  log(1 / (1 + exp(-x))) - x + x =
= -log(1 + exp(-x)) - log(exp(x)) + x =
= -log(1 + exp(x)) + x
=  x - softplus(x)

【讨论】:

【参考方案2】:

另一种选择是使用tf.math.xlogy 函数。功能说明说 “如果 x == 0,则返回 0,否则返回 x * log(y),按元素计算。” 你可以在这里找到文档:https://www.tensorflow.org/api_docs/python/tf/math/xlogy

【讨论】:

【参考方案3】:

2.0 兼容答案:从

迁移@user1111929 的答案的代码

Tensorflow 1.xTensorflow 2.x,如下图:

Tensorflow 1.x

cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))

张量流 2.x

cross_entropy = -tf.compat.v2.reduce_sum(y_*tf.log(tf.compat.v2.clip_by_value(y_conv,1e-10,1.0)))

cross_entropy = -tf.compat.v2.math.reduce_sum(y_*tf.log(tf.compat.v1.clip_by_value(y_conv,1e-10,1.0)))

【讨论】:

【参考方案4】:

我一直在使用 Tensorflow Estimator,我认为它可以解决除以零和其他数值稳定性问题,并且偶尔会出现此错误 (ERROR:tensorflow:Model diverged with loss = NaN during training)。大多数时候我得到这个是因为我的输入包括nans。所以:确保您的输入数据框(或您使用的任何东西)没有隐藏在其中某处的 NaN 值。

【讨论】:

【参考方案5】:

我将在这里添加我之前的 NaN 问题之一。我使用 sigmoid 函数作为网络最后一层的激活。但是,sigmoid 激活函数使用指数函数来计算,我得到了一些非常大的数字进入 sigmoid。

这导致了无限梯度,并且开始出现一些 NaN。

【讨论】:

【参考方案6】:

有时你在使用tf.sqrt() 函数时没有在其中添加一个小常量1e-10,从而引发nan 问题。

【讨论】:

sqrt 在 0 处的导数是无限的,这可能会导致不稳定。 它也可以是“隐藏的”:我使用tf.math.reduce_euclidean_norm 计算真正的范数(原文如此),而不是通常用于训练的平方...【参考方案7】:

除了上面所有的好答案,我会添加我的。这是一种不太常见的情况,但确实会导致 NaN:除以零

在我的 NLP 任务网络中,有一个层执行平均池。也就是说,每个数据都是一个令牌序列。我的层做了一些标记嵌入,然后计算嵌入向量的平均值。

平均计算编码为

tf.reduce_sum(embedded)/tf.reduce_sum(tf.not_equal(input, pad)) 

这里的pad 是我在批处理中使用的一些虚拟令牌。

现在,如果某些数据包含空标记列表(无论出于何种原因),它的长度(上面代码 sn-p 中的分母)将为 0。然后它会导致 除以零问题和NaN 将保留在以下所有层/优化步骤中。

如果有人遇到这个问题,我使用tf.where 来平滑这些长度:

sum_embedding = tf.reduce_sum(embedded, 1)
embedding_length = tf.reduce_sum(tf.cast(tf.not_equal(input, pad), dtype=tf.float32), axis=1, keep_dims=True)
embedding_length_smoothed = tf.where(tf.greater(embedding_length, 0.0), embedding_length, tf.ones(tf.shape(embedding_length)))
avg_embedding = sum_embedding / embedding_length_smoothed

本质上,这会将所有长度为 0 的标记列表的数据视为长度为 1,并避免 NaN 问题。

【讨论】:

【参考方案8】:

在标准的前馈网络上工作时,我有时会得到 nans,但有时却没有。我之前使用过类似的 TensorFlow 代码,效果很好。

原来是我无意中导入了变量名。因此,一旦批量选择第一行(变量名称),nan loss 就开始了。或许可以留意一下?

【讨论】:

【参考方案9】:

我将 LSTM 用于长序列并获得了 nan 梯度。这些答案都没有帮助我。但我想出了三个自己的解决方案。我希望它们对通过谷歌搜索来到这里的其他人有用。

    渐变裁剪对我没有帮助,因为渐变在一批更新中变成了 nan。在这种情况下,您可以使用以下行将 nan 替换为 0:

    opt = tf.train.AdamOptimizer(args.lr)
    grads = opt.compute_gradients(loss)
    grads2 = [(tf.where(tf.is_nan(grad), tf.zeros(grad.shape), grad), var) for grad, var in grads]
    opt_op = opt.apply_gradients(grads2)
    

    如果您想跟踪是否出现了 nans,可以使用以下代码:

    was_nan = tf.reduce_any(tf.convert_to_tensor([tf.reduce_any(tf.is_nan(g)) for g in grads]))
    

    将 LSTMCell 替换为 LayerNormBasicLSTMCell - 具有层范数的 LSTM 单元 - 类似于时间步之间的批处理范数。

    如果您使用常规的经常性状态丢失,您可以将其替换为“没有记忆丢失的反复性丢失”。代码:

    LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)
    

    注意,你也可以单独开启 dropout 功能,无需层归一化:

    LayerNormBasicLSTMCell(neurons, layer_norm=False, dropout_keep_prob=0.8)
    

【讨论】:

【参考方案10】:

无偏见的替代方案。

许多其他解决方案使用剪裁来避免未定义的渐变。根据您的问题,剪裁会引入偏差,并且可能并非在所有情况下都可接受。如以下代码所示,我们只需要处理不连续点,而不是它附近的区域。

具体答案

def cross_entropy(x, y, axis=-1):
  safe_y = tf.where(tf.equal(x, 0.), tf.ones_like(y), y)
  return -tf.reduce_sum(x * tf.log(safe_y), axis)

def entropy(x, axis=-1):
  return cross_entropy(x, x, axis)

但它有用吗?

x = tf.constant([0.1, 0.2, 0., 0.7])
e = entropy(x)
# ==> 0.80181855
g = tf.gradients(e, x)[0]
# ==> array([1.30258512,  0.60943794, 0., -0.64332503], dtype=float32)  Yay! No NaN.

(注:已删除dup cross-post。)

一般食谱

使用内部tf.where 确保函数没有渐近线。 也就是说,改变 inf 生成函数的输入,使得不能创建 inf。 然后使用第二个tf.where 始终选择有效的代码路径。 也就是说,按照“通常”的方式实现数学条件,即“幼稚”的实现。

在 Python 代码中,配方是:

而不是这个:

tf.where(x_ok, f(x), safe_f(x))

这样做:

safe_x = tf.where(x_ok, x, safe_x)
tf.where(x_ok, f(safe_x), safe_f(x))

示例

假设你想计算:

f(x) =  1/x, x!=0
        0,   x=0

一个简单的实现会导致梯度中的 NaN,即,

def f(x):
  x_ok = tf.not_equal(x, 0.)
  f = lambda x: 1. / x
  safe_f = tf.zeros_like
  return tf.where(x_ok, f(x), safe_f(x))

有用吗?

x = tf.constant([-1., 0, 1])
tf.gradients(f(x), x)[0].eval()
# ==> array([ -1.,  nan,  -1.], dtype=float32)
#  ...bah! We have a NaN at the asymptote despite not having
# an asymptote in the non-differentiated result.

使用tf.where 时避免NaN 梯度的基本模式是调用tf.where 两次。最里面的tf.where 确保结果f(x) 始终是有限的。最外面的tf.where 确保选择了正确的结果。对于正在运行的示例,技巧如下所示:

def safe_f(x):
  x_ok = tf.not_equal(x, 0.)
  f = lambda x: 1. / x
  safe_f = tf.zeros_like
  safe_x = tf.where(x_ok, x, tf.ones_like(x))
  return tf.where(x_ok, f(safe_x), safe_f(x))

但它有用吗?

x = tf.constant([-1., 0, 1])
tf.gradients(safe_f(x), x)[0].eval()
# ==> array([-1.,  0., -1.], dtype=float32)
# ...yay! double-where trick worked. Notice that the gradient
# is now a constant at the asymptote (as opposed to being NaN).

【讨论】:

我对你天真的 impl 的行为有点困惑。并使用tf.where 两次来解决这个问题,但是如果您自己绘制梯度的计算图,则很容易理解。在某些时候,grad(1./x, x) * 0.0 将导致 nan。顺便说一句,tf.cond 没有这个问题,但在大多数情况下这并不是真正的替代方案。 嗨,阿尔伯特——感谢您指出这一点。我已经更正了一般过程中的一些错误并改进了示例。 这个!很好的答案!它应该是高级 TensorFlow 教程/文档或类似内容的一部分 注意:我也在这里记录了这个答案:github.com/tensorflow/probability/blob/master/discussion/…【参考方案11】:

这是 TensorFlow 1.1 中二进制 (sigmoid) 和分类 (softmax) 交叉熵损失的实现:

https://github.com/tensorflow/tensorflow/blob/r1.1/tensorflow/python/ops/nn_impl.py#L159 https://github.com/tensorflow/tensorflow/blob/r1.1/tensorflow/python/ops/nn_ops.py#L1609

正如人们在二进制情况下看到的那样,他们考虑了一些特殊情况来实现数值稳定性:

# The logistic loss formula from above is
#   x - x * z + log(1 + exp(-x))
# For x < 0, a more numerically stable formula is
#   -x * z + log(1 + exp(x))
# Note that these two expressions can be combined into the following:
#   max(x, 0) - x * z + log(1 + exp(-abs(x)))
# To allow computing gradients at zero, we define custom versions of max and
# abs functions.
zeros = array_ops.zeros_like(logits, dtype=logits.dtype)
cond = (logits >= zeros)
relu_logits = array_ops.where(cond, logits, zeros)
neg_abs_logits = array_ops.where(cond, -logits, logits)
return math_ops.add(relu_logits - logits * labels,
                    math_ops.log1p(math_ops.exp(neg_abs_logits)),
                    name=name)

【讨论】:

【参考方案12】:

您正在尝试使用标准公式计算cross-entropy。不仅x=0时值未定义,而且数值不稳定。

最好使用tf.nn.softmax_cross_entropy_with_logits 或者如果你真的想使用手工公式,将tf.clip_by_value 零到日志中的非常小的数字。

【讨论】:

【参考方案13】:

实际上,裁剪并不是一个好主意,因为当达到阈值时,它会阻止梯度向后传播。相反,我们可以在 softmax 输出中添加一点常量。

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv + 1e-10))

【讨论】:

这正是我在我的网络中所做的,但在计算以下内容时我仍然得到 NaN:tf.log(1e-10 + 1 - 1)。如果我打印出数据并在 Excel 中计算相同的值,我会得到正确的值 -23。 @fwc,我遇到了同样的问题。将其增加到 tf.log(1e-7+...) 之类的东西解决了这个问题。 @fwc 我能够重现此问题并在此处填写错误报告:github.com/tensorflow/tensorflow/issues/25728【参考方案14】:

如果y_conv 是softmax 的结果,例如y_conv = tf.nn.softmax(x),那么更好的解决方案是将其替换为log_softmax

y = tf.nn.log_softmax(x)
cross_entropy = -tf.reduce_sum(y_*y)

【讨论】:

【参考方案15】:

实际上,结果很愚蠢。我发布这个以防其他人遇到类似的错误。

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))

实际上是一种计算交叉熵的可怕方法。在某些样本中,一段时间后可以确定地排除某些类,导致该样本的 y_conv=0。这通常不是问题,因为您对这些不感兴趣,但是以 cross_entropy 的写入方式,它会为该特定样本/类产生 0*log(0) 。因此是 NaN。

替换为

cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))

解决了我所有的问题。

【讨论】:

很高兴你解决了它!作为附加说明,如果您正在处理真实数据,您可能会发现convolutional.py 是一个更好的起点。它在文件顶部使用 NUM_CHANNELS 进行参数化,如果将其从 1 切换到 3,则最好使用 RGB 数据。我已经使用它开箱即用地对一些较大的 RGB 数据集进行分类,这些数据集被缩小到“mnist 大小”(28x28),并且效果相当不错。关键是使用 tf.nn.softmax_cross_entropy_with_logits @dga 这是convolution.py 的更新链接,因为它不再位于 tensorflow master 分支中 注意:这个解决方案引入了偏见。我在下面发布了一个答案,可以避免这个问题。 为什么不只是tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y)(通常不需要手动剪辑logits),而不是你的y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0))? beginner tutorial 中提到了这一点。 @YiboYang 我认为您应该考虑发布此评论作为答案。指出初学者教程中涵盖了这一点,这是一个有价值的贡献,因为许多有此问题的人可能已经看过教程中的手写公式,并且错过了指向 tf.nn.softmax_cross_entropy_with_logits 的指针(就像我一样)。证明教程仍然可以信任是很有帮助的。

以上是关于TensorFlow NaN 错误?的主要内容,如果未能解决你的问题,请参考以下文章

Tensorflow:GPU上矩阵乘法(NaN)的错误结果

TensorFlow NaN 错误?

安装带有 SYCL 支持的 TensorFlow

Tensorflow交叉熵计算错误

Tensorflow:损失变成'NaN'

解决tensorflow在训练的时候权重是nan问题