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.
Я не знаю, что с этим делать. Какие-либо предложения? Алгоритм сходится хорошо, его точность в моем наборе проверки постоянно поднималась и достигла 92,5% на итерации 8600.
На самом деле это оказалось чем-то глупо. Я отправляю это на случай, если кто-то другой столкнется с подобной ошибкой.
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
на самом деле является ужасным способом вычисления кросс-энтропии. В некоторых примерах некоторые classы могут быть исключены с уверенностью через некоторое время, в результате чего y_conv = 0 для этого образца. Это обычно не проблема, так как вас это не интересует, но в том, как там написана cross_entropy, она дает 0 * log (0) для этого конкретного образца / classа. Следовательно, NaN.
Заменяя его
cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))
решил все мои проблемы.
Фактически, отсечение – это не очень хорошая идея, так как это предотвратит распространение gradleиента назад, когда достигнут порог. Вместо этого мы можем добавить немного константы к выходу softmax.
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv + 1e-10))
Если y_conv
является результатом softmax, скажем, y_conv = tf.nn.softmax(x)
, то еще лучшим решением является его замена log_softmax
:
y = tf.nn.log_softmax(x) cross_entropy = -tf.reduce_sum(y_*y)
Альтернатива без предубеждения.
Многие другие решения используют обрезку, чтобы избежать неопределенного gradleиента. В зависимости от вашей проблемы, отсечение вводит предвзятость и может быть неприемлемым во всех случаях. Как показывает следующий код, нам нужно только обрабатывать точку разрыва – не область рядом с ней.
Конкретный ответ
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
чтобы всегда выбирать допустимый код-путь. То есть, реализуйте математическое условие, как вы «обычно», т. Е. «Наивную» реализацию.
В коде 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
Наивная реализация приводит к NaNs в gradleиенте, т. Е.
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.
Основной шаблон для избежания gradleиентов 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).
Вы пытаетесь рассчитать кросс-энтропию, используя стандартную формулу. Не только значение не определено при x=0
, оно также численно неустойчиво.
Лучше использовать tf.nn.softmax_cross_entropy_with_logits или если вы действительно хотите использовать формулу, созданную вручную, до нhive tf.clip_by_value до очень малого числа в журнале.
Ниже приведена реализация бинарных (сигмовидных) и категориальных (softmax) потерь кросс-энтропии в TensorFlow 1.1:
- 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 для длинных последовательностей и получил наном gradleиенты. Ни один из этих ответов не помог мне. Но я придумал три собственных решения. Надеюсь, они будут полезны для других людей, которые пришли сюда из поиска Google.
-
Градиентная обрезка не помогла мне, потому что gradleиенты превратили 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 со стандартом уровня – что-то похожее на пакетную норму между timesteps.
-
Если вы используете регулярное повторное выпадение состояния, вы можете заменить его на «Повторное выпадение без потери памяти». Код:
LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)
Обратите внимание, что вы также можете включить функцию отсечки без нормализации уровня:
LayerNormBasicLSTMCell(neurons, layer_norm=False, dropout_keep_prob=0.8)
Иногда я получал nans, а не другие, работая в стандартной сети прямой пересылки. Ранее я использовал аналогичный код TensorFlow, и он работал нормально.
Оказывается, я импортировал имена переменных случайно. Итак, как только первая строка (имена переменных) была выбрана в пакете, начались потери нану. Может быть, обратите внимание на это?
Помимо всех замечательных ответов выше, я добавлю свои. Это сценарий, который нередко встречается, но вызывает NaN: деление на ноль .
В моей сети для задачи NLP есть слой, который выполняет средний пул . А именно, каждый из данных представляет собой последовательность токенов. Мой слой делает некоторое вложение в токен, а затем вычисляет среднее значение встроенного вектора.
Средний расчет кодируется как
tf.reduce_sum(embedded)/tf.reduce_sum(tf.not_equal(input, pad))
Здесь pad
– некоторый фиктивный токен, который я использую в пакетной обработке.
Теперь, если некоторые данные содержат пустой список токенов (по какой-либо причине), его длина (знаменатель в fragmentе кода выше) будет равна 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
По сути, это относится ко всем этим данным с нулевым списком токенов длиной 1 и позволяет избежать проблемы с NaN.