Code Architecture — Latent Diffusion Models (LDM)
Overview
This notebook implements a miniature Latent Diffusion Model and compares it against a pixel-space DDPM on the same toy dataset. The pipeline has two stages: (1) train a small VAE to compress 32×32 images into an 8×8 latent space (f=4), then (2) train a U-Net denoiser in that latent space. We also train a pixel-space DDPM baseline for direct comparison of training/sampling speed and sample quality.
Section-by-Section Breakdown
1. Setup and Imports
- PyTorch, torchvision (FashionMNIST/CIFAR-10 subset), matplotlib, numpy
- Device detection (cuda for Kaggle GPU)
- Utility:
show_images()for grid visualization
2. Hyperparameters
IMG_SIZE=32,CHANNELS=1(grayscale),LATENT_CH=4,DOWNSAMPLE=4(f=4)- Latent spatial size: 32/4 = 8×8
T=200diffusion timestepsEPOCHS_VAE=5,EPOCHS_DIFFUSION=5,BATCH_SIZE=128,SUBSET_SIZE=8000BASE_CH=32(compact U-Net)
3. Toy Dataset
- FashionMNIST subset (8000 images), 32×32 grayscale, normalized to [-1, 1]
- Single DataLoader shared by both VAE and diffusion training
4. Stage 1 — Autoencoder (Perceptual Compression)
VAE class:
- Encoder: 3 conv blocks (stride-2 downsampling) → 32→64→128 channels → 32×32 → 8×8 spatial
- Conv to
LATENT_CH=4channels → latent shape (B, 4, 8, 8) - Decoder: conv from latent → 3 upsample blocks (transposed conv) → 8→16→32 spatial → output (B, 1, 32, 32)
- Loss: reconstruction MSE + KL divergence (β=0.001 for mild regularization)
- Training: Adam, 5 epochs
- After training: freeze the VAE encoder/decoder
5. Forward Diffusion Process (in latent space)
- Linear beta schedule: β_t from 1e-4 to 0.02
- Precompute: alphas, alphas_cumprod (ᾱ_t), sqrt quantities
q_sample(z_0, t, noise): z_t = √ᾱ_t · z_0 + √(1-ᾱ_t) · ε- Key difference from pixel DDPM: noise is added to latent codes (4×8×8) not pixels (1×32×32)
6. U-Net Denoiser (operates on latent space)
- SinusoidalPositionEmbeddings: timestep → sinusoidal vector
- ResBlock: Conv → GroupNorm → SiLU → Conv + time embedding injection + skip
- Self-Attention: at 4×4 latent resolution
- U-Net: down path (32→64→128) → bottleneck → up path with skip connections
- Input: (B, 4, 8, 8) + timestep → Output: (B, 4, 8, 8) predicted noise
- Architecture is identical to pixel DDPM U-Net but input/output channels = 4 (latent) instead of 1 (pixel)
7. Latent Diffusion Training
- Encode batch through frozen VAE encoder → z_0 (B, 4, 8, 8)
- Random timestep, forward diffusion on z_0, U-Net predicts noise, MSE loss
- Adam optimizer, 5 epochs
- Track training time for speed comparison
8. Pixel-Space DDPM Baseline
- Same U-Net architecture but input/output channels = 1 (pixel space)
- Same T=200, same dataset, same epochs
- Trained from scratch on 32×32 pixels for fair comparison
- Track training time
9. Sampling and Comparison
ddpm_sample(model, shape, T, ...) — standard DDPM reverse process:
- Start from pure noise → iteratively denoise through all T steps
- For LDM: sample in latent space → decode through frozen VAE decoder → image
- For pixel DDPM: sample directly in pixel space → image
Speed comparison metrics:
- Training time per epoch (LDM vs pixel DDPM)
- Sampling time for 16 images (LDM vs pixel DDPM)
- Latent diffusion should be ~f² = 16× faster per forward pass
Quality comparison:
- Visualize samples from both models side-by-side
- Report FID (if feasible) or visual quality assessment
10. Latent Space Analysis
- Visualize VAE reconstructions (encode → decode) to verify compression quality
- Show the latent representation’s structure via interpolation
- Demonstrate that the autoencoder preserves perceptual quality despite 16× compression
Data Flow / Shapes
Image: (B, 1, 32, 32)
→ VAE Encoder → Latent z: (B, 4, 8, 8) [16× compression]
→ Diffusion on z: (B, 4, 8, 8) ↔ noise (B, 4, 8, 8)
→ U-Net operates on (B, 4, 8, 8)
→ Sampled z: (B, 4, 8, 8)
→ VAE Decoder → Generated Image: (B, 1, 32, 32)
Pixel DDPM baseline:
Image: (B, 1, 32, 32) ↔ noise (B, 1, 32, 32)
→ U-Net operates on (B, 1, 32, 32)
→ Sampled image: (B, 1, 32, 32)
Deliberate Simplifications vs Full Paper
-
No cross-attention conditioning: The paper’s main contribution for text-to-image is cross-attention layers in the U-Net. This notebook implements unconditional LDM for clarity. Cross-attention would require a text encoder and conditioning pipeline, adding complexity without illustrating the core latent-space insight.
-
Small-scale VAE instead of VQGAN: The paper uses a large VQGAN autoencoder trained on ImageNet. We train a tiny VAE on the same toy dataset. The principle (compress → diffuse → decompress) is identical.
-
Grayscale 32×32 instead of RGB 256×256: For Kaggle GPU time constraints. The paper works at 256²–1024² in RGB. The latent-space speedup scales with resolution, so the advantage is even more dramatic at higher resolutions.
-
Linear noise schedule: The paper discusses choosing the noise schedule based on the latent SNR. We use a simple linear schedule adapted to the latent’s scale by normalizing the latent codes.
-
No classifier-free guidance: The paper uses guidance for conditional generation. Since we do unconditional generation, guidance is not applicable.
-
Fewer timesteps (T=200 vs 1000): For faster training/sampling within Kaggle’s 30-minute limit. The principle holds at any T.