TensorFlow представляет механизм динамического графа Eager Execution

TensorFlow Python PyTorch API
Динамический график PyTorch всегда был желанной функцией для пользователей TensorFlow, и Google пытается реализовать аналогичную функцию в TensorFlow. Недавно команда Google Brain выпустила Eager Execution, новый интерфейс, определяемый во время выполнения, который значительно упрощает разработку TensorFlow. После запуска инструмента разработчик Google Ярослав Булатов провел параллельное сравнение его производительности с PyTorch.

Сегодня мы представляем «Eager Execution» для TensorFlow, императивный, определяемый запуском интерфейс, операции которого выполняются сразу после вызова из Python. Это упрощает начало работы с TensorFlow и делает разработку более интуитивно понятной.

Преимущества Eager Execution заключаются в следующем:

  • Быстрая отладка немедленных ошибок во время выполнения и интеграция с инструментами Python
  • Поддерживает динамические модели с простым в использовании потоком управления Python
  • Мощная поддержка пользовательских градиентов и градиентов высокого порядка
  • Работает практически со всеми доступными операциями TensorFlow.

Eager Execution в настоящее время находится в стадии бета-тестирования, поэтому мы хотим получить отзывы от сообщества, которые помогут нам в нашем направлении.

Чтобы лучше понять Eager Execution, давайте посмотрим на код. Это технический вопрос, и знакомство с TensorFlow поможет.


Использование нетерпеливого исполнения

Когда вы запускаете Eager Execution, операции выполняются немедленно и их значения могут быть возвращены в Python без Session.run(). Например, чтобы перемножить две матрицы, мы пишем такой код:

Проверить промежуточные результаты с помощью печати или отладчика Python несложно.

Динамические модели можно создавать с помощью потока управления Python. Вот пример гипотезы Коллатца с использованием арифметических операций TensorFlow:

Здесь использование тензорных объектов tf.constant(12) преобразует все математические операции в тензорные, так что все возвращаемые значения будут тензорными.


градиент

Большинство пользователей TensorFlow заинтересованы в автоматическом дифференцировании. Поскольку у каждого вызова могут быть разные операции, можно понять, что мы записываем все прямые операции на «ленту», а затем «обратные» при расчете градиента. После завершения расчета градиента «лента» бесполезна.

Если вы знакомы с пакетом autograd, предоставляемый нами API очень похож. Например:

Вызов градиентной_функции принимает функцию квадрата Python в качестве аргумента и возвращает вызываемый объект Python, который вычисляет частную производную квадрата () входных данных. Итак, чтобы получить производную от квадрата(), когда вход равен 3,0, активируйте grad(3,0), что равно 6.

Тот же вызов функции gradient_function можно использовать для вычисления второй производной функции Square().

Как упоминалось ранее, поток управления вызывает разные операции, вот пример:

пользовательский градиент

Пользователи могут захотеть настроить градиенты для операций или функций. Одна из причин, по которой это может быть полезно, заключается в том, что она обеспечивает более эффективный и численно стабильный градиент для последовательности операций.

В приведенном ниже примере используется пользовательский градиент. Давайте сначала посмотрим на функцию log(1 + e^x), которая обычно используется для вычисления кросс-энтропии и логарифмического правдоподобия.

Мы можем применить собственный градиент к вышеуказанной функции, упростив выражение градиента. Обратите внимание, что реализация функции градиента ниже повторно использует (tf.exp(x)), вычисленный в прямом проходе, избегая избыточных вычислений, тем самым повышая эффективность вычислений градиента.

Моделирование

Модели можно разделить на несколько категорий. Предлагаемая здесь модель может классифицировать стандартные рукописные цифры MNIST путем создания простой двухслойной сети.

Мы рекомендуем использовать категории (а не функции) в tf.layers, потому что они создают и содержат параметры модели (переменные, переменные). Срок действия переменной тесно связан со сроком действия объекта слоя, поэтому их необходимо отслеживать.

Зачем использовать tfe.Network? Сеть содержит несколько слоев, которые сами по себе являются tf.layer.Layer, что позволяет встраивать сетевые объекты в другие сетевые объекты. Он также содержит инструменты, помогающие в осмотре, консервации и ремонте.

Даже без обучения модели мы можем вызвать ее императивно и проверить вывод:

Обратите внимание, что здесь нам не нужны никакие заполнители или сеансы. После ввода данных устанавливаются параметры слоя.

Обучение любой модели требует определения функции потерь, вычисления градиентов и обновления параметров с помощью оптимизатора. Сначала определите функцию потерь:

Затем идет тренировочный цикл:

implicit_gradients() вычисляет производную функции потерь по всем переменным TensorFlow, используемым при вычислении.

Мы можем перенести вычисления на GPU так же, как обычно используем TensorFlow:

(Примечание: мы упрощаем, а затем сохраняем функцию потерь и потерь и вызываем optimizer.minimize напрямую, но вы также можете использовать описанный выше метод apply_gradients(), который эквивалентен.)


Использование Eager и графиков

Стремительное выполнение делает разработку и отладку более интерактивными, но граф TensorFlow также имеет много преимуществ в распределенном обучении, оптимизации производительности и развертывании производства.

Когда активное выполнение включено, код, выполняющий операцию, может также построить вычислительный граф, отображающий, когда неактивное выполнение не включено. Чтобы преобразовать модель в график, просто запустите тот же код в сеансе Python с отключенным жадным выполнением. Пример:GitHub.com/tensorflow/…. Мы можем сохранять и восстанавливать значения переменных модели из контрольных точек, что позволяет нам легко переходить между нетерпеливым (императивным) и графовым (декларативным) программированием. Таким образом, модели, разработанные с активным немедленным выполнением, можно легко экспортировать в производственные среды.

В ближайшем будущем мы предоставим инструменты для выборочного преобразования частей модели в графики. Таким образом, вы можете объединять части вычислений (например, внутреннюю часть пользовательских ячеек RNN) для достижения высокой производительности, сохраняя при этом гибкость и удобочитаемость быстрого выполнения.


Как я могу переписать свой код?

Использование выполнения Eager должно быть интуитивно понятным для существующих пользователей TensorFlow. В настоящее время существует лишь несколько нетерпеливых API; большинство существующих API и операций должны работать с включенным нетерпеливым. Помните следующее:

  • Для TensorFlow в целом мы рекомендуем переключиться с очередей на использование tf.data для обработки ввода, если вы еще этого не сделали. Это проще в использовании и быстрее. Посмотрите этот пост в блоге (Developers.Google blog.com/2017/09/int…) и страницу документации (woohoo.tensorflow.org/programmers…) помог бы.
  • Используйте целевые слои (такие как tf.layer.Conv2D() или слои Keras), которые могут хранить переменные напрямую.
  • Вы можете написать код для большинства моделей, который одинаково хорошо подходит для быстрого выполнения и построения графа. Есть некоторые исключения, такие как динамические модели, которые используют поток управления Python для изменения вычислений на основе ввода.
  • После вызова tfe.enable_eager_execution() ее нельзя отключить. Чтобы получить поведение графа, необходимо установить новый сеанс Python.


начать использовать

Это всего лишь пре-релиз, не идеальный. Если вы хотите начать прямо сейчас, то:

  • Установите ночную версию TensorFlow (GitHub.com/tensorflow/…)
  • Ознакомьтесь с README (включая известные проблемы) по адресу:GitHub.com/tensorflow/…
  • Из руководства пользователя по нетерпеливому выполнению (GitHub.com/tensorflow/…) подробные инструкции
  • Взгляните на нетерпеливый пример на GitHub (GitHub.com/tensorflow/…)
  • Вовремя проверяйте журнал изменений (GitHub.com/tensorflow/…) для проверки обновлений


Тестирование производительности

Eager Execution в настоящее время находится только на ранней стадии разработки. Какова его производительность? Инженер Google Brain Ярослав Булатов сделал обзор нового инструмента.

Одним из самых больших критических замечаний в адрес TensorFlow было то, что он должен был определять вычисления как статические графы.

Частью нашей работы в Google Brain является удовлетворение таких потребностей и, в конечном итоге, открытие исходного кода в императивной версии. Но это зависит от частных/нестабильных API, обслуживание которых становится все более дорогим.

К счастью, PyTorch удовлетворяет потребности исследователя, а TensorFlow теперь официально поддерживает режимы выполнения без определения графов.

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

Обратите внимание, что эта операция не требует обработки графа, сессия может выполняться сразу. Чтобы применить ускорение графического процессора, сначала скопируйте тензор на указанное устройство.

код команды порта

Вы можете переписать существующий императивный код numpy/pytorch/matlab в правильные вызовы API. Например,


  • torch.sum -> tf.reduce_sum»
  • array.T -> tf.transpose(массив) и т.д.

Я использовал реализацию l-BFGS в PyTorch в качестве упражнения, и в первый раз, когда я провел два эксперимента параллельно на графическом процессоре (PyTorch и Eager), я получил одинаковый результат для первых 8 знаков после запятой. Это поразило меня, неслыханно.


Используйте существующий код на основе графа

Если ваш код не зависит от конкретного API, такого как graph_editor, вы можете использовать свой существующий код и запустить его в режиме нетерпеливого выполнения.

Существует также экспериментальная функция «graph_callable», которая принимает любой подграф тензорного потока в качестве вызываемой функции. Он все еще находится в разработке, но мне удалось получить рабочий пример, который оборачивает resnet_model в tensorflow/models в graph_callable. Ниже приведен пример обучения этой модели со случайным размером партии.

Как только эта функция будет запущена, она должна помочь повысить производительность программы, см. раздел о производительности ниже.



расширенный градиент

Новая производная от оригинальной функции tf.gradients_function отражает градиенты autograd. Вы можете вызвать "gradients_function" N раз в существующей функции, чтобы получить производную N-порядка, т.е.

Существует также примитивная функция «custom_gradient», которая упрощает создание пользовательских градиентов. Например, предположим, что мы хотим возвести функцию в квадрат, но добавить шум при обратном распространении.

Эффект следующий:


Вы увидите, что вторая версия сходится медленнее, но после сходимости обобщает лучше.

Эта коррекция градиента полезна при реализации расширенных алгоритмов оптимизации, таких как KFAC. Подумайте о том, что я сказал ранее, KFAC эквивалентен градиентному спуску с функцией активации и отбеливанием значения обратного распространения в простых сетях.

Это можно понимать как градиент, умноженный на выбеленную матрицу с обеих сторон

Предполагая, что вы сохранили эти матрицы как m1, m2, ваша пользовательская операция умножения может выглядеть так:


Обратите внимание, что функции true_grad1, true_grad2 являются реализациями операции умножения с обратным распространением, см. страницу 4 Майка Джайлза «Расширенная коллекция результатов производных матриц для алгоритмического дифференцирования в прямом и обратном режимах» (people.maths.o.AC.UK/Giles M/file…)

Вы можете восстановить исходный kfac, используя kfac_matmul вместо градиентного спуска, или вы можете попробовать новый вариант, используя импульс и Адама.

здесь(gist.GitHub.com/Йерос ав В.Б./…) содержит сквозной пример KFAC, работающего в режиме активного выполнения.


представление

Степень, в которой режим Eager Execution заставляет вашу программу выполняться немного или намного медленнее, зависит от того, выполняете ли вы вычисления с интенсивными вычислениями свертки или умножения матриц.

Выполнение чистого матричного умножения (время более 1 мс) не имеет большого значения, используете ли вы быстрый режим tensorflow, pytorch или классический режим tensorflow.

С другой стороны, сквозные примеры более восприимчивы.

В тестах Eager Execution был на 20% медленнее, чем PyTorch, когда время выполнения было установлено на O(n^(1.5)) операций, таких как matmul/conv, или в случае большого количества операций O(n), таких как сложение векторов, 2 -5 раз медленнее, чем PyTorch.

В качестве простого примера мы используем UFLDL, предложенный Эндрю Нг, для обучения автоэнкодера MNIST. При размере пакета=60k и истории I-BFGS=5 много вычислительной мощности тратится на прямое распространение автоэнкодера, а версия Eager в 1,4 раза медленнее, чем PyTorch.

При настройке размера пакета 60 КБ и истории = 100 I-BFGS два цикла выполняют «двухэтапную рекурсию» на каждом этапе I-BFGS (точечный продукт и сложение векторов), а скорость модели версии Eager снижается. в 2,5 раза, тогда как PyTorch пострадал незначительно.

Наконец, если мы уменьшим размер пакета до 10 КБ, мы увидим, что каждая итерация выполняется в 5 раз медленнее, а иногда даже в 10 раз медленнее, вероятно, из-за стратегии сборки мусора.

в заключении

Хотя Eager Execution еще недостаточно мощен, этот режим выполнения может значительно упростить прототипирование. Этот подход вскоре станет основным для разработчиков, создающих новые вычислительные задачи в TensorFlow.


Оригинальный адрес:


Из Google Мозг

Асим Шанкар и Вольф Добсон

Сборник "Сердце машины"


Эта статья составлена ​​для ядра машины, пожалуйста, свяжитесь с этой общедоступной учетной записью для разрешения на перепечатку.