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

С появлением TensorFlow 2.0 произошла смена парадигмы. В этом посте я остановлюсь на нескольких наиболее важных изменениях. К концу вы будете знать:

  • Что такое нетерпеливое исполнение;
  • Что такое AutoGraph;
  • Когда и как используются и то, и другое.

Нетерпеливое исполнение

Графики TensorFlow, которые мы рассмотрели на прошлой неделе, не очень удобны для новичков, но TensorFlow 2.0 частично снижает трудности, поскольку по умолчанию поставляется с Eager Execution.

Фактически, активное выполнение было возможно в TensorFlow 1.0, но оно было введено как «простой режим» для обучения и экспериментов. Даже сейчас, в определенной степени, так оно и есть.

Мы можем активировать нетерпеливый режим в TensorFlow 1.0 с помощью следующего кода:

# TensorFlow 1.x only
import tensorflow as tf
tf.enable_eager_execution()

Но что такое режим нетерпения?

Определение нетерпеливого исполнения или нетерпеливого режима

Вот определение нетерпеливого исполнения из официальной документации:

Стремительное исполнение - это гибкая платформа машинного обучения для исследований и экспериментов, обеспечивающая:

Интуитивно понятный интерфейс - структурируйте свой код естественным образом и используйте структуры данных Python.

Упрощенная отладка - вызовите команду напрямую, чтобы проверить работающие модели и проверить изменения.

Естественный поток управления - используйте поток управления Python вместо потока управления графом.

Чтобы увидеть, как код Eager отличается от кода графика, давайте посмотрим на них в действии:

# TensorFlow 1.+
a = tf.constant([[1, 2],[3, 4]])
print(a)
# output:
# Tensor(“Const:0”, shape=(2, 2), dtype=int32)

Как видно из приведенного выше кода, мы не можем видеть значение a, потому что оценка еще не запущена.

В режиме графика, который используется по умолчанию в TensorFlow 1.0, оценка происходит только после того, как мы заключили наш код в tf.Session.

Таким образом, оценке придется подождать, пока мы построим весь график, что довольно нелогично для новичков!

Мы также должны контролировать зависимости.

# only TensorFlow 1.x requires this statement to enable eager mode
# for TensorFlow, eager mode is enabled by default.
tf.enable_eager_execution()
a = tf.constant([[1, 2],[3, 4]])
print(a)
# output:
# tf.Tensor([[1 2][3 4]], shape=(2, 2), dtype=int32)

Теперь мы можем увидеть значение переменной a.

С Eager execution нам не нужен tf.Session для запуска нашего кода. Ого! Он ведет себя так же, как и любой другой код Python. Он прямой и интуитивно понятный. Мы можем использовать чистый Python if, while и for в потоке управления. Легко свежий.

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

  • tf.Session
  • tf.Graph
  • директивы потока управления (tf.if, tf. While, tf.control_depnedencies)
  • заполнители
  • инициализация переменной
  • совместное использование переменных

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

Линейная регрессия с нетерпеливым исполнением

Вот пример линейной регрессии в TensorFlow 2.0 с нетерпеливым исполнением. Большая часть кода довольно проста и написана на Pythonic, и только tf.GradientTape является дополнительным необходимым шагом для вычисления градиентов.

# Training Data
train_X = [3.3, 4.4, 5.5, 6.71, 6.93, 4.168, 9.779, 6.182, 7.59, 2.167,
           7.042, 10.791, 5.313, 7.997, 5.654, 9.27, 3.1]
train_Y = [1.7, 2.76, 2.09, 3.19, 1.694, 1.573, 3.366, 2.596, 2.53, 1.221,
           2.827, 3.465, 1.65, 2.904, 2.42, 2.94, 1.3]
n_samples = len(train_X)

# Parameters
learning_rate = 0.01
display_step = 100
num_steps = 1000

# Weight and Bias
W = tfe.Variable(np.random.randn())
b = tfe.Variable(np.random.randn())


# Linear regression (Wx + b)
def linear_regression(inputs):
    return inputs * W + b


# Mean square error
def mean_square_fn(model_fn, inputs, labels):
    return tf.reduce_sum(tf.pow(model_fn(inputs) - labels, 2)) / (2 * n_samples)


# SGD Optimizer
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
# Compute gradients
grad = tfe.implicit_gradients(mean_square_fn)

# Initial cost, before optimizing
print("Initial cost= {:.9f}".format(
    mean_square_fn(linear_regression, train_X, train_Y)),
    "W=", W.numpy(), "b=", b.numpy())

# Training
for step in range(num_steps):

    optimizer.apply_gradients(grad(linear_regression, train_X, train_Y))

    if (step + 1) % display_step == 0 or step == 0:
        print("Epoch:", '%04d' % (step + 1), "cost=",
              "{:.9f}".format(mean_square_fn(linear_regression, train_X, train_Y)),
              "W=", W.numpy(), "b=", b.numpy())

# Graphic display
plt.plot(train_X, train_Y, 'ro', label='Original data')
plt.plot(train_X, np.array(W * train_X + b), label='Fitted line')
plt.legend()
plt.show()

Поскольку большая часть сложности исчезла, не следует ли нам просто отказаться от Graph mode на Eager mode? Ну, держи лошадей. Как и все новинки, «Стремительный режим» пока не готов для прайм-тайма.

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

Чтобы преодолеть разрыв между режимом нетерпения и режимом графика, TensorFlow 2.0 представляет некоторые полезные функции через API AutoGraph (tf.function).

Автограф

AutoGraph сочетает в себе простоту исполнения Eager и мощь Graphs.

Короче говоря, AutoGraph - это мини-компилятор, который преобразует подмножество синтаксиса Python в переносимые высокопроизводительные графики TensorFlow. Как видно ниже, AutoGraph реализован с помощью декоратора tf.function:

@tf.function
def simpler_nn_layer(x,y):
return tf.nn.relu(tf.matmul(x,y))

Простая функция, такая как simple_nn_layer, будет перенесена в этот код графика.

Чтобы увидеть преобразованный код, мы можем использовать tf.autograph_to_code(simple_nn_layer), чтобы увидеть фактический сгенерированный код.

def tf__simpler_nn_layer(x, y):
  do_return = False
  retval_ = ag__.UndefinedReturnValue()
  with ag__.FunctionScope('simpler_nn_layer', 'simpler_nn_layer_scope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as simpler_nn_layer_scope:
    do_return = True
    retval_ = simpler_nn_layer_scope.mark_return_value(ag__.converted_call(tf.nn.relu, simpler_nn_layer_scope.callopts, (ag__.converted_call(tf.matmul, simpler_nn_layer_scope.callopts, (x, y), None, simpler_nn_layer_scope),), None, simpler_nn_layer_scope))
  do_return,
  return ag__.retval(retval_)

AutoGraph рекомендует использовать функциональный стиль и неизменяемые коллекции Python. Это не совсем красиво, но эффективно.

Чтобы узнать больше об AutoGraph, ознакомьтесь с некоторыми полезными ресурсами, доступными в Интернете, например:

В итоге

Когда мы должны использовать TensorFlow 2.0 с нетерпением исполнения, а когда мы должны использовать AutoGraph?

Я думаю, что для исследования и экспериментов «Стремительное исполнение» - идеальный выбор. А для производства на данный момент наилучшими вариантами являются графики и автографы.

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

Одна из основных претензий к TensorFlow заключается в том, что он слишком низкоуровневый. Начиная с версии 2.0, TensorFlow рекомендует пользователям воспользоваться более доступным tf.keras более высокого уровня, о котором я расскажу в части 3. Следите за обновлениями!

Спасибо за внимание! Если вам понравилась эта статья, пожалуйста, нажимайте кнопку хлопка столько раз, сколько сможете. Это будет много значить и побудит меня продолжать делиться своими знаниями.

Не стесняйтесь делиться здесь своими вопросами и комментариями и подписывайтесь на меня, чтобы не пропустить последний контент!