xbe.at

Deep learning / CNN and computer vision

Generative Adversarial Networks in Python:

10 min read, 18 sections

Contents
  1. Introduction to Generative Adversarial Networks (GANs)
  2. The Generator Network
  3. The Discriminator Network
  4. GAN Training Process
  5. Loss Functions in GANs
  6. Challenges in Training GANs
  7. Conditional GANs
  8. Wasserstein GAN (WGAN)
  9. Progressive Growing of GANs
  10. CycleGAN for Image-to-Image Translation
  11. StyleGAN for High-Quality Image Generation
  12. Evaluation Metrics for GANs
  13. Applications of GANs
  14. Advanced GAN Architectures
  15. Response:
  16. Advanced GAN Architectures
  17. Ethical Considerations and Future Directions
  18. Additional Resources

Introduction to Generative Adversarial Networks (GANs)

Generative Adversarial Networks are a class of deep learning models consisting of two neural networks that compete against each other. The generator network creates synthetic data, while the discriminator network attempts to distinguish between real and fake data. This adversarial process leads to the generation of highly realistic synthetic data.

import tensorflow as tf
from tensorflow.keras.layers import Dense, Reshape, Conv2D, Conv2DTranspose
from tensorflow.keras.models import Sequential

# Simple GAN architecture
def build_generator():
    model = Sequential([
        Dense(7*7*256, use_bias=False, input_shape=(100,)),
        Reshape((7, 7, 256)),
        Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False),
        Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False),
        Conv2D(1, (5, 5), activation='tanh', padding='same')
    ])
    return model

def build_discriminator():
    model = Sequential([
        Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1]),
        Conv2D(128, (5, 5), strides=(2, 2), padding='same'),
        Dense(1)
    ])
    return model

The Generator Network

The generator network takes random noise as input and produces synthetic data. Its goal is to create data that is indistinguishable from real data. The generator learns to map from a latent space to the data distribution of interest.

import numpy as np
import matplotlib.pyplot as plt

def generate_random_noise(batch_size, latent_dim):
    return np.random.normal(0, 1, (batch_size, latent_dim))

generator = build_generator()
noise = generate_random_noise(1, 100)
generated_image = generator(noise, training=False)

plt.imshow(generated_image[0, :, :, 0], cmap='gray')
plt.axis('off')
plt.show()

The Discriminator Network

The discriminator network acts as a binary classifier, attempting to distinguish between real and generated data. It takes both real and fake data as input and outputs a probability indicating whether the input is real or fake.

discriminator = build_discriminator()

def discriminator_loss(real_output, fake_output):
    real_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)(tf.ones_like(real_output), real_output)
    fake_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    return total_loss

GAN Training Process

The GAN training process involves alternating between training the discriminator and the generator. The discriminator is trained to correctly classify real and fake data, while the generator is trained to produce data that fools the discriminator.

@tf.function
def train_step(images):
    noise = generate_random_noise(batch_size, latent_dim)
    
    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
        generated_images = generator(noise, training=True)
        
        real_output = discriminator(images, training=True)
        fake_output = discriminator(generated_images, training=True)
        
        gen_loss = generator_loss(fake_output)
        disc_loss = discriminator_loss(real_output, fake_output)
    
    gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
    gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
    
    generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
    discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))

Loss Functions in GANs

GANs use specialized loss functions for both the generator and discriminator. The generator aims to minimize the probability that the discriminator correctly identifies generated samples, while the discriminator tries to maximize its accuracy in distinguishing real and fake data.

def generator_loss(fake_output):
    return tf.keras.losses.BinaryCrossentropy(from_logits=True)(tf.ones_like(fake_output), fake_output)

def discriminator_loss(real_output, fake_output):
    real_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)(tf.ones_like(real_output), real_output)
    fake_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    return total_loss

Challenges in Training GANs

Training GANs can be challenging due to issues like mode collapse, where the generator produces limited varieties of outputs, and vanishing gradients. Careful tuning of hyperparameters and network architectures is often necessary to achieve stable training.

# Example of a technique to help stabilize GAN training: label smoothing
def smooth_positive_labels(y):
    return y - 0.3 + (np.random.random(y.shape) * 0.5)

def smooth_negative_labels(y):
    return y + np.random.random(y.shape) * 0.3

# Usage in discriminator training
real_labels = smooth_positive_labels(np.ones((batch_size, 1)))
fake_labels = smooth_negative_labels(np.zeros((batch_size, 1)))

Conditional GANs

Conditional GANs (cGANs) allow for the generation of data with specific attributes by providing additional information to both the generator and discriminator. This enables more controlled generation of synthetic data.

def build_conditional_generator(latent_dim, num_classes):
    noise_input = tf.keras.Input(shape=(latent_dim,))
    label_input = tf.keras.Input(shape=(1,))
    
    label_embedding = tf.keras.layers.Embedding(num_classes, 50)(label_input)
    label_embedding = tf.keras.layers.Flatten()(label_embedding)
    
    combined_input = tf.keras.layers.Concatenate()([noise_input, label_embedding])
    
    x = Dense(7*7*256, use_bias=False)(combined_input)
    x = Reshape((7, 7, 256))(x)
    x = Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False)(x)
    x = Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False)(x)
    output = Conv2D(1, (5, 5), activation='tanh', padding='same')(x)
    
    return tf.keras.Model([noise_input, label_input], output)

Wasserstein GAN (WGAN)

Wasserstein GAN is a variant that uses the Wasserstein distance as a measure of the difference between the real and generated data distributions. This approach can lead to more stable training and better quality results.

def wasserstein_loss(y_true, y_pred):
    return tf.reduce_mean(y_true * y_pred)

def build_critic():
    model = Sequential([
        Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1]),
        tf.keras.layers.LeakyReLU(),
        Conv2D(128, (5, 5), strides=(2, 2), padding='same'),
        tf.keras.layers.LeakyReLU(),
        Dense(1)
    ])
    return model

critic = build_critic()
critic_optimizer = tf.keras.optimizers.RMSprop(lr=0.00005)

Progressive Growing of GANs

Progressive Growing of GANs is a technique where the generator and discriminator start with low-resolution images and gradually increase the resolution during training. This approach can lead to more stable training and higher quality results, especially for high-resolution image generation.

def build_progressive_generator(latent_dim, target_resolution):
    model = Sequential()
    model.add(Dense(4*4*512, use_bias=False, input_shape=(latent_dim,)))
    model.add(Reshape((4, 4, 512)))
    
    current_resolution = 4
    while current_resolution < target_resolution:
        model.add(Conv2DTranspose(512//2, (3, 3), strides=(2, 2), padding='same', use_bias=False))
        model.add(tf.keras.layers.LeakyReLU())
        current_resolution *= 2
    
    model.add(Conv2D(3, (3, 3), activation='tanh', padding='same'))
    return model

progressive_generator = build_progressive_generator(100, 128)

CycleGAN for Image-to-Image Translation

CycleGAN is a type of GAN used for unpaired image-to-image translation. It learns to translate an image from one domain to another without paired examples, using cycle consistency loss.

def build_generator_resnet():
    input_layer = tf.keras.layers.Input(shape=(256, 256, 3))
    x = tf.keras.layers.Conv2D(64, 7, strides=1, padding='same')(input_layer)
    x = tf.keras.layers.BatchNormalization()(x)
    x = tf.keras.layers.ReLU()(x)
    
    # Downsampling
    x = tf.keras.layers.Conv2D(128, 3, strides=2, padding='same')(x)
    x = tf.keras.layers.BatchNormalization()(x)
    x = tf.keras.layers.ReLU()(x)
    
    # Residual blocks
    for _ in range(9):
        residual = x
        x = tf.keras.layers.Conv2D(128, 3, strides=1, padding='same')(x)
        x = tf.keras.layers.BatchNormalization()(x)
        x = tf.keras.layers.ReLU()(x)
        x = tf.keras.layers.Conv2D(128, 3, strides=1, padding='same')(x)
        x = tf.keras.layers.BatchNormalization()(x)
        x = tf.keras.layers.Add()([x, residual])
    
    # Upsampling
    x = tf.keras.layers.Conv2DTranspose(64, 3, strides=2, padding='same')(x)
    x = tf.keras.layers.BatchNormalization()(x)
    x = tf.keras.layers.ReLU()(x)
    
    x = tf.keras.layers.Conv2D(3, 7, strides=1, padding='same', activation='tanh')(x)
    
    return tf.keras.Model(inputs=input_layer, outputs=x)

generator_G = build_generator_resnet()
generator_F = build_generator_resnet()

StyleGAN for High-Quality Image Generation

StyleGAN is an advanced GAN architecture that introduces a style-based generator, allowing for fine-grained control over the generated images and producing state-of-the-art results in terms of image quality and diversity.

def build_mapping_network(latent_dim, dlatent_dim, mapping_layers=8):
    inputs = tf.keras.Input(shape=(latent_dim,))
    x = inputs
    for _ in range(mapping_layers):
        x = Dense(dlatent_dim, activation='leaky_relu')(x)
    return tf.keras.Model(inputs, x)

def build_synthesis_network(dlatent_dim, resolution=1024):
    dlatents_in = tf.keras.Input(shape=[dlatent_dim])
    x = Dense(512 * 4 * 4, activation='leaky_relu')(dlatents_in)
    x = Reshape((4, 4, 512))(x)
    
    resolution_log2 = int(np.log2(resolution))
    for i in range(2, resolution_log2 + 1):
        x = Conv2DTranspose(512 // 2**(i-1), 3, strides=2, padding='same')(x)
        x = tf.keras.layers.LeakyReLU()(x)
    
    x = Conv2D(3, 1, activation='tanh')(x)
    return tf.keras.Model(dlatents_in, x)

latent_dim = 512
dlatent_dim = 512
mapping_network = build_mapping_network(latent_dim, dlatent_dim)
synthesis_network = build_synthesis_network(dlatent_dim)

Evaluation Metrics for GANs

Evaluating GANs can be challenging due to the lack of a single objective function. Common metrics include Inception Score (IS), Fréchet Inception Distance (FID), and human evaluation. These metrics help assess the quality and diversity of generated samples.

from tensorflow.keras.applications.inception_v3 import InceptionV3
import numpy as np

def calculate_fid(real_images, generated_images):
    inception_model = InceptionV3(include_top=False, pooling='avg', input_shape=(299, 299, 3))
    
    def preprocess_images(images):
        images = tf.image.resize(images, (299, 299))
        return tf.keras.applications.inception_v3.preprocess_input(images)
    
    real_features = inception_model.predict(preprocess_images(real_images))
    generated_features = inception_model.predict(preprocess_images(generated_images))
    
    mu1, sigma1 = real_features.mean(axis=0), np.cov(real_features, rowvar=False)
    mu2, sigma2 = generated_features.mean(axis=0), np.cov(generated_features, rowvar=False)
    
    diff = mu1 - mu2
    covmean = np.sqrt(sigma1.dot(sigma2))
    
    fid = np.sum(diff**2) + np.trace(sigma1 + sigma2 - 2*covmean)
    return fid

Applications of GANs

GANs have found numerous applications across various domains, including image generation, style transfer, text-to-image synthesis, and even in scientific fields like drug discovery. Their ability to generate realistic synthetic data makes them valuable in scenarios where data is scarce or privacy is a concern.

# Example: Using a pre-trained StyleGAN2 model for face generation
import dnnlib
import dnnlib.tflib as tflib
import pickle

def load_stylegan2(model_path):
    with dnnlib.util.open_url(model_path) as f:
        _G, _D, Gs = pickle.load(f)
    return Gs

def generate_faces(Gs, num_samples=1):
    latents = np.random.randn(num_samples, Gs.input_shape[1])
    images = Gs.run(latents, None, truncation_psi=0.7, randomize_noise=True, output_transform=dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True))
    return images

# Usage:
# Gs = load_stylegan2('path/to/stylegan2-ffhq-config-f.pkl')
# generated_faces = generate_faces(Gs, num_samples=4)

Advanced GAN Architectures

Recent advancements in GAN architectures have led to improved performance and capabilities. Some notable examples include BigGAN for high

Response:

Advanced GAN Architectures

Recent advancements in GAN architectures have led to improved performance and capabilities. Some notable examples include BigGAN for high-fidelity image generation, Self-Attention GAN (SAGAN) for long-range dependency modeling, and StyleGAN3 for alias-free image synthesis. These architectures incorporate novel techniques to address challenges in training stability, output quality, and control over generated content.

# Example: Self-Attention module for GANs
class SelfAttention(tf.keras.layers.Layer):
    def __init__(self, channels):
        super(SelfAttention, self).__init__()
        self.channels = channels
        self.query_conv = tf.keras.layers.Conv2D(channels // 8, 1)
        self.key_conv = tf.keras.layers.Conv2D(channels // 8, 1)
        self.value_conv = tf.keras.layers.Conv2D(channels, 1)
        self.gamma = self.add_weight(name='gamma', shape=[1], initializer='zeros', trainable=True)

    def call(self, x):
        batch_size, height, width, channels = x.shape
        
        query = self.query_conv(x)
        key = self.key_conv(x)
        value = self.value_conv(x)
        
        query = tf.reshape(query, [-1, height * width, self.channels // 8])
        key = tf.reshape(key, [-1, height * width, self.channels // 8])
        value = tf.reshape(value, [-1, height * width, self.channels])
        
        attention = tf.matmul(query, key, transpose_b=True)
        attention = tf.nn.softmax(attention)
        
        out = tf.matmul(attention, value)
        out = tf.reshape(out, [-1, height, width, self.channels])
        
        return self.gamma * out + x

# Usage in a generator or discriminator
x = Conv2D(64, 3, padding='same')(input_layer)
x = SelfAttention(64)(x)

Ethical Considerations and Future Directions

As GANs become more powerful, ethical considerations surrounding their use become increasingly important. Issues such as deepfakes, potential misuse for misinformation, and privacy concerns need to be addressed. Future research directions include improving GAN stability, enhancing interpretability, and developing robust defense mechanisms against malicious use of GANs.

# Example: Simple watermarking technique for generated images
def add_watermark(image, watermark_text):
    from PIL import Image, ImageDraw, ImageFont
    
    pil_image = Image.fromarray((image * 255).astype(np.uint8))
    draw = ImageDraw.Draw(pil_image)
    font = ImageFont.truetype("arial.ttf", 20)
    
    text_width, text_height = draw.textsize(watermark_text, font)
    x = pil_image.width - text_width - 10
    y = pil_image.height - text_height - 10
    
    draw.text((x, y), watermark_text, font=font, fill=(255, 255, 255, 128))
    
    return np.array(pil_image) / 255.0

# Usage
generated_image = generator(noise, training=False)
watermarked_image = add_watermark(generated_image[0], "AI Generated")

Additional Resources

For those interested in diving deeper into GANs, here are some valuable resources:

  1. "Generative Adversarial Networks" by Ian Goodfellow et al. (2014) ArXiv: https://arxiv.org/abs/1406.2661
  2. "Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks" by Radford et al. (2015) ArXiv: https://arxiv.org/abs/1511.06434
  3. "Improved Techniques for Training GANs" by Salimans et al. (2016) ArXiv: https://arxiv.org/abs/1606.03498
  4. "Progressive Growing of GANs for Improved Quality, Stability, and Variation" by Karras et al. (2017) ArXiv: https://arxiv.org/abs/1710.10196
  5. "A Style-Based Generator Architecture for Generative Adversarial Networks" by Karras et al. (2018) ArXiv: https://arxiv.org/abs/1812.04948

These papers provide a solid foundation for understanding GANs and their evolution over time.

More in CNN and computer vision

  1. Identity Mappings in Deep Residual Networks with Python10 min

    Identity mappings are a key component in deep residual networks, enhancing gradient flow and easing optimization. They allow for the creation of very deep...

  2. Fractal Patterns in Neural Network Hyperparameter Grids12 min

    Neural networks are complex systems with numerous hyperparameters. When performing a grid search over these parameters, unexpected patterns can emerge. This...

  3. Introduction to Diffusion Models in Python9 min

    Diffusion models are a class of generative models that learn to gradually denoise data. They start with pure noise and iteratively refine it into...

  4. Exploring 1x1 Convolutions in Deep Learning9 min

    1x1 convolutions, also known as pointwise convolutions, are a special type of convolutional layer in neural networks. They operate on a single pixel across...

  5. Introduction to Padding, Strides, and Pooling in Deep Learning with Python9 min

    Padding, strides, and pooling are fundamental concepts in deep learning, particularly in convolutional neural networks (CNNs). These techniques help manage...

  6. Diffusion Models for Imaging and Vision in Python10 min

    Diffusion models are a class of generative models that have gained significant attention in the field of computer vision and image processing. These models...