Tensorflow NaN 错误?
Tensorflow NaN bug?
我正在使用 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
由于我的 none 值非常高,唯一可能出现 NaN 的方法是处理不当的 0/0,但由于本教程代码不执行任何除法或类似操作,我除了这来自内部 TF 代码外,没有其他解释。
我不知道该怎么办。有什么建议么?该算法很好地收敛,它在我的验证集上的准确性稳步攀升,在迭代 8600 时刚刚达到 92.5%。
其实,结果是搞了个傻逼。我发布这个是为了防止其他人 运行 遇到类似的错误。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
实际上是一种计算交叉熵的可怕方法。在一些样本中,某些 类 可以在一段时间后被排除在外,导致该样本的 y_conv=0。这通常不是问题,因为您对这些不感兴趣,但是按照 cross_entropy 的写法,它会为特定的 sample/class 产生 0*log(0)。因此 NaN。
替换为
cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))
解决了我所有的问题。
如果 y_conv
是 softmax 的结果,比如说 y_conv = tf.nn.softmax(x)
,那么更好的解决方案是将其替换为 log_softmax
:
y = tf.nn.log_softmax(x)
cross_entropy = -tf.reduce_sum(y_*y)
实际上,裁剪不是一个好主意,因为它会在达到阈值时阻止梯度向后传播。相反,我们可以在 softmax 输出中添加一点常量。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv + 1e-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.
(注:删除。)
一般食谱
使用内部 tf.where
确保函数没有渐近线。
也就是说,更改 inf 生成函数的输入,以便无法创建 inf。
然后使用第二个 tf.where
始终 select 有效代码路径。
也就是说,像 "normally" 那样实现数学条件,即 "naive" 实现。
在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).
您正在尝试使用标准公式计算 cross-entropy。 x=0
不仅数值不确定,而且数值不稳定
最好在日志中使用 tf.nn.softmax_cross_entropy_with_logits or if you really want to use hand-crafted formula, to tf.clip_by_value 零到非常小的数字。
这是 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)
我对长序列使用了 LSTM,得到了 nan 梯度。 None 这些答案对我有帮助。但我想出了三个自己的解决方案。我希望它们对通过 google 搜索来到这里的其他人有用。
梯度裁剪对我没有帮助,因为梯度在一批更新中变成了 nan。在这种情况下,您可以使用以下行将 nans 替换为零:
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 单元 - 类似于时间步之间的批量范数。
如果您使用常规的循环状态丢失,您可以将其替换为 "Recurrent Dropout without Memory Loss"。代码:
LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)
注意你也可以单独开启dropout功能而不用层归一化:
LayerNormBasicLSTMCell(neurons, layer_norm=False, dropout_keep_prob=0.8)
在标准 feed-forward 网络上工作时,我有时会收到 nans,而其他时候则不会。我以前使用过类似的 TensorFlow 代码,它运行良好。
原来我不小心导入了变量名。因此,一旦批量选择了第一行(变量名称),nan 损失就开始了。也许留意一下?
除了上面所有的好答案,我会添加我的。 运行 这种情况不太常见,但会导致 NaN:除以零 。
在我的 NLP 任务网络中,有一个层执行 平均池化 。即,每个数据都是一个标记序列。我的层做了一些标记嵌入,然后计算嵌入向量的平均值。
平均计算编码为
tf.reduce_sum(embedded)/tf.reduce_sum(tf.not_equal(input, pad))
这里 pad
是我在批处理中使用的一些虚拟标记。
现在,如果某些数据包含空标记列表(无论出于何种原因),其长度(上面代码片段中的分母)将为 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 问题。
有时您使用 tf.sqrt()
函数而没有在其中添加一个小常量 1e-10
,从而导致此 nan
问题。
我将在此处添加我之前遇到的 NaN 问题之一。我使用 sigmoid 函数作为网络最后一层的激活函数。但是,sigmoid 激活函数使用指数函数进行计算,我得到了一些非常大的数字进入 sigmoid。
它导致了无限梯度,一些 NaN 开始出现。
我一直在使用 Tensorflow Estimator,我认为它可以解决被零除和其他数值稳定性问题,并且偶尔会出现此错误 (ERROR:tensorflow:Model diverged with loss = NaN during training
)。大多数时候我得到这个是因为我的输入包括 nan
s。所以:确保你的输入数据帧(或你使用的任何东西)没有隐藏在其中某处的 NaN 值。
2.0 兼容答案:从
迁移@user1111929 的答案的代码
Tensorflow 1.x
到Tensorflow 2.x
,如下图:
张量流1.x:
cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))
Tensorflow 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)))
另一种选择是使用tf.math.xlogy
函数。功能描述说
“Returns 0 如果 x == 0,并且 x * log(y) 否则,按元素。”
您可以在此处找到文档:https://www.tensorflow.org/api_docs/python/tf/math/xlogy
在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)
我正在使用 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
由于我的 none 值非常高,唯一可能出现 NaN 的方法是处理不当的 0/0,但由于本教程代码不执行任何除法或类似操作,我除了这来自内部 TF 代码外,没有其他解释。
我不知道该怎么办。有什么建议么?该算法很好地收敛,它在我的验证集上的准确性稳步攀升,在迭代 8600 时刚刚达到 92.5%。
其实,结果是搞了个傻逼。我发布这个是为了防止其他人 运行 遇到类似的错误。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
实际上是一种计算交叉熵的可怕方法。在一些样本中,某些 类 可以在一段时间后被排除在外,导致该样本的 y_conv=0。这通常不是问题,因为您对这些不感兴趣,但是按照 cross_entropy 的写法,它会为特定的 sample/class 产生 0*log(0)。因此 NaN。
替换为
cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))
解决了我所有的问题。
如果 y_conv
是 softmax 的结果,比如说 y_conv = tf.nn.softmax(x)
,那么更好的解决方案是将其替换为 log_softmax
:
y = tf.nn.log_softmax(x)
cross_entropy = -tf.reduce_sum(y_*y)
实际上,裁剪不是一个好主意,因为它会在达到阈值时阻止梯度向后传播。相反,我们可以在 softmax 输出中添加一点常量。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv + 1e-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.
(注:删除
一般食谱
使用内部 tf.where
确保函数没有渐近线。
也就是说,更改 inf 生成函数的输入,以便无法创建 inf。
然后使用第二个 tf.where
始终 select 有效代码路径。
也就是说,像 "normally" 那样实现数学条件,即 "naive" 实现。
在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).
您正在尝试使用标准公式计算 cross-entropy。 x=0
不仅数值不确定,而且数值不稳定
最好在日志中使用 tf.nn.softmax_cross_entropy_with_logits or if you really want to use hand-crafted formula, to tf.clip_by_value 零到非常小的数字。
这是 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)
我对长序列使用了 LSTM,得到了 nan 梯度。 None 这些答案对我有帮助。但我想出了三个自己的解决方案。我希望它们对通过 google 搜索来到这里的其他人有用。
梯度裁剪对我没有帮助,因为梯度在一批更新中变成了 nan。在这种情况下,您可以使用以下行将 nans 替换为零:
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 单元 - 类似于时间步之间的批量范数。
如果您使用常规的循环状态丢失,您可以将其替换为 "Recurrent Dropout without Memory Loss"。代码:
LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)
注意你也可以单独开启dropout功能而不用层归一化:
LayerNormBasicLSTMCell(neurons, layer_norm=False, dropout_keep_prob=0.8)
在标准 feed-forward 网络上工作时,我有时会收到 nans,而其他时候则不会。我以前使用过类似的 TensorFlow 代码,它运行良好。
原来我不小心导入了变量名。因此,一旦批量选择了第一行(变量名称),nan 损失就开始了。也许留意一下?
除了上面所有的好答案,我会添加我的。 运行 这种情况不太常见,但会导致 NaN:除以零 。
在我的 NLP 任务网络中,有一个层执行 平均池化 。即,每个数据都是一个标记序列。我的层做了一些标记嵌入,然后计算嵌入向量的平均值。
平均计算编码为
tf.reduce_sum(embedded)/tf.reduce_sum(tf.not_equal(input, pad))
这里 pad
是我在批处理中使用的一些虚拟标记。
现在,如果某些数据包含空标记列表(无论出于何种原因),其长度(上面代码片段中的分母)将为 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 问题。
有时您使用 tf.sqrt()
函数而没有在其中添加一个小常量 1e-10
,从而导致此 nan
问题。
我将在此处添加我之前遇到的 NaN 问题之一。我使用 sigmoid 函数作为网络最后一层的激活函数。但是,sigmoid 激活函数使用指数函数进行计算,我得到了一些非常大的数字进入 sigmoid。
它导致了无限梯度,一些 NaN 开始出现。
我一直在使用 Tensorflow Estimator,我认为它可以解决被零除和其他数值稳定性问题,并且偶尔会出现此错误 (ERROR:tensorflow:Model diverged with loss = NaN during training
)。大多数时候我得到这个是因为我的输入包括 nan
s。所以:确保你的输入数据帧(或你使用的任何东西)没有隐藏在其中某处的 NaN 值。
2.0 兼容答案:从
迁移@user1111929 的答案的代码Tensorflow 1.x
到Tensorflow 2.x
,如下图:
张量流1.x:
cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))
Tensorflow 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)))
另一种选择是使用tf.math.xlogy
函数。功能描述说
“Returns 0 如果 x == 0,并且 x * log(y) 否则,按元素。”
您可以在此处找到文档:https://www.tensorflow.org/api_docs/python/tf/math/xlogy
在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)