Ismail Ben Alla

GAN Workbench

Ten GAN Variants on One Training Loop

Generative AI · Oct 2026 · Completed

Ten GAN variants behind one training loop: same data, same budget, same metrics, so only the method changes.

Built withPyTorchtorchvisionMatplotlibpytestKaggle

GAN Workbench — overview

Overview

A GAN is two networks trained against each other: a generator that turns noise into images and a discriminator that tries to tell them from real ones. Since 2014 that game has been patched many times with new losses, new networks and new ways to keep the discriminator in check, and the papers rarely compare under the same conditions. I wanted to watch each patch work on its own, so I wrote ten variants in plain PyTorch: the original GAN, DCGAN, conditional GAN, ACGAN, LSGAN, WGAN, WGAN-GP, SNGAN (hinge loss with spectral norm), pix2pix and CycleGAN.

They share one training loop, one data pipeline and one evaluation. A method is a small object that defines one discriminator step and one generator step; the loop decides when they run and what is measured, so switching method is one flag and every run gets the same seeds, data order and number of generator updates. The networks are small on purpose (0.1-3 M parameters) so that each mechanism can be watched on a CPU in minutes, and a Kaggle/Colab notebook runs the same code longer on a GPU.

On MNIST, conditioning won (cGAN FID 29.6, ACGAN 44.1), spectral norm with the hinge loss was the best unconditional method (57.5), and WGAN with weight clipping failed to train in the budget (1035.5) until the gradient penalty replaced clipping (127.2). On synthetic shapes, pix2pix learned photo-to-mask almost perfectly (IoU 0.977) while CycleGAN found the known 'steganography' shortcut and reached only IoU 0.28. The repo grew from two coursework notebooks; their eleven bugs, including optimisers built for networks that were then replaced, are documented and covered by tests.

The question

If ten GAN variants share the same data, budget, loop and metrics, which of their differences (loss, conditioning, Lipschitz control, convolutions) actually matter on a CPU-sized run?

Key features

How it works

  1. One loop

    Each step runs n_critic discriminator updates and then one generator update, optionally updates an EMA copy of G, and logs the losses, D(x), D(G(z)) and the update ratios. The loop decides when things run; the method decides what they are.

  2. A method is two steps

    NoiseGAN, Pix2Pix and CycleGAN each define a D step and a G step from a METHODS table of networks, loss (BCE, least squares, Wasserstein, hinge), conditioning, Lipschitz control and optimiser defaults.

  3. Keep D in check

    DCGAN relies on BatchNorm; WGAN clips every critic weight to ±0.01 after each step; WGAN-GP penalises the gradient norm on real/fake interpolates and drops BatchNorm; SNGAN divides every D layer by its largest singular value.

  4. Image to image

    pix2pix uses a U-Net generator, a PatchGAN discriminator that judges (input, output) pairs and a 100x L1 term. CycleGAN uses two U-Nets, two PatchGANs, a least-squares loss and a 10x cycle L1, with no pairs at all.

  5. Evaluate

    5,000 samples are compared with 5,000 test images in the 128-d feature space of the small classifier: Fréchet distance, an IS analogue, the number of classes that receive at least 1/(4K) of the samples, and label accuracy for conditional models.

Examples

Training progress on MNIST for DCGAN, cGAN, SNGAN and WGAN-GP: the same fixed noise (and, for cGAN, the same labels) replayed through snapshots of a short CPU run, from flat grey at step 0 to digits.
pix2pix on held-out test photos of shapes. Rows: input photo, the generator's output and the true mask; the middle row goes from flat grey at step 0 to clean masks after 800 steps.
Untrained versus trained generators with the same z: an untrained generator outputs flat grey for every input because its N(0, 0.02) weights keep the final tanh near 0.
Untrained versus trained generators with the same z: an untrained generator outputs flat grey for every input because its N(0, 0.02) weights keep the final tanh near 0.
Samples of the eight noise-to-image methods after 2,000 generator steps on MNIST. WGAN with weight clipping (FID 1035.5) stays grey and collapsed; cGAN (29.6) and ACGAN (44.1) draw the requested digit.
Samples of the eight noise-to-image methods after 2,000 generator steps on MNIST. WGAN with weight clipping (FID 1035.5) stays grey and collapsed; cGAN (29.6) and ACGAN (44.1) draw the requested digit.
CycleGAN round trip after 2,000 steps. Rows: a, G_AB(a), G_BA(G_AB(a)), the true outline. G_AB hides the filled shape in a faint pattern that G_BA reads back, so the cycle error is tiny (0.013) while IoU is only 0.28.
CycleGAN round trip after 2,000 steps. Rows: a, G_AB(a), G_BA(G_AB(a)), the true outline. G_AB hides the filled shape in a faint pattern that G_BA reads back, so the cycle error is tiny (0.013) while IoU is only 0.28.

Input

gans-train --method cgan --dataset mnist --steps 2000 && gans-eval checkpoints/cgan/last.pt

Output

FID (clf) 29.64, IS (clf) 8.02, modes 10/10, label accuracy 87.5% (0.27 M / 0.04 M parameters, 9.7 min on one CPU thread)

The cGAN row of the results table. The FID is computed in the feature space of a small MNIST classifier, not Inception, so it only ranks runs within this repo.

Tech stack

What I learned

Limitations

Keywords

GAN · generative adversarial networks · DCGAN · conditional GAN · ACGAN · LSGAN · WGAN · WGAN-GP · gradient penalty · spectral normalization · hinge loss · pix2pix · CycleGAN · image-to-image translation · mode collapse · FID · MNIST · PyTorch · generative models