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

> Learn to implement GANs for image generation by building a generator and discriminator network. Train them adversarially for realistic synthetic images. Get started with our guide.

- Repository: [Microsoft/AI-For-Beginners](https://github.com/microsoft/AI-For-Beginners)
- Tags: how-to-guide
- Published: 2026-08-06

---

**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:

```python
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:

```python
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:

```python
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`:

```python
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.