Детерминизм в обновлениях тензорного градиента?
Итак, у меня есть очень простой NN-скрипт, написанный на Tensorflow, и я с трудом пытаюсь отследить, откуда взялась некоторая "случайность".
Я записал
- Массы,
- Градиенты,
- логит
из моей сети, когда я тренируюсь, и для первой итерации ясно, что все начинается одинаково. У меня есть значение SEED как для считывания данных, так и значение SEED для инициализации весов сети. Те, которые я никогда не меняю.
Моя проблема в том, что, скажем, на второй итерации каждого повторного запуска я начинаю видеть, как градиенты расходятся (на небольшое количество, например, 1e-6 или около того). Однако со временем это, конечно, приводит к неповторяющемуся поведению.
Что может быть причиной этого? Я не знаю, откуда взялся любой возможный источник случайности...
Спасибо
3 ответа
Существует большая вероятность того, что вы можете получить детерминированные результаты, если ваша сеть работает на CPU (export CUDA_VISIBLE_DEVICES=
), с одним потоком в пуле собственных потоков (tf.Session(config=tf.ConfigProto(intra_op_parallelism_threads=1)
), один поток Python (нет многопоточных обработчиков очередей, которые вы получаете от ops, как tf.batch
), и один четко определенный порядок работы. Также используя inter_op_parallelism_threads=1
может помочь в некоторых сценариях.
Одна из проблем заключается в том, что сложение / умножение с плавающей запятой неассоциативно, поэтому одним из надежных способов получения детерминированных результатов является использование целочисленных арифметических или квантованных значений.
За исключением этого, вы можете выделить, какая операция недетерминированная, и попытаться избежать использования этой операции. Например, есть tf.add_n
op, который ничего не говорит о порядке, в котором он суммирует значения, но разные порядки дают разные результаты.
Получение детерминированных результатов - трудная битва, потому что детерминизм находится в конфликте с производительностью, а производительность обычно является целью, которая привлекает больше внимания. Альтернативой попыткам иметь одинаковые числа при повторных запусках является фокусировка на числовой стабильности - если ваш алгоритм стабилен, вы получите воспроизводимые результаты (т. Е. Такое же количество ошибочных классификаций), даже если точные значения параметров могут немного отличаться
Как известно, тензор потока redu_sum определен как недетерминированный. Кроме того, lower_sum используется для вычисления градиентов смещения.
В этом посте обсуждается обходной путь, позволяющий избежать использования redu_sum (т. Е. Взятие точечного произведения любого вектора с вектором всех единиц равно значению Reduce_sum)
Я столкнулся с той же проблемой.. Рабочим решением для меня было:
1- использование tf.set_random_seed(1)
для того, чтобы все функции TF имели одинаковое начальное число при каждом новом запуске
2- Обучение модели с использованием CPU, а не GPU, чтобы избежать недетерминированных операций GPU из-за точности.