Обычно в кодах GAN с использованием TensorFlow мы имеем следующую форму:
_, D_loss_curr, _ = sess.run(
[D_solver, D_loss, clip_D],
feed_dict = {X: X_mb, z: sample_z(mb_size, z_dim)}
)
_, G_loss_curr = sess.run(
[G_solver, G_loss],
feed_dict = {z: sample_z(mb_size, z_dim)}
)
Что можно рассматривать сначала как обучающий дискриминатор (D), а затем как обучающий генератор (G). Однако что, если мы сделаем следующее:
_, D_loss_curr, _ ,_, G_loss_curr= sess.run(
[D_solver, D_loss, clip_D,G_solver, G_loss],
feed_dict = {X: X_mb, z: sample_z(mb_size, z_dim)}
)
Означает ли это, что D и G обучаются параллельно? Или по сравнению с прошлым, G теперь становится "устаревшим" D?

Дискриминатор D и генератор G не будут обучаться параллельно при передаче списка [D_solver, D_loss, clip_D, G_solver, G_loss] в функцию sess.run(). Все операции из этого списка будут выполнены, но функция Session.run() не может гарантировать какой-либо порядок выполнения этих операций. Может случиться так, что G выполняется раньше D или наоборот.
Такое поведение Session.run () обсуждалось в предыдущих выпусках tensorflow: # 13133 и # 10860.
Редактировать: добавление ответа на перефразированный вопрос
Если вы вызовете sess.run дважды, как в вашем примере, вы сначала обучите дискриминатор D, а затем генератор G.
Если вы вызовете sess.run только один раз, со всеми операциями для D и G в списке fetches, все операции в этом списке будут выполнены в определенном порядке. Но нет гарантии, в каком именно порядке это будет. При каждом вызове сначала можно обучить D, затем G или G, а затем D.
Функция Session.run() следует порядку выполнения и не выполняет операции параллельно, то есть операции с графиком не выполняются одновременно. Этот порядок выполнения не обязательно совпадает с порядком, который вы передали в fetches=[D_solver, D_loss, clip_D, G_solver, G_loss].
Например, рассмотрим второй сценарий, в котором мы передаем все операции всего за один вызов sess.run(). На первой итерации обучения может случиться так, что сначала выполняется G_loss, после этого выполняется G_solver, затем D_solver и т. д. На второй итерации мы можем фактически получить, что сначала выполняется D_loss, затем D_solver, затем G_solver и т. д.
Пример с порядком выполнения, выполняемым sess.run на каждой итерации (без clip_D):
Итерация 1: G_loss, G_solver, D_solver, D_loss
Итерация 2: D_loss, D_solver, G_solver, G_loss
Итерация 3: G_solver, G_loss, D_loss, D_solver
Итерация 4: G_solver, G_loss, D_loss, D_solver ...
При таком подходе у вас не будет шаблонов в тренировках. Например, на первой итерации мы обучаем G перед обучением D. На второй итерации мы обучаем D без каких-либо недавних обновлений в G, то есть G не обучался между обучением D на итерациях 1 и 2.
С другой стороны, при рассмотрении первого сценария, в котором мы последовательно обучаем D и G, мы гарантируем, что на каждой итерации нашего обучения D обучается до G, и метрики для D вычисляются до обучения G, а метрики для G вычисляются после обучения D.
Отредактировал ответ на перефразированный вопрос. Надеюсь, поможет.
Спасибо @K. Богдан. Я все еще немного запутался, вот что я пытаюсь сделать. Я хочу рассчитать потерю D и G, а затем одновременно воспроизвести градиенты обоих. То есть я не хочу, чтобы G использовал обновленную D при расчете потерь. Я могу легко сделать это в pytorch, поместив D_loss.backward (), D_solver.step (), G_loss.backward (), G_solver.step () последовательно после вычисления потерь D и G. Вот почему я думал, что смогу это сделать. в TF, как я уже упоминал. Любая помощь будет принята с благодарностью.
Думаю, вы можете проверить Распределенный тензорный поток на это. Но зачем вам одновременно распространять градиенты G и D в обратном направлении?
@ k-bogdan Это связано с проектом, над которым я работаю, по сути, чтобы убедиться, что D и G обучены таким образом, чтобы гарантировать, что градиенты G не основаны на обновленном D, или, другими словами, что D не получить обновленные градиенты перед вычислением потерь G. Просто интересно, сработает ли группировка функций потерь с помощью tf.group ()?
возможно, лучше сформулированный вопрос: если я вызываю sessions.run дважды, один раз с обновлением до D (D_solver, D_loss) и один раз с обновлением до G (G_solver, G_loss), чем это отличается от однократного вызова sessions.run с D и G в такое же условие (D_solver, D_loss, G_solver, G_loss). Насколько я понимаю, двойной вызов sessions.run приведет к последовательному выполнению D и G, а вызов его только один раз со всеми параметрами приведет к одновременному выполнению D и G (т.е. веса D будут обновляться одновременно с G, тогда как при последовательном обновлении веса D будут обновляться первыми, а G - позже).