Одновременное обучение дискриминатора и генератора (Tensorflow)

Обычно в кодах 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?

Udacity Nanodegree Capstone Project: Классификатор пород собак
Udacity Nanodegree Capstone Project: Классификатор пород собак
Вы можете ознакомиться со скриптами проекта и данными на github .
0
0
718
1

Ответы 1

Дискриминатор 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.

возможно, лучше сформулированный вопрос: если я вызываю 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 - позже).

lvdp 17.12.2018 21:53

Отредактировал ответ на перефразированный вопрос. Надеюсь, поможет.

K. Bogdan 20.12.2018 14:17

Спасибо @K. Богдан. Я все еще немного запутался, вот что я пытаюсь сделать. Я хочу рассчитать потерю D и G, а затем одновременно воспроизвести градиенты обоих. То есть я не хочу, чтобы G использовал обновленную D при расчете потерь. Я могу легко сделать это в pytorch, поместив D_loss.backward (), D_solver.step (), G_loss.backward (), G_solver.step () последовательно после вычисления потерь D и G. Вот почему я думал, что смогу это сделать. в TF, как я уже упоминал. Любая помощь будет принята с благодарностью.

lvdp 21.12.2018 03:52

Думаю, вы можете проверить Распределенный тензорный поток на это. Но зачем вам одновременно распространять градиенты G и D в обратном направлении?

K. Bogdan 22.12.2018 13:30

@ k-bogdan Это связано с проектом, над которым я работаю, по сути, чтобы убедиться, что D и G обучены таким образом, чтобы гарантировать, что градиенты G не основаны на обновленном D, или, другими словами, что D не получить обновленные градиенты перед вычислением потерь G. Просто интересно, сработает ли группировка функций потерь с помощью tf.group ()?

lvdp 24.12.2018 18:49

Другие вопросы по теме