TensorFlow GANs Basics

A Generative Adversarial Network (GAN) is a system where two neural networks compete against each other to produce realistic synthetic data. GANs generate photorealistic human faces that do not belong to real people, create artwork in any artistic style, synthesize medical images for training datasets, and convert rough sketches into detailed photos. Understanding GANs opens one of the most creative areas in modern machine learning.

The Counterfeiter and Detective Analogy

Imagine a counterfeiter who prints fake banknotes and a detective who tries to spot the fakes. The counterfeiter studies the detective's feedback and improves the fakes. The detective studies both real and fake notes and sharpens their detection skills. They push each other to higher levels of expertise. Eventually, the counterfeiter produces banknotes so good that even the detective cannot reliably tell them apart from real ones.

In a GAN:

  • The Generator is the counterfeiter — it creates fake data
  • The Discriminator is the detective — it classifies data as real or fake

The Two Networks in Detail

The Generator

The Generator takes random noise as input (a vector of random numbers) and produces a synthetic output — an image, a sound clip, or text. It starts by generating terrible, random-looking outputs. Over thousands of training steps, it learns to produce outputs that fool the Discriminator.

Random Noise Vector        Generated Image
[0.23, -0.81, 0.55, ...]   (looks like random pixels initially)
          │
          ▼
    [Generator Network]
    Dense → Reshape → Conv2DTranspose × 3
          │
          ▼
    Synthetic Image
    (128×128 pixels)

The Discriminator

The Discriminator is a standard image classifier. It receives either a real image from your dataset or a fake image from the Generator. It outputs a single number: close to 1 means "this looks real," close to 0 means "this looks fake."

Real Image ──┐
             ├──► [Discriminator Network] ──► 0.92 (probably real)
Fake Image ──┘     Conv2D × 3 → Dense         0.03 (probably fake)

How Training Works

GAN Training Loop:
──────────────────────────────────────────────────────────
Phase 1: Train the Discriminator
  1. Take a batch of REAL images → label them 1 (real)
  2. Generate a batch of FAKE images → label them 0 (fake)
  3. Train Discriminator on both batches
  4. Update Discriminator weights to improve detection

Phase 2: Train the Generator
  1. Generate a batch of FAKE images
  2. Pass them through the (now frozen) Discriminator
  3. Label them 1 (pretend they are real — this tricks the loss)
  4. Update Generator weights to produce more convincing fakes
  5. The Generator's goal: maximize Discriminator's error

Repeat these two phases alternately for thousands of steps
──────────────────────────────────────────────────────────

Building a Simple GAN in TensorFlow

Step 1: Define the Generator

import tensorflow as tf

def build_generator(latent_dim=100):
    model = tf.keras.Sequential([
        # Accept random noise vector
        tf.keras.layers.Dense(7 * 7 * 256, activation='relu',
                              input_shape=(latent_dim,)),
        tf.keras.layers.Reshape((7, 7, 256)),

        # Upsample to 14×14
        tf.keras.layers.Conv2DTranspose(128, (4, 4), strides=2,
                                        padding='same', activation='relu'),

        # Upsample to 28×28
        tf.keras.layers.Conv2DTranspose(64, (4, 4), strides=2,
                                        padding='same', activation='relu'),

        # Final image layer
        tf.keras.layers.Conv2DTranspose(1, (7, 7), padding='same',
                                         activation='tanh')
        # tanh outputs values between -1 and 1 (image pixels)
    ])
    return model

Conv2DTranspose is the reverse of Conv2D. While Conv2D shrinks spatial dimensions, Conv2DTranspose expands them. The Generator uses Conv2DTranspose layers to grow a small latent vector into a full-sized image.

Step 2: Define the Discriminator

def build_discriminator():
    model = tf.keras.Sequential([
        tf.keras.layers.Conv2D(64, (3, 3), strides=2, padding='same',
                               input_shape=(28, 28, 1)),
        tf.keras.layers.LeakyReLU(0.2),
        tf.keras.layers.Dropout(0.3),

        tf.keras.layers.Conv2D(128, (3, 3), strides=2, padding='same'),
        tf.keras.layers.LeakyReLU(0.2),
        tf.keras.layers.Dropout(0.3),

        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(1, activation='sigmoid')
        # Outputs probability: 1=real, 0=fake
    ])
    return model

LeakyReLU is used in the Discriminator instead of ReLU because it allows small negative values to pass through, which prevents neurons from becoming permanently inactive during adversarial training.

Step 3: Build the Combined GAN

latent_dim = 100

generator = build_generator(latent_dim)
discriminator = build_discriminator()

# Compile discriminator separately
discriminator.compile(optimizer='adam',
                       loss='binary_crossentropy',
                       metrics=['accuracy'])

# Combined GAN: noise → generator → discriminator
discriminator.trainable = False  # Freeze discriminator during GAN training
gan_input = tf.keras.Input(shape=(latent_dim,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')

Step 4: The Custom Training Loop

import numpy as np

def train_gan(gan, generator, discriminator, dataset, epochs, latent_dim):
    for epoch in range(epochs):
        for real_images in dataset:
            batch_size = real_images.shape[0]

            # ── Phase 1: Train Discriminator ──────────────────────
            noise = np.random.normal(0, 1, (batch_size, latent_dim))
            fake_images = generator.predict(noise, verbose=0)

            real_labels = np.ones((batch_size, 1))
            fake_labels = np.zeros((batch_size, 1))

            d_loss_real = discriminator.train_on_batch(real_images, real_labels)
            d_loss_fake = discriminator.train_on_batch(fake_images, fake_labels)

            # ── Phase 2: Train Generator ──────────────────────────
            noise = np.random.normal(0, 1, (batch_size, latent_dim))
            misleading_labels = np.ones((batch_size, 1))  # Pretend fakes are real
            g_loss = gan.train_on_batch(noise, misleading_labels)

        print(f"Epoch {epoch+1}: D_loss={d_loss_real[0]:.3f}, G_loss={g_loss:.3f}")

The Nash Equilibrium: When GANs Converge

GANs aim for a theoretical state called Nash Equilibrium: the Generator produces outputs so realistic that the Discriminator can do no better than guessing randomly (50% accuracy). At this point, the Generator has learned the true distribution of the training data and can generate new samples that are statistically indistinguishable from real data.

Training Progress:
─────────────────────────────────────────────────────
Epoch 1:    Generator output = noise  Discriminator = 99% accurate
Epoch 100:  Blurry shapes            Discriminator = 80% accurate
Epoch 500:  Recognizable images      Discriminator = 65% accurate
Epoch 1000: Realistic images         Discriminator ≈ 50% accurate (Nash)
─────────────────────────────────────────────────────

Common GAN Problems and Solutions

Mode Collapse

The Generator learns to produce only one or a few types of outputs, ignoring the diversity in the real data. For example, a face GAN might generate only blonde women regardless of what noise you provide.

Solution: Use Wasserstein GAN (WGAN) loss or add minibatch discrimination.

Training Instability

The Generator and Discriminator fail to improve together. One becomes too powerful and the other stops learning.

Solution: Use the same learning rate for both, train them the same number of steps, and use label smoothing (set real labels to 0.9 instead of 1.0).

Vanishing Gradients

When the Discriminator is much better than the Generator early on, it classifies all fakes with near-zero probability. The Generator receives almost no gradient signal and stops learning.

Solution: Use LeakyReLU, spectral normalization, or Wasserstein loss.

Types of GANs

  • DCGAN — Deep Convolutional GAN; the standard baseline for image generation
  • Conditional GAN (cGAN) — both networks receive a class label, so you control what type of image gets generated
  • Pix2Pix — translates one image type to another (sketch to photo, day to night)
  • CycleGAN — converts between two image domains without paired training examples (horses to zebras)
  • StyleGAN — generates high-resolution faces with controllable style attributes
  • WGAN — uses Wasserstein distance for more stable training

GANs represent one of the most active research areas in machine learning. The creative applications — from art generation to drug discovery — continue to expand. The next topic covers the Transformer model, which revolutionized natural language processing and is now reshaping computer vision as well.

Leave a Comment

Your email address will not be published. Required fields are marked *