Development Tip

Tensorflow NaN 버그?

yourdevel 2020. 12. 9. 21:53
반응형

Tensorflow NaN 버그?


저는 TensorFlow를 사용하고 있으며 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 %에 도달했습니다.


사실, 그것은 어리석은 것으로 판명되었습니다. 다른 사람이 비슷한 오류가 발생할 경우를 대비하여 게시하고 있습니다.

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)))

내 모든 문제를 해결했습니다.


실제로 클리핑은 임계 값에 도달했을 때 그라디언트가 뒤로 전파되는 것을 막기 때문에 좋은 생각이 아닙니다. 대신 소프트 맥스 출력에 약간의 상수를 추가 할 수 있습니다.

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.

(참고 : 삭제 된 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.

사용할 때 NaN 그라디언트를 피하는 기본 패턴은 두 번 tf.where호출하는 것 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).

If y_conv is the result of a softmax, say, y_conv = tf.nn.softmax(x), then an even better solution is to replace it with log_softmax:

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

Sometimes you use tf.sqrt() function without adding a small constant 1e-10 in it, inducing this nan problem.


You are trying to calculate cross-entropy using the standard formula. Not only the value is undefinined when x=0, it is also numerically unstable.

It is better to use tf.nn.softmax_cross_entropy_with_logits or if you really want to use hand-crafted formula, to tf.clip_by_value zeros to very small number in the log.


Here is the implementation of the binary (sigmoid) and categorical (softmax) cross-entropy losses in TensorFlow 1.1:

As one can see in the binary case they consider some special cases to achieve numerical stability:

# 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)

I used LSTM for long sequences and got nan gradients. None of these answers helped me. But I came up with three own solutions. I hope they will be useful for some other people who came here from google search.

  1. Gradient clipping didn't help me because gradients turned nan in one batch update. In this case, you can replace nans with zeros with such lines:

    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)
    

    If you want to track if nans appeared you can use this code:

    was_nan = tf.reduce_any(tf.convert_to_tensor([tf.reduce_any(tf.is_nan(g)) for g in grads]))
    
  2. Replace LSTMCell with LayerNormBasicLSTMCell - an LSTM cell with layer norm - something similar to batch norm between timesteps.

  3. If you use regular recurrent state dropout you can replace it with "Recurrent Dropout without Memory Loss". Code:

    LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)
    

    Note that you can also turn on the dropout feature alone without layer normalization:

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

Besides all the great answers above, I will add mine. It's a scenario less common to run into, but does cause NaN: divide by zero.

In my network for a NLP task, there is a layer that does average pooling. Namely, each data is a sequence of tokens. My layer does some token embedding and then calculates the average of the embedded vector.

The average calculation is coded as

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

Here pad is some dummy token I use in batch processing.

Now if some data contains empty token list (for whatever reason), its length (the denominator in the code snippet above) would be 0. Then it causes a divide by zero issue and the NaN will remain in all the following layers/ optimization steps.

In case anyone ran into this issue, I used tf.where to smooth those length:

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

Essentially this treats all those data with 0-length token list to be of length 1, and avoids the NaN issue.


I was getting nans sometimes and not other times while working on a standard feed-forward network. I have previously used similar TensorFlow code and it worked fine.

It turns out that I imported the variable names by accident. So, as soon as the first row (the variable names) was selected in a batch, the nan losses started. Maybe keep an eye out for that?


I will add here one of my previous problems with NaNs. I was using the sigmoid function as the activation of the last layer of my network. However, the sigmoid activation function uses the exponential function to be computed and I got some really big numbers entering the sigmoid.

It resulted in infinite gradients and some NaNs started to appear.


I've been using Tensorflow Estimator, which I believe account for those division by zero and other numerical stability issues, and occasionally get this error (ERROR:tensorflow:Model diverged with loss = NaN during training). Most of the time when I get this is because my inputs include nans. So: be sure that your input dataframes (or whatever you use) don't have NaN values hidden somewhere in them.

참고URL : https://stackoverflow.com/questions/33712178/tensorflow-nan-bug

반응형