Катастрофические события, связанные с NaN , иногда могут происходить во время программы TensorFlow, нарушая процессы обучения модели. Основная причина таких событий часто неясна, особенно для моделей нетривиального размера и сложности. Чтобы упростить отладку ошибок модели этого типа, TensorBoard 2.3+ (вместе с TensorFlow 2.3+) предоставляет специализированную панель мониторинга под названием Debugger V2. Здесь мы демонстрируем, как использовать этот инструмент, работая с реальной ошибкой, связанной с NaN в нейронной сети, написанной на TensorFlow.
Методы, показанные в этом учебнике, применимы к другим типам действий по отладке, таким как проверка форм тензора времени выполнения в сложных программах. В этом руководстве основное внимание уделяется NaN из-за их относительно высокой частоты появления.
Наблюдение за ошибкой
Исходный код программы TF2, которую мы будем отлаживать, доступен на GitHub . Пример программы также упакован в пакет pip tensorflow (версия 2.3+) и может быть вызван:
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2
Эта программа TF2 создает многослойное восприятие (MLP) и обучает его распознаванию изображений MNIST . В этом примере намеренно используется низкоуровневый API TF2 для определения пользовательских конструкций слоев, функции потерь и цикла обучения, потому что вероятность ошибок NaN выше, когда мы используем этот более гибкий, но более подверженный ошибкам API, чем когда мы используем более простой удобные в использовании, но немного менее гибкие высокоуровневые API, такие как tf.keras .
Программа печатает тест точности после каждого шага обучения. Мы можем видеть в консоли, что точность теста зависает на уровне, близком к шансу (~ 0,1) после первого шага. Это определенно не то, как ожидается, что обучение модели будет вести себя: мы ожидаем, что точность будет постепенно приближаться к 1,0 (100%) по мере увеличения шага.
Accuracy at step 0: 0.216
Accuracy at step 1: 0.098
Accuracy at step 2: 0.098
Accuracy at step 3: 0.098
...
Обоснованное предположение состоит в том, что эта проблема вызвана численной нестабильностью, такой как NaN или бесконечность. Однако как мы можем подтвердить, что это действительно так, и как мы находим операцию TensorFlow (op), ответственную за создание численной нестабильности? Чтобы ответить на эти вопросы, давайте оснастим программу с ошибками Debugger V2.
Инструментирование кода TensorFlow с помощью Debugger V2
tf.debugging.experimental.enable_dump_debug_info() — это точка входа в API Debugger V2. Он инструментирует программу TF2 с помощью одной строки кода. Например, добавление следующей строки в начало программы приведет к записи отладочной информации в каталог журнала (logdir) по адресу /tmp/tfdbg2_logdir. Информация об отладке охватывает различные аспекты среды выполнения TensorFlow. В TF2 он включает в себя полную историю нетерпеливого выполнения, построение графа, выполненное @tf.function , выполнение графов, значения тензора, сгенерированные событиями выполнения, а также расположение кода (трассировки стека Python) этих событий. . Богатство отладочной информации позволяет пользователям сузить круг неизвестных ошибок.
tf.debugging.experimental.enable_dump_debug_info(
"/tmp/tfdbg2_logdir",
tensor_debug_mode="FULL_HEALTH",
circular_buffer_size=-1)
Аргумент tensor_debug_mode определяет, какую информацию Debugger V2 извлекает из каждого активного тензора или тензора в графе. «FULL_HEALTH» — это режим, который фиксирует следующую информацию о каждом тензоре плавающего типа (например, часто встречающийся тип float32 и менее распространенный тип bfloat16 ):
- DТип
- Классифицировать
- Общее количество элементов
- Разбивка элементов плавающего типа на следующие категории: отрицательная конечная (
-), ноль (0), положительная конечная (+), отрицательная бесконечность (-∞), положительная бесконечность (+∞) иNaN.
Режим «FULL_HEALTH» подходит для отладки ошибок, связанных с NaN и бесконечностью. См. ниже другие поддерживаемые tensor_debug_mode s.
Аргумент circular_buffer_size определяет, сколько событий тензора сохраняется в logdir. По умолчанию он равен 1000, что приводит к сохранению на диск только последних 1000 тензоров перед окончанием инструментированной программы TF2. Такое поведение по умолчанию снижает нагрузку на отладчик, жертвуя полнотой отладочных данных. Если предпочтительна полнота, как в этом случае, мы можем отключить кольцевой буфер, установив для аргумента отрицательное значение (например, здесь -1).
Пример debug_mnist_v2 вызывает enable_dump_debug_info() , передавая ему флаги командной строки. Чтобы снова запустить нашу проблемную программу TF2 с включенным инструментарием отладки, выполните:
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2 \
--dump_dir /tmp/tfdbg2_logdir --dump_tensor_debug_mode FULL_HEALTH
Запуск графического интерфейса Debugger V2 в TensorBoard
Запуск программы с инструментами отладчика создает каталог журнала в /tmp/tfdbg2_logdir. Мы можем запустить TensorBoard и указать его в logdir с помощью:
tensorboard --logdir /tmp/tfdbg2_logdir
В веб-браузере перейдите на страницу TensorBoard по адресу http://localhost:6006. Плагин Debugger V2 будет неактивен по умолчанию, поэтому выберите его в меню «Неактивные плагины» в правом верхнем углу. После выбора он должен выглядеть следующим образом:

Использование графического интерфейса Debugger V2 для поиска основной причины NaN
Графический интерфейс Debugger V2 в TensorBoard состоит из шести разделов:
- Оповещения . Этот верхний левый раздел содержит список событий «оповещения», обнаруженных отладчиком в данных отладки инструментированной программы TensorFlow. Каждое предупреждение указывает на определенную аномалию, требующую внимания. В нашем случае в этом разделе выделено 499 событий NaN/∞ ярко выраженным розово-красным цветом. Это подтверждает наше подозрение, что модель не может обучаться из-за наличия NaN и/или бесконечности в ее внутренних значениях тензора. Вскоре мы углубимся в эти предупреждения.
- Временная шкала выполнения Python : это верхняя половина верхней средней части. В нем представлена полная история нетерпеливого выполнения операций и графиков. Каждый блок временной шкалы помечен начальной буквой имени операции или графика (например, «T» для операции «TensorSliceDataset», «m» для «модели»
tf.function). Мы можем перемещаться по этой временной шкале, используя кнопки навигации и полосу прокрутки над временной шкалой. - Выполнение графа : расположенный в правом верхнем углу графического интерфейса, этот раздел будет центральным в нашей задаче отладки. Он содержит историю всех тензоров с плавающим типом, вычисленных внутри графов (т.е. скомпилированных с помощью
@tf-functions). - Структура графика (нижняя половина верхнего среднего раздела), исходный код (нижний левый раздел) и трассировка стека (нижний правый раздел) изначально пусты. Их содержимое будет заполняться при взаимодействии с графическим интерфейсом. Эти три раздела также будут играть важную роль в нашей задаче отладки.
Сориентировавшись в организации UI, давайте проделаем следующие шаги, чтобы разобраться, почему появились NaN. Сначала щелкните оповещение NaN/∞ в разделе «Оповещения». Это автоматически прокручивает список из 600 тензоров графа в разделе «Выполнение графа» и фокусируется на #88, который представляет собой тензор с именем Log:0 , сгенерированный операцией Log (натуральный логарифм). Явным розово-красным цветом выделяется элемент -∞ среди 1000 элементов двумерного тензора float32. Это первый тензор в истории выполнения программы TF2, который содержал какое-либо NaN или бесконечность: тензоры, вычисленные до него, не содержат NaN или ∞; многие (на самом деле, большинство) тензоров, вычисляемых впоследствии, содержат NaN. Мы можем убедиться в этом, прокручивая вверх и вниз список Graph Execution. Это наблюдение дает убедительный намек на то, что Log является источником численной нестабильности в этой программе TF2.

Почему эта Log -операция выдает -∞? Ответ на этот вопрос требует изучения входных данных в op. Щелчок по имени тензора ( Log:0 ) вызывает простую, но информативную визуализацию окрестности операции Log на его графике TensorFlow в разделе «Структура графика». Обратите внимание на направление потока информации сверху вниз. Сама операция выделена жирным шрифтом посередине. Непосредственно над ним мы видим, что операция Placeholder предоставляет единственный вход для операции Log . Где находится тензор, сгенерированный этим probs в списке выполнения графа? Используя желтый цвет фона в качестве наглядного пособия, мы можем видеть, что тензор probs:0 находится на три строки выше тензора Log:0 , то есть в строке 85.

Более внимательный взгляд на числовую разбивку тензора probs:0 в строке 85 показывает, почему его потребитель Log:0 выдает -∞: среди 1000 элементов probs:0 один элемент имеет значение 0. -∞ результат вычисления натурального логарифма 0! Если мы каким-то образом сможем обеспечить, чтобы Log подвергалась воздействию только положительных входных данных, мы сможем предотвратить возникновение NaN/∞. Этого можно добиться, применив отсечение (например, с помощью tf.clip_by_value() ) к тензору проб- probs .
Мы приближаемся к решению проблемы, но еще не все сделано. Чтобы применить исправление, нам нужно знать, где в исходном коде Python возникла операция Log и ее ввод-заполнитель. Debugger V2 обеспечивает первоклассную поддержку для отслеживания операций графа и событий выполнения до их источника. Когда мы щелкнули тензор Log:0 в Graph Executions, раздел Stack Trace был заполнен исходной трассировкой стека создания операции Log . Трассировка стека несколько велика, потому что она включает в себя множество кадров из внутреннего кода TensorFlow (например, gen_math_ops.py и dumping_callback.py), которые мы можем спокойно игнорировать для большинства задач отладки. Нас интересует строка 216 файла debug_mnist_v2.py (т. е. файл Python, который мы фактически пытаемся отлаживать). Нажатие «Строка 216» открывает представление соответствующей строки кода в разделе «Исходный код».

Это, наконец, подводит нас к исходному коду, который создал проблемную операцию Log на основе входных данных probs . Это наша пользовательская категориальная функция кросс-энтропийных потерь, украшенная @tf.function и, следовательно, преобразованная в граф TensorFlow. Заполнитель op probs соответствует первому входному аргументу функции потерь. Оператор Log создается с помощью API-вызова tf.math.log().
Исправление этой ошибки с отсечением значений будет выглядеть примерно так:
diff = -(labels *
tf.math.log(tf.clip_by_value(probs), 1e-6, 1.))
Это устранит числовую нестабильность в этой программе TF2 и приведет к успешному обучению MLP. Другой возможный подход к устранению численной нестабильности — использовать tf.keras.losses.CategoricalCrossentropy .
На этом мы завершаем наш путь от наблюдения за ошибкой модели TF2 до внесения изменений в код, которые устраняют ошибку, с помощью инструмента Debugger V2, который обеспечивает полную видимость нетерпеливой и графической истории выполнения инструментированной программы TF2, включая числовые сводки. значений тензоров и связи между операциями, тензорами и их исходным кодом.
Аппаратная совместимость Debugger V2
Debugger V2 поддерживает основное обучающее оборудование, включая ЦП и ГП. Также поддерживается обучение с использованием нескольких графических процессоров с помощью tf.distributed.MirroredStrategy . Поддержка TPU все еще находится на ранней стадии и требует звонка
tf.config.set_soft_device_placement(True)
перед вызовом enable_dump_debug_info() . У него могут быть и другие ограничения для TPU. Если у вас возникнут проблемы с использованием Debugger V2, сообщите об ошибках на нашей странице проблем GitHub .
API-совместимость Debugger V2
Debugger V2 реализован на относительно низком уровне программного стека TensorFlow и, следовательно, совместим с tf.keras , tf.data и другими API, созданными поверх более низких уровней TensorFlow. Debugger V2 также обратно совместим с TF1, хотя временная шкала Eager Execution Timeline будет пустой для журналов отладки, созданных программами TF1.
Советы по использованию API
Часто задаваемый вопрос об этом API отладки заключается в том, где в коде TensorFlow следует вставить вызов enable_dump_debug_info() . Как правило, API следует вызывать как можно раньше в вашей программе TF2, предпочтительно после строк импорта Python и до начала построения и выполнения графа. Это обеспечит полный охват всех операций и графиков, на которых основана ваша модель и ее обучение.
В настоящее время поддерживаются следующие режимы tensor_debug_modes: NO_TENSOR , CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH и SHAPE . Они различаются объемом информации, извлекаемой из каждого тензора, и производительностью отлаживаемой программы. Пожалуйста, обратитесь к разделу args документации enable_dump_debug_info() .
Накладные расходы на производительность
API отладки увеличивает производительность инструментированной программы TensorFlow. Накладные расходы зависят от tensor_debug_mode , типа оборудования и характера инструментированной программы TensorFlow. В качестве ориентира на графическом процессоре режим NO_TENSOR добавляет 15 % накладных расходов во время обучения модели Transformer при размере партии 64. Накладные расходы в процентах для других режимов tensor_debug_modes выше: примерно 50 % для CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH и SHAPE . режимы. На процессорах накладные расходы немного ниже. На TPU накладные расходы в настоящее время выше.
Отношение к другим API-интерфейсам отладки TensorFlow
Обратите внимание, что TensorFlow предлагает другие инструменты и API для отладки. Вы можете просматривать такие API в пространстве имен tf.debugging.* на странице документации по API. Среди этих API наиболее часто используется tf.print() . Когда следует использовать Debugger V2, а когда вместо него следует использовать tf.print() ? tf.print() удобен в случае, когда
- мы точно знаем, какие тензоры печатать,
- мы знаем, где именно в исходном коде вставлять эти
tf.print(), - число таких тензоров не слишком велико.
Для других случаев (например, проверки многих значений тензора, проверки значений тензора, сгенерированных внутренним кодом TensorFlow, и поиска источника численной нестабильности, как мы показали выше), Debugger V2 предоставляет более быстрый способ отладки. Кроме того, Debugger V2 предоставляет унифицированный подход к проверке нетерпеливых и графовых тензоров. Он дополнительно предоставляет информацию о структуре графа и расположении кода, которые выходят за рамки возможностей tf.print() .
Другой API, который можно использовать для отладки проблем, связанных с ∞ и NaN, — это tf.debugging.enable_check_numerics() . В отличие от enable_dump_debug_info() , enable_check_numerics() не сохраняет отладочную информацию на диск. Вместо этого он просто отслеживает ∞ и NaN во время выполнения TensorFlow и выдает ошибки с расположением исходного кода, как только какая-либо операция генерирует такие неверные числовые значения. Он имеет меньшую нагрузку на производительность по сравнению с enable_dump_debug_info() , но не обеспечивает полной трассировки истории выполнения программы и не имеет графического пользовательского интерфейса, такого как Debugger V2.
Катастрофические события, связанные с NaN , иногда могут происходить во время программы TensorFlow, нарушая процессы обучения модели. Основная причина таких событий часто неясна, особенно для моделей нетривиального размера и сложности. Чтобы упростить отладку ошибок модели этого типа, TensorBoard 2.3+ (вместе с TensorFlow 2.3+) предоставляет специализированную панель мониторинга под названием Debugger V2. Здесь мы демонстрируем, как использовать этот инструмент, работая с реальной ошибкой, связанной с NaN в нейронной сети, написанной на TensorFlow.
Методы, показанные в этом учебнике, применимы к другим типам действий по отладке, таким как проверка форм тензора времени выполнения в сложных программах. В этом руководстве основное внимание уделяется NaN из-за их относительно высокой частоты появления.
Наблюдение за ошибкой
Исходный код программы TF2, которую мы будем отлаживать, доступен на GitHub . Пример программы также упакован в пакет pip tensorflow (версия 2.3+) и может быть вызван:
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2
Эта программа TF2 создает многослойное восприятие (MLP) и обучает его распознаванию изображений MNIST . В этом примере намеренно используется низкоуровневый API TF2 для определения пользовательских конструкций слоев, функции потерь и цикла обучения, потому что вероятность ошибок NaN выше, когда мы используем этот более гибкий, но более подверженный ошибкам API, чем когда мы используем более простой удобные в использовании, но немного менее гибкие высокоуровневые API, такие как tf.keras .
Программа печатает тест точности после каждого шага обучения. Мы можем видеть в консоли, что точность теста зависает на уровне, близком к шансу (~ 0,1) после первого шага. Это определенно не то, как ожидается, что обучение модели будет вести себя: мы ожидаем, что точность будет постепенно приближаться к 1,0 (100%) по мере увеличения шага.
Accuracy at step 0: 0.216
Accuracy at step 1: 0.098
Accuracy at step 2: 0.098
Accuracy at step 3: 0.098
...
Обоснованное предположение состоит в том, что эта проблема вызвана численной нестабильностью, такой как NaN или бесконечность. Однако как мы можем подтвердить, что это действительно так, и как мы находим операцию TensorFlow (op), ответственную за создание численной нестабильности? Чтобы ответить на эти вопросы, давайте оснастим программу с ошибками Debugger V2.
Инструментирование кода TensorFlow с помощью Debugger V2
tf.debugging.experimental.enable_dump_debug_info() — это точка входа в API Debugger V2. Он инструментирует программу TF2 с помощью одной строки кода. Например, добавление следующей строки в начало программы приведет к записи отладочной информации в каталог журнала (logdir) по адресу /tmp/tfdbg2_logdir. Информация об отладке охватывает различные аспекты среды выполнения TensorFlow. В TF2 он включает в себя полную историю нетерпеливого выполнения, построение графа, выполненное @tf.function , выполнение графов, значения тензора, сгенерированные событиями выполнения, а также расположение кода (трассировки стека Python) этих событий. . Богатство отладочной информации позволяет пользователям сузить круг неизвестных ошибок.
tf.debugging.experimental.enable_dump_debug_info(
"/tmp/tfdbg2_logdir",
tensor_debug_mode="FULL_HEALTH",
circular_buffer_size=-1)
Аргумент tensor_debug_mode определяет, какую информацию Debugger V2 извлекает из каждого активного тензора или тензора в графе. «FULL_HEALTH» — это режим, который фиксирует следующую информацию о каждом тензоре плавающего типа (например, часто встречающийся тип float32 и менее распространенный тип bfloat16 ):
- DТип
- Классифицировать
- Общее количество элементов
- Разбивка элементов плавающего типа на следующие категории: отрицательная конечная (
-), ноль (0), положительная конечная (+), отрицательная бесконечность (-∞), положительная бесконечность (+∞) иNaN.
Режим «FULL_HEALTH» подходит для отладки ошибок, связанных с NaN и бесконечностью. См. ниже другие поддерживаемые tensor_debug_mode s.
Аргумент circular_buffer_size определяет, сколько событий тензора сохраняется в logdir. По умолчанию он равен 1000, что приводит к сохранению на диск только последних 1000 тензоров перед окончанием инструментированной программы TF2. Такое поведение по умолчанию снижает нагрузку на отладчик, жертвуя полнотой отладочных данных. Если предпочтительна полнота, как в этом случае, мы можем отключить кольцевой буфер, установив для аргумента отрицательное значение (например, здесь -1).
Пример debug_mnist_v2 вызывает enable_dump_debug_info() , передавая ему флаги командной строки. Чтобы снова запустить нашу проблемную программу TF2 с включенным инструментарием отладки, выполните:
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2 \
--dump_dir /tmp/tfdbg2_logdir --dump_tensor_debug_mode FULL_HEALTH
Запуск графического интерфейса Debugger V2 в TensorBoard
Запуск программы с инструментами отладчика создает каталог журнала в /tmp/tfdbg2_logdir. Мы можем запустить TensorBoard и указать его в logdir с помощью:
tensorboard --logdir /tmp/tfdbg2_logdir
В веб-браузере перейдите на страницу TensorBoard по адресу http://localhost:6006. Плагин Debugger V2 будет неактивен по умолчанию, поэтому выберите его в меню «Неактивные плагины» в правом верхнем углу. После выбора он должен выглядеть следующим образом:

Использование графического интерфейса Debugger V2 для поиска основной причины NaN
Графический интерфейс Debugger V2 в TensorBoard состоит из шести разделов:
- Оповещения . Этот верхний левый раздел содержит список событий «оповещения», обнаруженных отладчиком в данных отладки инструментированной программы TensorFlow. Каждое предупреждение указывает на определенную аномалию, требующую внимания. В нашем случае в этом разделе выделено 499 событий NaN/∞ ярко выраженным розово-красным цветом. Это подтверждает наше подозрение, что модель не может обучаться из-за наличия NaN и/или бесконечности в ее внутренних значениях тензора. Вскоре мы углубимся в эти предупреждения.
- Временная шкала выполнения Python : это верхняя половина верхней средней части. В нем представлена полная история нетерпеливого выполнения операций и графиков. Каждый блок временной шкалы помечен начальной буквой имени операции или графика (например, «T» для операции «TensorSliceDataset», «m» для «модели»
tf.function). Мы можем перемещаться по этой временной шкале, используя кнопки навигации и полосу прокрутки над временной шкалой. - Выполнение графа : расположенный в правом верхнем углу графического интерфейса, этот раздел будет центральным в нашей задаче отладки. Он содержит историю всех тензоров с плавающим типом, вычисленных внутри графов (т.е. скомпилированных с помощью
@tf-functions). - Структура графика (нижняя половина верхнего среднего раздела), исходный код (нижний левый раздел) и трассировка стека (нижний правый раздел) изначально пусты. Их содержимое будет заполняться при взаимодействии с графическим интерфейсом. Эти три раздела также будут играть важную роль в нашей задаче отладки.
Сориентировавшись в организации UI, давайте проделаем следующие шаги, чтобы разобраться, почему появились NaN. Сначала щелкните оповещение NaN/∞ в разделе «Оповещения». Это автоматически прокручивает список из 600 тензоров графа в разделе «Выполнение графа» и фокусируется на #88, который представляет собой тензор с именем Log:0 , сгенерированный операцией Log (натуральный логарифм). Явным розово-красным цветом выделяется элемент -∞ среди 1000 элементов двумерного тензора float32. Это первый тензор в истории выполнения программы TF2, который содержал какое-либо NaN или бесконечность: тензоры, вычисленные до него, не содержат NaN или ∞; многие (на самом деле, большинство) тензоров, вычисляемых впоследствии, содержат NaN. Мы можем убедиться в этом, прокручивая вверх и вниз список Graph Execution. Это наблюдение дает убедительный намек на то, что Log является источником численной нестабильности в этой программе TF2.

Почему эта Log -операция выдает -∞? Ответ на этот вопрос требует изучения входных данных в op. Щелчок по имени тензора ( Log:0 ) вызывает простую, но информативную визуализацию окрестности операции Log на его графике TensorFlow в разделе «Структура графика». Обратите внимание на направление потока информации сверху вниз. Сама операция выделена жирным шрифтом посередине. Непосредственно над ним мы видим, что операция Placeholder предоставляет единственный вход для операции Log . Где находится тензор, сгенерированный этим probs в списке выполнения графа? Используя желтый цвет фона в качестве наглядного пособия, мы можем видеть, что тензор probs:0 находится на три строки выше тензора Log:0 , то есть в строке 85.

Более внимательный взгляд на числовую разбивку тензора probs:0 в строке 85 показывает, почему его потребитель Log:0 выдает -∞: среди 1000 элементов probs:0 один элемент имеет значение 0. -∞ результат вычисления натурального логарифма 0! Если мы каким-то образом сможем обеспечить, чтобы Log подвергалась воздействию только положительных входных данных, мы сможем предотвратить возникновение NaN/∞. Этого можно добиться, применив отсечение (например, с помощью tf.clip_by_value() ) к тензору проб- probs .
Мы приближаемся к решению проблемы, но еще не все сделано. Чтобы применить исправление, нам нужно знать, где в исходном коде Python возникла операция Log и ее ввод-заполнитель. Debugger V2 обеспечивает первоклассную поддержку для отслеживания операций графа и событий выполнения до их источника. Когда мы щелкнули тензор Log:0 в Graph Executions, раздел Stack Trace был заполнен исходной трассировкой стека создания операции Log . Трассировка стека несколько велика, потому что она включает в себя множество кадров из внутреннего кода TensorFlow (например, gen_math_ops.py и dumping_callback.py), которые мы можем спокойно игнорировать для большинства задач отладки. Нас интересует строка 216 файла debug_mnist_v2.py (т. е. файл Python, который мы фактически пытаемся отлаживать). Нажатие «Строка 216» открывает представление соответствующей строки кода в разделе «Исходный код».

Это, наконец, подводит нас к исходному коду, который создал проблемную операцию Log на основе входных данных probs . Это наша пользовательская категориальная функция кросс-энтропийных потерь, украшенная @tf.function и, следовательно, преобразованная в граф TensorFlow. Заполнитель op probs соответствует первому входному аргументу функции потерь. Оператор Log создается с помощью API-вызова tf.math.log().
Исправление этой ошибки с отсечением значений будет выглядеть примерно так:
diff = -(labels *
tf.math.log(tf.clip_by_value(probs), 1e-6, 1.))
Это устранит числовую нестабильность в этой программе TF2 и приведет к успешному обучению MLP. Другой возможный подход к устранению численной нестабильности — использовать tf.keras.losses.CategoricalCrossentropy .
На этом мы завершаем наш путь от наблюдения за ошибкой модели TF2 до внесения изменений в код, которые устраняют ошибку, с помощью инструмента Debugger V2, который обеспечивает полную видимость нетерпеливой и графической истории выполнения инструментированной программы TF2, включая числовые сводки. значений тензоров и связи между операциями, тензорами и их исходным кодом.
Аппаратная совместимость Debugger V2
Debugger V2 поддерживает основное обучающее оборудование, включая ЦП и ГП. Также поддерживается обучение с использованием нескольких графических процессоров с помощью tf.distributed.MirroredStrategy . Поддержка TPU все еще находится на ранней стадии и требует звонка
tf.config.set_soft_device_placement(True)
перед вызовом enable_dump_debug_info() . У него могут быть и другие ограничения для TPU. Если у вас возникнут проблемы с использованием Debugger V2, сообщите об ошибках на нашей странице проблем GitHub .
API-совместимость Debugger V2
Debugger V2 реализован на относительно низком уровне программного стека TensorFlow и, следовательно, совместим с tf.keras , tf.data и другими API, созданными поверх более низких уровней TensorFlow. Debugger V2 также обратно совместим с TF1, хотя временная шкала Eager Execution Timeline будет пустой для журналов отладки, созданных программами TF1.
Советы по использованию API
Часто задаваемый вопрос об этом API отладки заключается в том, где в коде TensorFlow следует вставить вызов enable_dump_debug_info() . Как правило, API следует вызывать как можно раньше в вашей программе TF2, предпочтительно после строк импорта Python и до начала построения и выполнения графа. Это обеспечит полный охват всех операций и графиков, на которых основана ваша модель и ее обучение.
В настоящее время поддерживаются следующие режимы tensor_debug_modes: NO_TENSOR , CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH и SHAPE . Они различаются объемом информации, извлекаемой из каждого тензора, и производительностью отлаживаемой программы. Пожалуйста, обратитесь к разделу args документации enable_dump_debug_info() .
Накладные расходы на производительность
API отладки увеличивает производительность инструментированной программы TensorFlow. Накладные расходы зависят от tensor_debug_mode , типа оборудования и характера инструментированной программы TensorFlow. В качестве ориентира на графическом процессоре режим NO_TENSOR добавляет 15 % накладных расходов во время обучения модели Transformer при размере партии 64. Накладные расходы в процентах для других режимов tensor_debug_modes выше: примерно 50 % для CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH и SHAPE . режимы. На процессорах накладные расходы немного ниже. На TPU накладные расходы в настоящее время выше.
Отношение к другим API-интерфейсам отладки TensorFlow
Обратите внимание, что TensorFlow предлагает другие инструменты и API для отладки. Вы можете просматривать такие API в пространстве имен tf.debugging.* на странице документации по API. Среди этих API наиболее часто используется tf.print() . Когда следует использовать Debugger V2, а когда вместо него следует использовать tf.print() ? tf.print() удобен в случае, когда
- мы точно знаем, какие тензоры печатать,
- мы знаем, где именно в исходном коде вставлять эти
tf.print(), - число таких тензоров не слишком велико.
Для других случаев (например, проверки многих значений тензора, проверки значений тензора, сгенерированных внутренним кодом TensorFlow, и поиска источника численной нестабильности, как мы показали выше), Debugger V2 предоставляет более быстрый способ отладки. Кроме того, Debugger V2 предоставляет унифицированный подход к проверке нетерпеливых и графовых тензоров. Он дополнительно предоставляет информацию о структуре графа и расположении кода, которые выходят за рамки возможностей tf.print() .
Другой API, который можно использовать для отладки проблем, связанных с ∞ и NaN, — это tf.debugging.enable_check_numerics() . В отличие от enable_dump_debug_info() , enable_check_numerics() не сохраняет отладочную информацию на диск. Вместо этого он просто отслеживает ∞ и NaN во время выполнения TensorFlow и выдает ошибки с расположением исходного кода, как только какая-либо операция генерирует такие неверные числовые значения. Он имеет меньшую нагрузку на производительность по сравнению с enable_dump_debug_info() , но не обеспечивает полной трассировки истории выполнения программы и не имеет графического пользовательского интерфейса, такого как Debugger V2.