"заморозить" некоторые переменные / области видимости в tenorflow: stop_gradient против передачи переменных для минимизации
Я пытаюсь реализовать Adversarial NN, который требует "заморозить" ту или иную часть графика во время чередующихся мини-пакетов обучения. Т.е. есть две подсети: G и D.
G( Z ) -> Xz
D( X ) -> Y
где функция потери G
зависит от D[G(Z)], D[X]
,
Сначала мне нужно обучить параметры в D со всеми фиксированными параметрами G, а затем параметры в G с фиксированными параметрами в D. Функция потерь в первом случае будет отрицательной функцией потерь во втором случае, и обновление должно будет применяться к параметрам первой или второй подсети.
Я видел, что тензор потока имеет tf.stop_gradient
функция. В целях обучения подсети D (нисходящего) я могу использовать эту функцию, чтобы заблокировать поток градиента для
Z -> [ G ] -> tf.stop_gradient(Xz) -> [ D ] -> Y
tf.stop_gradient
очень кратко аннотированы без встроенного примера (и пример seq2seq.py
слишком длинный и не так легко читается), но, похоже, его нужно вызывать при создании графа. Означает ли это, что если я хочу блокировать / разблокировать поток градиента в чередующихся пакетах, мне нужно заново создать и повторно инициализировать модель графа?
Также кажется, что невозможно заблокировать градиент, протекающий через сеть G (восходящий поток) посредством tf.stop_gradient
, право?
В качестве альтернативы я увидел, что список переменных можно передать вызову оптимизатора как opt_op = opt.minimize(cost, <list of variables>)
, что было бы простым решением, если бы можно было получить все переменные в областях действия каждой подсети. Можно ли получить <list of variables>
для tf.scope?
4 ответа
Как вы упоминаете в своем вопросе, самый простой способ добиться этого - создать две операции оптимизатора, используя отдельные вызовы для opt.minimize(cost, ...)
, По умолчанию оптимизатор будет использовать все переменные в tf.trainable_variables()
, Если вы хотите отфильтровать переменные в определенной области, вы можете использовать scope
аргумент tf.get_collection()
следующее:
optimizer = tf.train.AdagradOptimzer(0.01)
first_train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
"scope/prefix/for/first/vars")
first_train_op = optimizer.minimize(cost, var_list=first_train_vars)
second_train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
"scope/prefix/for/second/vars")
second_train_op = optimizer.minimize(cost, var_list=second_train_vars)
Ответ @mrry совершенно правильный и, возможно, более общий, чем то, что я собираюсь предложить. Но я думаю, что более простой способ сделать это - просто передать ссылку на Python напрямую var_list
:
W = tf.Variable(...)
C = tf.Variable(...)
Y_est = tf.matmul(W,C)
loss = tf.reduce_sum((data-Y_est)**2)
optimizer = tf.train.AdamOptimizer(0.001)
# You can pass the python object directly
train_W = optimizer.minimize(loss, var_list=[W])
train_C = optimizer.minimize(loss, var_list=[C])
У меня есть автономный пример здесь: https://gist.github.com/ahwillia/8cedc710352eb919b684d8848bc2df3a
Другой вариант, который вы можете рассмотреть, - это установить trainable=False для переменной. Что означает, что это не будет изменено обучением.
tf.Variable(my_weights, trainable=False)
Я не знаю, есть ли у моего подхода отрицательные стороны, но я решил эту проблему для себя с помощью этой конструкции:
do_gradient = <Tensor that evaluates to 0 or 1>
no_gradient = 1 - do_gradient
wrapped_op = do_gradient * original + no_gradient * tf.stop_gradient(original)
Так что если do_gradient = 1
, значения и градиенты будут проходить очень хорошо, но если do_gradient = 0
, тогда значения будут проходить только через операцию stop_gradient, которая остановит возврат градиентов.
Для моего сценария подключение do_gradient к индексу тензора random_shuffle позволяет мне произвольно обучать различные части моей сети.