Есть вопрос? Присоединяйтесь к сообществу на форуме TensorFlow. Посетите форум.

Перенос использования tf.summary на TF 2.0

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот
import tensorflow as tf

TensorFlow 2.0 включает значительные изменения в API tf.summary используется для записи сводных данных для визуализации в TensorBoard.

Что изменилось

Полезно tf.summary API tf.summary как два суб-API:

  • Набор операций для записи отдельных сводок - summary.scalar() , summary.histogram() , summary.image() , summary.audio() иsummary.text() которые вызываются встроенными из кода вашей модели.
  • Написание логики, которая собирает эти отдельные сводки и записывает их в специально отформатированный файл журнала (который затем TensorBoard считывает для создания визуализаций).

В TF 1.x

Две половины пришлось вручную соединить вместе - путем получения итоговых результатов операции через Session.run() и вызова FileWriter.add_summary(output, step) . Операция v1.summary.merge_all() упростила это за счет использования коллекции графов для агрегирования всех итоговых результатов операции, но этот подход по-прежнему плохо работал для активного выполнения и потока управления, что делало его особенно плохо подходящим для TF 2.0.

В TF 2.X

Две половины тесно интегрированы, и теперь отдельные tf.summary сразу же записывают свои данные при выполнении. Использование API из кода вашей модели все еще должно выглядеть знакомо, но теперь он удобен для активного выполнения, оставаясь совместимым с графическим режимом. Интеграция обеих половин API означает summary.FileWriter теперь является частью контекста выполнения tf.summary и к нему напрямую tf.summary ops, поэтому настройка писателей - это основная часть, которая выглядит иначе.

Пример использования с нетерпеливым исполнением, по умолчанию в TF 2.0:

writer = tf.summary.create_file_writer("/tmp/mylogs/eager")

with writer.as_default():
  for step in range(100):
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)
    writer.flush()
ls /tmp/mylogs/eager
events.out.tfevents.1617793798.kokoro-gcp-ubuntu-prod-2000985207.3796.5.v2

Пример использования с выполнением графа tf.function:

writer = tf.summary.create_file_writer("/tmp/mylogs/tf_function")

@tf.function
def my_func(step):
  with writer.as_default():
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)

for step in tf.range(100, dtype=tf.int64):
  my_func(step)
  writer.flush()
ls /tmp/mylogs/tf_function
events.out.tfevents.1617793799.kokoro-gcp-ubuntu-prod-2000985207.3796.1013.v2

Пример использования с выполнением устаревшего графа TF 1.x:

g = tf.compat.v1.Graph()
with g.as_default():
  step = tf.Variable(0, dtype=tf.int64)
  step_update = step.assign_add(1)
  writer = tf.summary.create_file_writer("/tmp/mylogs/session")
  with writer.as_default():
    tf.summary.scalar("my_metric", 0.5, step=step)
  all_summary_ops = tf.compat.v1.summary.all_v2_summary_ops()
  writer_flush = writer.flush()


with tf.compat.v1.Session(graph=g) as sess:
  sess.run([writer.init(), step.initializer])

  for i in range(100):
    sess.run(all_summary_ops)
    sess.run(step_update)
    sess.run(writer_flush)
ls /tmp/mylogs/session
events.out.tfevents.1617793799.kokoro-gcp-ubuntu-prod-2000985207.3796.1446.v2

Преобразование вашего кода

Преобразование существующего использования tf.summary в API TF 2.0 нельзя надежно автоматизировать, поэтому сценарий tf_upgrade_v2 просто переписывает все это в tf.compat.v1.summary . Для перехода на TF 2.0 вам необходимо адаптировать код следующим образом:

  1. Для использования сводных операций должен присутствовать писатель по умолчанию, установленный через .as_default()

    • Это означает активное выполнение операций или использование операций при построении графа.
    • Без писателя по умолчанию сводные операции становятся беззвучными.
    • @tf.function умолчанию (пока) не распространяются через @tf.function выполнения @tf.function - они обнаруживаются только при трассировке функции, поэтому лучше всего вызывать writer.as_default() в теле функции и гарантировать, что объект записи продолжает существовать, пока используется @tf.function
  2. Значение "шага" должно быть передано в каждую операцию через аргумент step

    • TensorBoard требует значения шага для рендеринга данных как временного ряда.
    • Явная передача необходима, потому что глобальный шаг из TF 1.x был удален, поэтому каждая операция должна знать желаемую переменную шага для чтения
    • Чтобы уменьшить шаблон, экспериментальная поддержка для регистрации значения шага по умолчанию доступна как tf.summary.experimental.set_step() , но это временная функция, которая может быть изменена без уведомления.
  3. Сигнатуры функций отдельных сводных операций изменены

    • Возвращаемое значение теперь является логическим (указывает, действительно ли было написано резюме)
    • Имя второго параметра (если используется) изменилось с tensor на data
    • Параметр collections удален; коллекции только TF 1.x
    • Параметр family удален; просто используйте tf.name_scope()
  4. [Только для устаревшего режима графа / пользователей, выполняющих сеанс]

    • Сначала инициализируйте писатель с помощью v1.Session.run(writer.init())

    • Используйте v1.summary.all_v2_summary_ops() чтобы получить все сводные v1.summary.all_v2_summary_ops() TF 2.0 для текущего графика, например, чтобы выполнить их через Session.run()

    • v1.Session.run(writer.flush()) писатель с помощью v1.Session.run(writer.flush()) и аналогичным образом для close()

Если ваш код TF 1.x вместо этого использовал tf.contrib.summary API, он намного больше похож на API TF 2.0, поэтому сценарий tf_upgrade_v2 автоматизирует большинство шагов миграции (и будет tf_upgrade_v2 предупреждения или ошибки для любого использования, которое не может быть полностью выполнено). перенесено). По большей части он просто переписывает вызовы API на tf.compat.v2.summary ; если вам нужна только совместимость с TF 2.0+, вы можете удалить compat.v2 и указать его как tf.summary .

Дополнительные советы

В дополнение к критическим областям, указанным выше, также изменились некоторые вспомогательные аспекты:

  • Условная запись (например, «журнал каждые 100 шагов») получила новый вид.

    • Чтобы управлять операциями и связанным с ними кодом, оберните их в обычный оператор if (который работает в режиме ожидания и в функции @tf.function через автограф ) или tf.cond
    • Чтобы управлять только сводками, используйте новый tf.summary.record_if() контекста tf.summary.record_if() и передайте ему логическое условие по вашему выбору.
    • Они заменяют шаблон TF 1.x:

      if condition:
        writer.add_summary()
      
  • Нет прямого написания tf.compat.v1.Graph - вместо этого используйте функции трассировки

    • Выполнение графа в TF 2.0 использует @tf.function вместо явного Graph
    • В TF 2.0 используйте новые API-интерфейсы в стиле трассировки tf.summary.trace_on() и tf.summary.trace_export() для записи графиков выполненных функций.
  • Больше нет глобального кэширования записи на logdir с tf.summary.FileWriterCache

    • Пользователи должны либо реализовать собственное кэширование / совместное использование объектов записи, либо просто использовать отдельные средства записи (поддержка TensorBoard для последних находится в стадии разработки )
  • Изменилось двоичное представление файла событий.

    • TensorBoard 1.x уже поддерживает новый формат; это различие влияет только на пользователей, которые вручную анализируют сводные данные из файлов событий.
    • Сводные данные теперь хранятся в виде тензорных байтов; вы можете использовать tf.make_ndarray(event.summary.value[0].tensor) чтобы преобразовать его в numpy