Generative Adversarial Networks Generator vs Discriminator Competition
# Generative Adversarial Networks: Generator vs Discriminator Competition
## Introduction & Motivation
GANs: generator and discriminator compete. Generator: learn to fool discriminator; generate realistic samples. Discriminator: distinguish real from fake. Adversarial loss: minimax game. Applications: image synthesis, style transfer, image-to-image translation, data augmentation.
Motivation: Supervised: need paired data. GANs: unsupervised; learn from real data distribution.
Applications: Image generation, style transfer, data augmentation.
---
## Core Concepts & Theory
### Generator
Transforms noise → samples; learns data distribution.
### Discriminator
Classifies real vs fake; provides learning signal.
### Adversarial Game
Minimax: generator minimizes discriminator advantage.
---
## Mathematical Formulation
GAN objective:
$$\min_G \max_D \mathbb{E}_x[\log D(x)] + \mathbb{E}_z[\log(1 - D(G(z)))]$$
Generator loss (practical):
$$L_G = -\mathbb{E}_z[\log D(G(z))]$$
Discriminator loss:
$$L_D = -\mathbb{E}_x[\log D(x)] - \mathbb{E}_z[\log(1 - D(G(z)))]$$
---
## Advanced Theory & Extensions
### Wasserstein GAN (WGAN)
Wasserstein distance; more stable training; gradient penalty.
### Conditional GAN
Class-conditional generation; control output.
### StyleGAN
Learned style mixing; progressive growing.
---
## Computational Considerations
Generator: O(forward + backward) per batch.
Discriminator: O(2·forward + backward) real + fake.
Training: Alternating updates; typically 1 D : 1 G step.
---
## Practical Implementation Strategies
### Training Stability
Balance G/D strength; careful learning rates.
### Spectral Normalization
Stabilize discriminator; prevent vanishing gradients.
### Progressive Growing
Start small, gradually increase resolution.
---
## Benchmark Datasets & Evaluation
CIFAR-10: Standard benchmark; Inception Score ~8.0.
CelebA: Faces; FID ~5-10 for good models.
ImageNet: Large-scale; resolution 256×256+.
---
## Key Challenges & Limitations
### Mode Collapse
Generator ignores modes; produces limited diversity.
### Training Instability
Careful balancing required; hyperparameter sensitive.
### Evaluation Difficulty
No single metric; Inception Score, FID, human evaluation.
---
## Hyperparameter Tuning
Learning rate: 2e-4 typical; G:D 1:1 to 1:5 ratio.
Batch size: 32-128; larger helps stability.
Architecture: Deep discriminator; progressive generator.
---
## Real-World Applications & Case Studies
Image Generation: DCGAN, StyleGAN; photorealistic synthesis.
Conditional: CycleGAN; unpaired image translation.
Text-to-Image: CLIP guidance + diffusion.
---
## Integration with Other Methods
GAN + VAE → adversarial VAE.
GAN + Diffusion → hybrid models.
---
## Summary & Key Takeaways
Generative adversarial networks via competing generator-discriminator pair enable unsupervised learning of data distributions, producing high-quality samples through adversarial training.
Principles:
1. Minimax game: generator vs discriminator.
2. Mode collapse: avoid via balanced loss.
3. Spectral normalization: stabilize training.
4. Progressive growing: increase resolution gradually.
5. Evaluation: multi-metric assessment essential.
---
---
## Appendix: Practical Labs
### Lab 1: GAN Components
import torch
import torch.nn as nn
import numpy as np
class Generator(nn.Module):
def __init__(self, latent_dim=100, output_dim=784):
super().__init__()
self.net = nn.Sequential(
nn.Linear(latent_dim, 256),
nn.ReLU(),
nn.Linear(256, 512),
nn.ReLU(),
nn.Linear(512, output_dim),
nn.Sigmoid()
)
def forward(self, z):
return self.net(z)
class Discriminator(nn.Module):
def __init__(self, input_dim=784):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, 512),
nn.ReLU(),
nn.Linear(512, 256),
nn.ReLU(),
nn.Linear(256, 1),
nn.Sigmoid()
)
def forward(self, x):
return self.net(x)
# Test
np.random.seed(42)
G = Generator(latent_dim=100)
D = Discriminator(input_dim=784)
z = torch.randn(32, 100)
fake_samples = G(z)
score_fake = D(fake_samples)
assert fake_samples.shape == (32, 784), "Generator output shape"
assert score_fake.shape == (32, 1), "Discriminator output shape"
print("✓ GAN components working")
if __name__ == "__main__":
print("Lab 1: Components - PASSED")### Lab 2: Adversarial Loss
import torch
import torch.nn.functional as F
import numpy as np
def generator_loss(D, fake_samples):
"""Generator loss: fool discriminator"""
fake_scores = D(fake_samples)
loss = -F.logsigmoid(fake_scores).mean()
return loss
def discriminator_loss(D, real_samples, fake_samples):
"""Discriminator loss: distinguish real vs fake"""
real_scores = D(real_samples)
fake_scores = D(fake_samples)
loss_real = -F.logsigmoid(real_scores).mean()
loss_fake = -F.logsigmoid(1 - fake_scores).mean()
return loss_real + loss_fake
# Test
np.random.seed(42)
class DummyD(nn.Module):
def forward(self, x):
return torch.rand(len(x), 1)
D = DummyD()
real = torch.randn(32, 784)
fake = torch.randn(32, 784)
g_loss = generator_loss(D, fake)
d_loss = discriminator_loss(D, real, fake)
assert torch.isfinite(g_loss), "G loss finite"
assert torch.isfinite(d_loss), "D loss finite"
print("✓ Adversarial loss working")
if __name__ == "__main__":
print("Lab 2: Loss - PASSED")### Lab 3: Training Step
import torch
import torch.nn as nn
import numpy as np
def train_step(G, D, real_batch, optimizer_G, optimizer_D, latent_dim=100):
"""Single GAN training step"""
batch_size = len(real_batch)
# Discriminator step
optimizer_D.zero_grad()
real_score = D(real_batch)
z = torch.randn(batch_size, latent_dim)
fake_batch = G(z)
fake_score = D(fake_batch.detach())
d_loss = -torch.log(real_score).mean() - torch.log(1 - fake_score).mean()
d_loss.backward()
optimizer_D.step()
# Generator step
optimizer_G.zero_grad()
z = torch.randn(batch_size, latent_dim)
fake_batch = G(z)
fake_score = D(fake_batch)
g_loss = -torch.log(fake_score).mean()
g_loss.backward()
optimizer_G.step()
return g_loss.item(), d_loss.item()
# Test
np.random.seed(42)
G = nn.Linear(100, 784)
D = nn.Linear(784, 1)
opt_G = torch.optim.Adam(G.parameters())
opt_D = torch.optim.Adam(D.parameters())
real = torch.randn(32, 784)
g_loss, d_loss = train_step(G, D, real, opt_G, opt_D)
assert np.isfinite(g_loss), "G loss finite"
assert np.isfinite(d_loss), "D loss finite"
print("✓ Training step working")
if __name__ == "__main__":
print("Lab 3: Training - PASSED")### Lab 4: Mode Coverage Assessment
import numpy as np
def mode_coverage(generated_samples, num_modes=10):
"""Assess generator mode coverage"""
# Cluster generated samples
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=num_modes, random_state=42)
labels = kmeans.fit_predict(generated_samples)
# Count samples per mode
unique_modes = len(np.unique(labels))
coverage = unique_modes / num_modes
return coverage
# Test
np.random.seed(42)
generated = np.random.randn(1000, 64)
coverage = mode_coverage(generated, num_modes=10)
assert 0 <= coverage <= 1, "Coverage in [0,1]"
print("✓ Mode coverage working")
if __name__ == "__main__":
print("Lab 4: Coverage - PASSED")