GANs
Keras
machine learning
neural networks
optimization

When training GANs in Keras, are multiple passes required to optimize the generator and discriminator?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

GAN training is not a single optimization problem; it is an alternating game between two models. Because of that, people often ask whether the discriminator and generator each need multiple passes every iteration. The practical answer is no fixed rule is required, but multiple updates on one side can be useful when the two models become unbalanced.

The Basic GAN Training Pattern

A standard GAN step has two phases:

  1. update the discriminator so it better separates real and fake samples
  2. update the generator so it better fools the discriminator

In Keras or raw TensorFlow, the simplest loop does one discriminator update and one generator update per batch. That is already a valid GAN training scheme.

Here is a minimal runnable example with tiny dense models:

python
1import tensorflow as tf
2from tensorflow import keras
3
4latent_dim = 8
5
6generator = keras.Sequential([
7    keras.layers.Input(shape=(latent_dim,)),
8    keras.layers.Dense(16, activation="relu"),
9    keras.layers.Dense(4),
10])
11
12discriminator = keras.Sequential([
13    keras.layers.Input(shape=(4,)),
14    keras.layers.Dense(16, activation="relu"),
15    keras.layers.Dense(1),
16])
17
18g_optimizer = keras.optimizers.Adam(1e-3)
19d_optimizer = keras.optimizers.Adam(1e-3)
20loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
21
22real_batch = tf.random.normal((32, 4))
23
24noise = tf.random.normal((32, latent_dim))
25with tf.GradientTape() as d_tape:
26    fake_batch = generator(noise, training=True)
27    real_logits = discriminator(real_batch, training=True)
28    fake_logits = discriminator(fake_batch, training=True)
29    d_loss_real = loss_fn(tf.ones_like(real_logits), real_logits)
30    d_loss_fake = loss_fn(tf.zeros_like(fake_logits), fake_logits)
31    d_loss = d_loss_real + d_loss_fake
32
33d_grads = d_tape.gradient(d_loss, discriminator.trainable_weights)
34d_optimizer.apply_gradients(zip(d_grads, discriminator.trainable_weights))
35
36noise = tf.random.normal((32, latent_dim))
37with tf.GradientTape() as g_tape:
38    fake_batch = generator(noise, training=True)
39    fake_logits = discriminator(fake_batch, training=True)
40    g_loss = loss_fn(tf.ones_like(fake_logits), fake_logits)
41
42g_grads = g_tape.gradient(g_loss, generator.trainable_weights)
43g_optimizer.apply_gradients(zip(g_grads, generator.trainable_weights))
44
45print("d_loss:", float(d_loss))
46print("g_loss:", float(g_loss))

That loop uses one pass for each network within one outer iteration.

So Are Multiple Passes Required

Not as a universal requirement. One update for D and one update for G is the baseline and often the first thing to try.

Multiple passes become useful as a training policy choice, not as a law of GANs. You might do:

  • '1D : 1G for balanced baseline training'
  • 'kD : 1G when the discriminator is undertrained'
  • '1D : kG less commonly, if the generator is lagging badly'

In Wasserstein-style training, several critic updates per generator update are common. In ordinary GAN setups, many implementations still start with one update each and then adjust only if diagnostics show imbalance.

Why Imbalance Happens

GAN losses move against each other. If the discriminator becomes too strong too quickly, the generator receives poor gradients and learns slowly. If the discriminator is too weak, the generator may exploit shallow errors and converge to poor samples.

That is why extra passes are sometimes added:

  • to strengthen the discriminator early
  • to stabilize the game when one side collapses
  • to compensate for different model capacities or learning rates

The right ratio depends on data, architecture, batch size, optimizer, and loss formulation.

Keras-Specific View

Modern Keras encourages implementing GAN training with a custom train_step or a custom loop. That design makes the update ratio explicit. You are not forced into a fixed compile-time training pattern.

A sketch looks like this:

python
1for real_batch in dataset:
2    for _ in range(d_steps):
3        train_discriminator(real_batch)
4    for _ in range(g_steps):
5        train_generator()

The important part is that d_steps and g_steps are hyperparameters. They are not mandatory values imposed by Keras.

How To Choose the Update Ratio

Start simple:

  • one discriminator step
  • one generator step
  • identical batch cadence

Then observe:

  • discriminator loss saturates near zero
  • generator samples stop improving
  • one side learns much faster than the other

If the discriminator is clearly too weak, increase discriminator steps or capacity. If it is too strong, reduce its advantage with fewer steps, lower learning rate, regularization, or a different loss setup.

In other words, extra passes are a control knob for training dynamics, not a correctness requirement.

Common Pitfalls

The most common mistake is assuming more discriminator steps are always better. An overpowered discriminator can make training less useful, not more.

Another mistake is reading raw loss values without context. GAN losses are game-dependent and do not behave like ordinary supervised learning curves.

It is also easy to freeze or unfreeze the wrong model at the wrong time in high-level Keras wrappers. In custom loops, make sure the gradients are applied only to the intended network during each phase.

Finally, do not treat update ratio as the only stability tool. Learning rates, normalization, label smoothing, gradient penalty, architecture choices, and data preprocessing can matter just as much.

Summary

  • GANs are trained by alternating discriminator and generator updates.
  • One discriminator step and one generator step per batch is a valid default.
  • Multiple passes are optional tuning choices, not a universal requirement.
  • Extra discriminator or generator steps are used when training becomes unbalanced.
  • In Keras, custom train_step logic makes these update ratios explicit and adjustable.

Course illustration
Course illustration

All Rights Reserved.