Tensorflow NaN ошибка?
Я использую 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.
Я не знаю, что с этим делать. Какие-либо предложения? Алгоритм сходится красиво, его точность на моем наборе проверки неуклонно поднималась и достигла 92,5% на итерации 8600.
Ответы
Ответ 1
Собственно, это оказалось чем-то глупо. Я отправляю это на случай, если кто-то другой столкнется с подобной ошибкой.
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)))
решил все мои проблемы.
Ответ 2
Фактически, отсечение - это не очень хорошая идея, так как это предотвратит распространение градиента назад, когда достигнут порог. Вместо этого мы можем добавить немного константы к выходу softmax.
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv + 1e-10))
Ответ 3
Альтернативная альтернатива.
Многие из других решений используют обрезку, чтобы избежать градиента undefined. В зависимости от вашей проблемы, отсечение вводит предвзятость и может быть неприемлемым во всех случаях. Как показывает следующий код, нам нужно только обрабатывать точку разрыва - не область рядом с ней.
Конкретный ответ
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
Наивная реализация приводит к NaNs в градиенте, то есть
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).
Ответ 4
Если y_conv
является результатом softmax, скажем, y_conv = tf.nn.softmax(x)
, то еще лучшим решением является замена его на log_softmax
:
y = tf.nn.log_softmax(x)
cross_entropy = -tf.reduce_sum(y_*y)
Ответ 5
Иногда вы используете tf.sqrt()
не добавляя в нее небольшую константу 1e-10
, вызывая эту проблему с nan
.
Ответ 6
Вы пытаетесь вычислить кросс-энтропию, используя стандартную формулу. Не только значение не определено при x=0
, оно также численно неустойчиво.
Лучше использовать tf.nn.softmax_cross_entropy_with_logits или если вы действительно хотите использовать формулу, созданную вручную, до tf.clip_by_value нули до очень малого числа в журнале.
Ответ 7
Вот реализация бинарных (сигмовидных) и категориальных (softmax) потерь кросс-энтропии в TensorFlow 1.1:
Как можно видеть в двоичном случае, они рассматривают некоторые особые случаи для достижения численной устойчивости:
# 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)
Ответ 8
Я использовал LSTM для длинных последовательностей и получил наном градиенты. Ни один из этих ответов не помог мне. Но я придумал три собственных решения. Надеюсь, они будут полезны для других людей, которые пришли сюда из поиска 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 со стандартом уровня - что-то похожее на пакетную норму между timesteps.
-
Если вы используете регулярное повторное выпадение состояния, вы можете заменить его на "Повторное выпадение без потери памяти". Код:
LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)
Обратите внимание, что вы также можете включить функцию отсечки без нормализации уровня:
LayerNormBasicLSTMCell(neurons, layer_norm=False, dropout_keep_prob=0.8)
Ответ 9
Помимо всех замечательных ответов выше, я добавлю свои. Это менее распространенный сценарий, но он вызывает 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
По сути, это обрабатывает все эти данные со списком токенов нулевой длины до длины 1 и позволяет избежать проблемы NaN.
Ответ 10
Иногда я получал nans, а не иногда, работая в стандартной сети прямой связи. Ранее я использовал аналогичный код TensorFlow, и он работал нормально.
Оказывается, я случайно импортировал имена переменных. Таким образом, как только в партии был выбран первый ряд (имена переменных), начались нанопотери. Может быть, следить за этим?
Ответ 11
Я добавлю сюда одну из моих предыдущих проблем с NaNs. Я использовал функцию сигмоида в качестве активации последнего уровня моей сети. Однако, функция активации сигмоида использует экспоненциальную функцию для вычисления, и я получил несколько действительно больших чисел, вводящих сигмоид.
Это привело к бесконечным градиентам, и некоторые NaN начали появляться.
Ответ 12
Я использовал Tensorflow Estimator, который, по моему мнению, учитывает это деление на ноль и другие проблемы числовой стабильности, и иногда получаю эту ошибку (ERROR:tensorflow:Model diverged with loss = NaN during training
). Большую часть времени, когда я получаю это, потому что мои входные данные включают в себя nan
с. Итак: убедитесь, что ваши входные фреймы данных (или что вы используете) не имеют значений NaN, скрытых где-то в них.