Code Architecture — StyleGAN Notebook

Overview

This notebook implements a miniature StyleGAN that captures the paper’s core architectural innovations: a mapping network, AdaIN-based style injection, learned constant input, stochastic noise injection, and style mixing. The implementation is deliberately scaled down to run within Kaggle’s 30-minute GPU limit on a toy dataset (MNIST or small synthetic face-like data).

Section-by-Section Breakdown

1. Imports and Configuration

  • PyTorch, torchvision, matplotlib, numpy
  • Hyperparameters: latent_dim=256, w_dim=256, image_size=32, channels=16, lr=1e-3, epochs=150
  • Device selection (CUDA if available)

2. Toy Dataset

  • Uses MNIST resized to 32×32 (or a small synthetic colored shape dataset)
  • Simple, fast to train, available without download on Kaggle
  • DataLoader with batch_size=64

3. Mapping Network (MappingNetwork)

Input: z (B, 256) ~ N(0, I)
Architecture: 8 × [Linear(256, 256) → LeakyReLU(0.2)]
Output: w (B, 256)
  • 8-layer MLP as in the paper
  • LeakyReLU with α=0.2
  • Deliberate simplification: 256-dim instead of 512-dim to reduce parameters

4. AdaIN Module (AdaIN)

Input: x (B, C, H, W), y_style (B, 2*C)
  y_s = y_style[:, :C]  -- scale
  y_b = y_style[:, C:]  -- bias
Process:
  x_norm = (x - mean(x, dim=[2,3])) / std(x, dim=[2,3])   # per-channel instance norm
  output = y_s[:, :, None, None] * x_norm + y_b[:, :, None, None]
  • Implements Eq. 1 from the paper exactly
  • Per-channel normalization then affine transform with style

5. Styled Conv Block (StyledConvBlock)

Per block:
  1. Conv2d(in_ch, out_ch, 3, padding=1) with equalized LR
  2. Add noise: x + noise_weight * randn(B, 1, H, W)
  3. LeakyReLU(0.2)
  4. AdaIN(style from w)
  5. Conv2d(out_ch, out_ch, 3, padding=1)
  6. Add noise
  7. LeakyReLU(0.2)
  8. AdaIN(style from w)
  • Two convolutions per resolution (matching paper’s 2 layers per resolution)
  • Noise is single-channel, broadcast via learned per-channel scaling
  • Style affine transform: A(w) = Linear(w_dim, 2*channels) — learned affine

6. Synthesis Network (SynthesisNetwork)

Start: learned constant (1, channels, 4, 4) — nn.Parameter
Layers:
  4×4:   StyledConvBlock(ch, ch) → upsample
  8×8:   StyledConvBlock(ch, ch) → upsample
  16×16: StyledConvBlock(ch, ch) → upsample
  32×32: StyledConvBlock(ch, ch) → toRGB
toRGB: Conv2d(ch, 3, 1) -- 1×1 conv to 3-channel RGB
  • Starts from learned constant (not random input)
  • Progressive upsampling via bilinear interpolation
  • 1×1 conv to RGB at the end
  • Deliberate simplification: 4 resolutions (4²→32²) instead of 9 (4²→1024²)

7. Generator (StyleGenerator)

Forward(z):
  w = mapping_network(z)         # z → w
  img = synthesis_network(w)     # w → image
  return img
  • Combines mapping + synthesis
  • No progressive growing fade-in (simplified)

8. Discriminator (Discriminator)

Input: image (B, 3, 32, 32)
Architecture: 
  Conv2d(3, ch, 3, 2, 1) → LeakyReLU
  Conv2d(ch, ch*2, 3, 2, 1) → LeakyReLU
  Conv2d(ch*2, ch*4, 3, 2, 1) → LeakyReLU
  Conv2d(ch*4, 1, 4) → squeeze
Output: logits (B, 1)
  • Standard DCGAN-style discriminator
  • Not modified from baseline (paper doesn’t change discriminator)

9. Equalized Learning Rate

  • Weights initialized from N(0, 1), scaled by constant at runtime
  • weight = weight * (fan_in ** -0.5) — equalized LR trick from Progressive GAN

10. Training Loop

  • Loss: Non-saturating GAN loss (hinge loss variant)
  • R1 regularization on discriminator (γ=10)
  • Separate learning rates: mapping network gets 0.01× base LR
  • Adam optimizer (β1=0, β2=0.99)
  • Mixing regularization: 50% of batches use two latent codes with random crossover
  • Exponential moving average of generator weights

11. Style Mixing Visualization

  • Generate two images from z1, z2
  • Mix styles: use w1 for coarse layers, w2 for fine layers
  • Display grid showing: source A, source B, and mixed results at different crossover points
  • Demonstrates the scale-specific control of styles

12. Stochastic Variation Visualization

  • Fix w, vary noise inputs
  • Generate multiple images with same style but different noise
  • Show that high-level structure is preserved while details (texture, edges) vary

Key Data Flow / Shapes

z: (B, 256) 
  → MappingNetwork → w: (B, 256)
  → SynthesisNetwork:
      const: (B, 16, 4, 4) 
      → StyledConv(4²) → upsample → (B, 16, 8, 8)
      → StyledConv(8²) → upsample → (B, 16, 16, 16)
      → StyledConv(16²) → upsample → (B, 16, 32, 32)
      → StyledConv(32²) → toRGB → (B, 3, 32, 32)
  → output: (B, 3, 32, 32)

Deliberate Simplifications vs Full Paper

Aspect Paper This Notebook
Image resolution 1024×1024 32×32
Latent dim 512 256
Feature maps 512→16 (progressive) 16 (fixed)
Mapping network depth 8 layers 8 layers (same)
Synthesis layers 18 (2 per resolution, 9 resolutions) 8 (2 per resolution, 4 resolutions)
Dataset FFHQ (70K faces) MNIST (or synthetic shapes)
Training time ~1 week on 8×V100 ~5-10 min on 1×T4
Progressive growing Yes (with fade-in) No (fixed resolution)
Truncation trick In 𝒲 space Not implemented (visualization only)
Perceptual path length metric Yes Not computed
Linear separability metric Yes Not computed

These simplifications are necessary to fit within Kaggle’s 30-minute GPU limit while still demonstrating all core architectural components: mapping network, AdaIN style injection, learned constant, noise injection, and style mixing.