How to Implement GANs for Image Generation: From Vanilla GAN to DCGAN

To implement GANs for image generation, construct a generator network that transforms random latent vectors into synthetic images, pair it with a discriminator that distinguishes real from fake samples, and train both networks adversarially using binary cross-entropy loss while alternating between discriminator and generator updates.

The microsoft/AI-For-Beginners repository provides a comprehensive, beginner-friendly implementation of Generative Adversarial Networks (GANs) for synthesizing handwritten digits from the MNIST dataset. This curriculum walks through building both a fully-connected vanilla GAN and a Deep Convolutional GAN (DCGAN) using TensorFlow and PyTorch, demonstrating how to implement GANs for image generation from scratch with production-ready code patterns.

Understanding GAN Architecture Components

The Generator Network

The generator maps a random latent vector z (sampled from a normal distribution) to a synthetic image. In the vanilla implementation found in lessons/4-ComputerVision/10-GANs/GANTF.ipynb, the generator uses a stack of fully-connected layers with LeakyReLU activations and BatchNormalization to stabilize training. The final layer employs a tanh activation to constrain pixel values to the range [-1, 1], matching the preprocessed MNIST data.

The Discriminator Network

The discriminator acts as a binary classifier that outputs the probability of an image being real. The vanilla version uses dense layers ending with a sigmoid activation, while the DCGAN variant in the repository implements a convolutional architecture using Conv2D → LeakyReLU → Dropout layers to progressively downsample the input.

Implementing a Vanilla GAN in TensorFlow

The TensorFlow notebook GANTF.ipynb demonstrates the complete implementation using Keras Sequential API.

First, define the generator with dense layers and batch normalization:

import tensorflow as tf
import numpy as np

generator = tf.keras.Sequential([
    tf.keras.layers.Dense(256, input_shape=(100,)),
    tf.keras.layers.LeakyReLU(alpha=0.2),
    tf.keras.layers.BatchNormalization(momentum=0.8),
    tf.keras.layers.Dense(512),
    tf.keras.layers.LeakyReLU(alpha=0.2),
    tf.keras.layers.BatchNormalization(momentum=0.8),
    tf.keras.layers.Dense(1024),
    tf.keras.layers.LeakyReLU(alpha=0.2),
    tf.keras.layers.BatchNormalization(momentum=0.8),
    tf.keras.layers.Dense(784, activation='tanh'),
    tf.keras.layers.Reshape((28, 28))
])
optimizer = tf.keras.optimizers.Adam(learning_rate=0.0002, decay=8e-9)
generator.compile(loss='binary_crossentropy', optimizer=optimizer)

Next, implement the discriminator to classify real versus fake images:

discriminator = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(784),
    tf.keras.layers.LeakyReLU(alpha=0.2),
    tf.keras.layers.Dense(392),
    tf.keras.layers.LeakyReLU(alpha=0.2),
    tf.keras.layers.Dense(1, activation='sigmoid')
])
discriminator.compile(loss='binary_crossentropy', optimizer=optimizer)

To train the generator, assemble the adversarial model by stacking the generator and discriminator while freezing the discriminator's weights:

discriminator.trainable = False
adversarial = tf.keras.Sequential([generator, discriminator])
adversarial.compile(loss='binary_crossentropy', optimizer=optimizer)

Implementing the DCGAN Architecture

The DCGAN implementation replaces dense layers with convolutional operations for higher-quality image synthesis. According to the source code in GANTF.ipynb and GANPyTorch.ipynb, the DCGAN generator uses transposed convolutions (Conv2DTranspose in TensorFlow, ConvTranspose2d in PyTorch) to upsample low-resolution feature maps into full-size images.

Key architectural differences include:

  • Transposed convolutions for learnable upsampling rather than fixed interpolation
  • Spatial structure preservation through convolutional layers rather than flattening
  • Batch normalization after each convolutional layer to maintain stable gradients

The Adversarial Training Loop

The training process alternates between updating the discriminator and the generator. The implementation in the microsoft/AI-For-Beginners repository follows this specific workflow:

  1. Sample a batch of real images from MNIST
  2. Generate fake images from random noise vectors
  3. Train the discriminator on both real and fake batches with labels 1 (real) and 0 (fake)
  4. Train the generator via the adversarial model using labels 1 to fool the discriminator

Here is the simplified training loop from GANTF.ipynb:

batch_size = 32

for step in range(3000):
    # Train Discriminator

    noise = np.random.normal(0, 1, (batch_size // 2, 100))
    gen_imgs = generator.predict(noise)
    real_imgs = X_train[np.random.randint(0, X_train.shape[0], batch_size // 2)]
    
    x = np.concatenate([real_imgs, gen_imgs])
    y = np.concatenate([np.ones((batch_size // 2, 1)), np.zeros((batch_size // 2, 1))])
    d_loss = discriminator.train_on_batch(x, y)
    
    # Train Generator

    noise = np.random.normal(0, 1, (batch_size, 100))
    y_gen = np.ones((batch_size, 1))  # Generator tries to fool discriminator

    g_loss = adversarial.train_on_batch(noise, y_gen)
    
    if step % 500 == 0:
        print(f'Step: {step}, [D loss: {d_loss:.4f}], [G loss: {g_loss:.4f}]')

Key Stabilization Techniques

The notebooks emphasize several critical techniques for stable GAN training:

  • LeakyReLU activation with a small negative slope (alpha=0.2) prevents dead gradients when the discriminator receives negative inputs, unlike standard ReLU which can zero out gradients entirely.
  • Batch normalization maintains zero-mean, unit-variance activations throughout the network, preventing the generator from collapsing to a single mode.
  • Tanh output scaling ensures generated pixel values match the [-1, 1] range of preprocessed MNIST images, creating a consistent target distribution for the generator.

PyTorch Implementation Reference

For PyTorch users, the repository provides GANPyTorch.ipynb, which implements the same adversarial training logic using torch.nn.Sequential, torch.nn.ConvTranspose2d, and torch.optim.Adam. The architectural principles remain identical, though the API differs in explicitly defining forward passes and manual backpropagation steps.

Summary

  • Implement GANs for image generation by constructing separate generator and discriminator networks that compete in a minimax game.
  • Use LeakyReLU activations and BatchNormalization to prevent training instability and mode collapse.
  • The microsoft/AI-For-Beginners repository provides complete implementations in both TensorFlow (GANTF.ipynb) and PyTorch (GANPyTorch.ipynb).
  • Train the discriminator on real and fake batches separately, then update the generator through a frozen discriminator to maximize adversarial loss.
  • Scale output activations with tanh when training data is normalized to [-1, 1] range.

Frequently Asked Questions

What is the difference between a vanilla GAN and DCGAN?

A vanilla GAN uses fully-connected dense layers for both generator and discriminator, making it suitable for simple datasets like MNIST but limited in capturing spatial hierarchies. DCGAN replaces these with convolutional and transposed convolutional layers, enabling the generator to learn spatial features and produce higher-resolution, more coherent images through learnable upsampling.

Why use LeakyReLU instead of ReLU in GANs?

LeakyReLU allows a small gradient to flow when the unit is not active (negative input), preventing "dead neurons" that occur with standard ReLU in the discriminator. This is crucial because the discriminator often receives strong negative gradients early in training, and zero gradients would halt learning in those pathways.

How do you prevent mode collapse when implementing GANs?

Mode collapse occurs when the generator produces limited varieties of samples. The implementation in GANTF.ipynb mitigates this through BatchNormalization, which maintains diverse activations across layers, and by ensuring the discriminator sees balanced batches of real and generated samples. Additionally, using a learning rate of 0.0002 with Adam optimizer helps maintain stable gradient flows.

Can I use these implementations for datasets other than MNIST?

Yes, the architectures can be adapted for other image datasets by adjusting the input dimensions in the discriminator and the output shape in the generator. For color images (RGB), change the final layer of the generator to output 3 channels and modify the discriminator input shape accordingly. The convolutional DCGAN architecture particularly scales well to larger datasets like CIFAR-10 or CelebA with deeper networks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →