Generative Adversarial Networks GAN Architecture Adversarial Training

# Generative Adversarial Networks: GAN Architecture & Adversarial Training

## Introduction & Motivation

GANs train two neural networks—generator and discriminator—in adversarial competition. Generator creates synthetic samples; discriminator distinguishes fake from real. Reaches Nash equilibrium; generator produces high-quality, realistic data. Foundation for image synthesis, super-resolution, style transfer.

Motivation: Traditional generative models (VAE, autoregressive) have limitations. Adversarial training enables implicit density modeling without explicit likelihood. Generator learns data distribution directly.

Applications: Image generation, data augmentation, style transfer, super-resolution, deepfakes.

---

## Core Concepts & Theory

### Generator & Discriminator

Generator G: z → x (latent noise to fake samples).
Discriminator D: x → [0,1] (real/fake classification).

### Adversarial Loss

Binary cross-entropy: min_G max_D E[log D(x)] + E[log(1 - D(G(z)))].

### Nash Equilibrium

Training converges when D correctly classifies (50/50) and G replicates data distribution.

---

## Mathematical Formulation

Minimax objective:
$$\min_G \max_D \mathbb{E}_{x \sim p_{ ext{data}}}[\log D(\mathbf{x})] + \mathbb{E}_{z \sim p_z}[\log(1 - D(G(\mathbf{z})))]$$

Wasserstein GAN (improved stability):
$$\min_G \max_D \mathbb{E}_{x}[D(\mathbf{x})] - \mathbb{E}_{z}[D(G(\mathbf{z}))]$$

---

## Advanced Theory & Extensions

### Mode Collapse

Generator produces limited sample diversity; discriminator overfits to specific modes. Addressed via spectral normalization, feature matching, minibatch discrimination.

### Conditional GAN (cGAN)

Condition both G and D on class label y: G(z, y), D(x, y). Enables class-conditional generation.

---

## Computational Considerations

Generator: Deconvolution layers (transpose convolution) or upsampling + convolution.

Discriminator: Standard CNN with batch normalization, spectral normalization for stability.

Training: ~O(iterations × (G forward + D forward + backward)) per step.

---

## Practical Implementation Strategies

### Training Stability

1. Use batch normalization in generator, spectral normalization in discriminator.
2. Label smoothing (real=0.9, fake=0.1) instead of (1, 0).
3. Train discriminator k steps per generator step (k=1-5).

### Latent Space Interpolation

Sample z1, z2; interpolate z(t) = (1-t)z1 + tz2; visualize G(z(t)) smoothness.

---

## Benchmark Datasets & Evaluation

CIFAR-10: 32×32 color images; Inception Score, FID.

CelebA: Face images; visual quality assessment.

Inception Score (IS): Entropy of predicted class distribution; higher = better diversity + confidence.

Fréchet Inception Distance (FID): Wasserstein distance between real/fake feature distributions; lower = better.

---

## Key Challenges & Limitations

### Mode Collapse

Generator ignores latent diversity; produces repeated samples.

### Training Instability

Discriminator loss → 0 too quickly; generator receives vanishing gradients.

### Evaluation Difficulty

No direct likelihood; rely on proxy metrics (IS, FID, human evaluation).

---

## Hyperparameter Tuning

Learning rates: G_lr ≈ 0.0002, D_lr ≈ 0.0002 (lower than supervised).

Batch size: 64-256 (larger stabilizes).

k (D steps per G step): 1-5.

---

## Real-World Applications & Case Studies

StyleGAN: Learned hierarchical image generation; high-resolution face synthesis.

CycleGAN: Unpaired image-to-image translation (photos ↔ paintings).

Super-Resolution GAN (SRGAN): Upscale low-res → high-res images.

---

## Integration with Other Methods

GAN + Reinforcement Learning → inverse reward learning.

GAN + Anomaly Detection → learn generator on normal data; OOD samples have high reconstruction error.

---

## Summary & Key Takeaways

GANs use adversarial competition between generator and discriminator to learn implicit data distributions, enabling high-quality synthetic data generation.

Principles:
1. Generator creates samples from noise; discriminator classifies real/fake.
2. Minimax game converges to Nash equilibrium.
3. Training is unstable; use batch norm, spectral norm, label smoothing.
4. Mode collapse limits diversity; addressed via architectural/training improvements.
5. Evaluate via IS, FID; no direct likelihood available.

---

---

## Appendix: Practical Labs

### Lab 1: Basic GAN on MNIST

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np

# Generator
class Generator(nn.Module):
 def __init__(self, latent_dim=100):
 super().__init__()
 self.model = nn.Sequential(
 nn.Linear(latent_dim, 128),
 nn.ReLU(),
 nn.Linear(128, 256),
 nn.ReLU(),
 nn.Linear(256, 784),
 nn.Tanh()
 )
 def forward(self, z):
 return self.model(z)

# Discriminator
class Discriminator(nn.Module):
 def __init__(self):
 super().__init__()
 self.model = nn.Sequential(
 nn.Linear(784, 256),
 nn.ReLU(),
 nn.Linear(256, 128),
 nn.ReLU(),
 nn.Linear(128, 1),
 nn.Sigmoid()
 )
 def forward(self, x):
 return self.model(x)

# Setup
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
G = Generator().to(device)
D = Discriminator().to(device)
g_opt = optim.Adam(G.parameters(), lr=0.0002, betas=(0.5, 0.999))
d_opt = optim.Adam(D.parameters(), lr=0.0002, betas=(0.5, 0.999))
criterion = nn.BCELoss()

# Data
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
mnist = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
loader = DataLoader(mnist, batch_size=64, shuffle=True)

# Train 1 epoch
latent_dim = 100
g_loss_log, d_loss_log = [], []
for real_x, _ in loader:
 real_x = real_x.view(real_x.size(0), -1).to(device)
 batch_size = real_x.size(0)
 
 # D update
 d_opt.zero_grad()
 real_y = torch.ones(batch_size, 1).to(device)
 fake_y = torch.zeros(batch_size, 1).to(device)
 d_real = D(real_x)
 d_loss_real = criterion(d_real, real_y)
 
 z = torch.randn(batch_size, latent_dim).to(device)
 fake_x = G(z)
 d_fake = D(fake_x.detach())
 d_loss_fake = criterion(d_fake, fake_y)
 
 d_loss = d_loss_real + d_loss_fake
 d_loss.backward()
 d_opt.step()
 d_loss_log.append(d_loss.item())
 
 # G update
 g_opt.zero_grad()
 z = torch.randn(batch_size, latent_dim).to(device)
 fake_x = G(z)
 d_fake = D(fake_x)
 g_loss = criterion(d_fake, real_y)
 g_loss.backward()
 g_opt.step()
 g_loss_log.append(g_loss.item())

avg_g_loss = np.mean(g_loss_log)
avg_d_loss = np.mean(d_loss_log)
print(f"Generator loss: {avg_g_loss:.4f}, Discriminator loss: {avg_d_loss:.4f}")
assert avg_g_loss > 0 and avg_d_loss > 0, "Losses should be positive"
assert avg_g_loss < 10 and avg_d_loss < 10, "Losses should be reasonable"
print("✓ Basic GAN training working")

if __name__ == "__main__":
 print("Lab 1: Basic GAN - PASSED")

### Lab 2: Latent Space Interpolation

import torch
import torch.nn as nn
import numpy as np

# Reuse Generator from Lab 1
class Generator(nn.Module):
 def __init__(self, latent_dim=100):
 super().__init__()
 self.model = nn.Sequential(
 nn.Linear(latent_dim, 128),
 nn.ReLU(),
 nn.Linear(128, 256),
 nn.ReLU(),
 nn.Linear(256, 784),
 nn.Tanh()
 )
 def forward(self, z):
 return self.model(z)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
G = Generator().to(device)
latent_dim = 100

# Sample two latent vectors
z1 = torch.randn(1, latent_dim).to(device)
z2 = torch.randn(1, latent_dim).to(device)

# Interpolate
n_steps = 5
interpolated = []
for t in np.linspace(0, 1, n_steps):
 z_t = (1 - t) * z1 + t * z2
 x_t = G(z_t).detach().cpu().numpy()
 interpolated.append(x_t)

print(f"Interpolation steps: {len(interpolated)}")
assert len(interpolated) == n_steps, "Should have n_steps samples"
assert all(x.shape == (1, 784) for x in interpolated), "All samples should have shape (1, 784)"
print("✓ Latent space interpolation working")

if __name__ == "__main__":
 print("Lab 2: Interpolation - PASSED")

### Lab 3: Discriminator Loss Monitoring

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np

class Generator(nn.Module):
 def __init__(self, latent_dim=100):
 super().__init__()
 self.model = nn.Sequential(
 nn.Linear(latent_dim, 128),
 nn.ReLU(),
 nn.Linear(128, 256),
 nn.ReLU(),
 nn.Linear(256, 784),
 nn.Tanh()
 )
 def forward(self, z):
 return self.model(z)

class Discriminator(nn.Module):
 def __init__(self):
 super().__init__()
 self.model = nn.Sequential(
 nn.Linear(784, 256),
 nn.ReLU(),
 nn.Linear(256, 128),
 nn.ReLU(),
 nn.Linear(128, 1),
 nn.Sigmoid()
 )
 def forward(self, x):
 return self.model(x)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
G = Generator().to(device)
D = Discriminator().to(device)
d_opt = optim.Adam(D.parameters(), lr=0.0002)
criterion = nn.BCELoss()

transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
mnist = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
loader = DataLoader(mnist, batch_size=64, shuffle=True)

latent_dim = 100
d_losses = []
for real_x, _ in loader:
 real_x = real_x.view(real_x.size(0), -1).to(device)
 batch_size = real_x.size(0)
 
 d_opt.zero_grad()
 real_y = torch.ones(batch_size, 1).to(device)
 fake_y = torch.zeros(batch_size, 1).to(device)
 
 d_real = D(real_x)
 d_loss_real = criterion(d_real, real_y)
 
 z = torch.randn(batch_size, latent_dim).to(device)
 fake_x = G(z).detach()
 d_fake = D(fake_x)
 d_loss_fake = criterion(d_fake, fake_y)
 
 d_loss = d_loss_real + d_loss_fake
 d_loss.backward()
 d_opt.step()
 d_losses.append(d_loss.item())

avg_d_loss = np.mean(d_losses)
print(f"Average discriminator loss: {avg_d_loss:.4f}")
assert len(d_losses) > 0, "Should have loss values"
assert 0 < avg_d_loss < 10, "D loss should be in reasonable range"
print("✓ Discriminator loss monitoring working")

if __name__ == "__main__":
 print("Lab 3: D Loss Monitoring - PASSED")

### Lab 4: Mode Collapse Detection

import torch
import torch.nn as nn
import numpy as np
from scipy.spatial.distance import pdist

class Generator(nn.Module):
 def __init__(self, latent_dim=100):
 super().__init__()
 self.model = nn.Sequential(
 nn.Linear(latent_dim, 128),
 nn.ReLU(),
 nn.Linear(128, 256),
 nn.ReLU(),
 nn.Linear(256, 784),
 nn.Tanh()
 )
 def forward(self, z):
 return self.model(z)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
G = Generator().to(device)
latent_dim = 100

# Generate samples
n_samples = 100
z = torch.randn(n_samples, latent_dim).to(device)
fake_x = G(z).detach().cpu().numpy()

# Compute pairwise distances
distances = pdist(fake_x, metric='euclidean')
mean_distance = np.mean(distances)
std_distance = np.std(distances)
diversity_ratio = std_distance / (mean_distance + 1e-8)

print(f"Mean pairwise distance: {mean_distance:.4f}")
print(f"Diversity ratio: {diversity_ratio:.4f}")
assert mean_distance > 0, "Distances should be positive"
assert diversity_ratio > 0, "Diversity ratio should be positive"
assert len(fake_x) == n_samples, "Should have n_samples"
print("✓ Mode collapse detection working")

if __name__ == "__main__":
 print("Lab 4: Mode Collapse - PASSED")

Go deeper with CFSGPT

Get AI-powered deep-dives, save terms, and run advanced simulations — free account.

Create Free Account