Существует 4 способа автоматического вычисления градиентов при активном выполнении (на самом деле, они также работают в графическом режиме):
tf.GradientTape
контекст записывает вычисления, так что вы можете вызвать tfe.gradient()
, чтобы получить градиенты любого тензора, вычисленные при записи в отношении любой обучаемой переменной.
tfe.gradients_function()
принимает функцию (скажем, f()
) и возвращает функцию градиента (скажем, fg()
), которая может вычислять градиенты выходных данных f()
в отношении параметров f()
(или их подмножество).
tfe.implicit_gradients()
очень похож, но fg()
вычисляет градиенты выходных данных f()
относительно всех обучаемых переменных, от которых зависят эти выходные данные.
tfe.implicit_value_and_gradients()
почти идентичен, но fg()
также возвращает вывод функции f()
.
Обычно в машинном обучении вам нужно вычислять градиенты потерь в зависимости от параметров модели (т. Е. Переменных), и вас, как правило, также интересует значение самой потери. Для этого варианта использования наиболее простыми и эффективными являются tf.GradientTape
и tfe.implicit_value_and_gradients()
(два других варианта не дают значения самой потери, поэтому, если она вам понадобится, для нее потребуются дополнительные вычисления). Я лично предпочитаю tfe.implicit_value_and_gradients()
при написании рабочего кода и tf.GradientTape
при экспериментах с ноутбуком Jupyter.
Редактировать : В TF 2.0 кажется, что остается только tf.GradientTape
. Возможно, другие функции будут добавлены обратно, но я бы на это не рассчитывал.
Подробный пример
Давайте создадим небольшую функцию для выделения различий:
import tensorflow as tf
import tensorflow.contrib.eager as tfe
tf.enable_eager_execution()
w1 = tfe.Variable(2.0)
w2 = tfe.Variable(3.0)
def weighted_sum(x1, x2):
return w1 * x1 + w2 * x2
s = weighted_sum(5., 7.)
print(s.numpy()) # 31
Использование tf.GradientTape
В контексте GradientTape
все операции записываются, после чего вы можете вычислить градиенты любого тензора, вычисленного в контексте, в отношении любой обучаемой переменной. Например, этот код вычисляет s
в контексте GradientTape
, а затем вычисляет градиент s
относительно w1
. Начиная с s = w1 * x1 + w2 * x2
, градиент s
относительно w1
составляет x1
:
with tf.GradientTape() as tape:
s = weighted_sum(5., 7.)
[w1_grad] = tape.gradient(s, [w1])
print(w1_grad.numpy()) # 5.0 = gradient of s with regards to w1 = x1
Использование tfe.gradients_function()
Эта функция возвращает другую функцию, которая может вычислять градиенты возвращаемого значения функции относительно ее параметров. Например, мы можем использовать его для определения функции, которая будет вычислять градиенты s
относительно x1
и x2
:
grad_fn = tfe.gradients_function(weighted_sum)
x1_grad, x2_grad = grad_fn(5., 7.)
print(x1_grad.numpy()) # 2.0 = gradient of s with regards to x1 = w1
В контексте оптимизации было бы более разумно вычислять градиенты в зависимости от переменных, которые мы можем настроить. Для этого мы можем изменить функцию weighted_sum()
так, чтобы она принимала в качестве параметров w1
и w2
, и указать tfe.gradients_function()
, чтобы она учитывала только параметры с именами "w1"
и "w2"
:
def weighted_sum_with_weights(w1, x1, w2, x2):
return w1 * x1 + w2 * x2
grad_fn = tfe.gradients_function(weighted_sum_with_weights, params=["w1", "w2"])
[w1_grad, w2_grad] = grad_fn(w1, 5., w2, 7.)
print(w2_grad.numpy()) # 7.0 = gradient of s with regards to w2 = x2
Использование tfe.implicit_gradients()
Эта функция возвращает другую функцию, которая может вычислять градиенты возвращаемого значения функции относительно всех обучаемых переменных, от которых она зависит. Возвращаясь к первой версии weighted_sum()
, мы можем использовать ее для вычисления градиентов s
по w1
и w2
без явной передачи этих переменных. Обратите внимание, что функция градиента возвращает список пар градиент / переменная:
grad_fn = tfe.implicit_gradients(weighted_sum)
[(w1_grad, w1_var), (w2_grad, w2_var)] = grad_fn(5., 7.)
print(w1_grad.numpy()) # 5.0 = gradient of s with regards to w1 = x1
assert w1_var is w1
assert w2_var is w2
Эта функция кажется самой простой и наиболее полезной опцией, поскольку, как правило, мы заинтересованы в вычислении градиентов потерь с учетом параметров модели (т. Е. Переменных).
Примечание: попробуйте сделать w1
неотслеживаемым (w1 = tfe.Variable(2., trainable=False)
) и переопределить weighted_sum()
, и вы увидите, что grad_fn
возвращает только градиент s
относительно w2
.
Использование tfe.implicit_value_and_gradients()
Эта функция почти идентична implicit_gradients()
, за исключением того, что созданная ею функция также возвращает результат дифференцируемой функции (в данном случае weighted_sum()
):
grad_fn = tfe.implicit_value_and_gradients(weighted_sum)
s, [(w1_grad, w1_var), (w2_grad, w2_var)] = grad_fn(5., 7.)
print(s.numpy()) # 31.0 = s = w1 * x1 + w2 * x2
Когда вам нужен как вывод функции, так и ее градиенты, эта функция может дать вам хороший прирост производительности, так как вы получаете выход функции бесплатно при вычислении градиентов с использованием автодифференцирования.