Code Architecture — DDPM
Overview
The notebook implements a simplified DDPM from scratch in PyTorch, training on a small image dataset (FashionMNIST or MNIST for speed) with a compact U-Net denoiser. The goal is to demonstrate the full pipeline: forward noising, U-Net training, and iterative sampling.
Section-by-Section Breakdown
1. Setup & Hyperparameters
- T = 200 (reduced from paper’s 1000 for faster training)
- betas: linear schedule from 1e-4 to 0.02
- Image size: 32×32 (FashionMNIST resized) or 28×28
- Batch size: 128
- Epochs: 10-20 (reduced for Kaggle runtime budget)
- Precompute: alphas, alphas_cumprod (ᾱ_t), sqrt terms, and one-pass terms for the posterior q(x_{t-1}|x_t,x_0)
2. Forward Diffusion Process
q_sample(x_0, t, noise): Closed-form noising: x_t = √(ᾱ_t)·x_0 + √(1-ᾱ_t)·ε- Precomputes all
sqrt_alphas_cumprodandsqrt_one_minus_alphas_cumprodas buffers - Demonstrates forward noising on sample images at various timesteps
3. U-Net Denoiser
Key components:
- Sinusoidal time embedding: Projects timestep t into a continuous embedding using sine/cosine at different frequencies, then through an MLP
- Conv blocks: Two conv layers + GroupNorm + SiLU (swish) activation
- Down/Up path: 3 downsample stages (channel dims: 64→128→256) with skip connections
- Self-attention: At the mid-block (lowest resolution) for global context
- Time conditioning: Time embedding added to each residual block via a linear projection
- Input: (B, C, H, W) + timestep → Output: (B, C, H, W) predicted noise
4. Training Loop
- For each batch: sample random timesteps t ~ Uniform({1,…,T}), sample noise ε ~ N(0,I)
- Compute x_t via q_sample
- Forward pass: ε_θ = model(x_t, t)
- Loss: MSE(ε, ε_θ) — the simplified training objective from the paper
- Optimizer: Adam, lr=2e-4
- No EMA for the simplified version
5. Sampling (Reverse Process)
p_sample(model, x_t, t): Single reverse step- Compute μ_θ = (1/√(ᾱ_t)) (x_t - (β_t/√(1-ᾱ_t)) ε_θ)
- If t > 0: x_{t-1} = μ_θ + σ_t·z, where z ~ N(0,I), σ_t = √β_t
- If t == 0: x_0 = μ_θ
sample(model, shape): Start from x_T ~ N(0,I), iterate t=T→0- Generates and displays a grid of sampled images
6. Visualization
- Forward process demonstration: progressively noised images
- Training loss curve
- Final generated samples grid
Data Flow / Shapes
Input image: (B, 1, 32, 32) [FashionMNIST, single channel]
Noise ε: (B, 1, 32, 32)
x_t: (B, 1, 32, 32) [noised image at timestep t]
U-Net output:(B, 1, 32, 32) [predicted noise]
Loss: scalar MSE
Sampling:
x_T: (B, 1, 32, 32) [pure noise]
→ iteratively denoise through T steps →
x_0: (B, 1, 32, 32) [generated image]
Deliberate Simplifications vs Full Paper
| Aspect | Paper | This Notebook |
|---|---|---|
| Timesteps T | 1000 | 200 (faster sampling) |
| Dataset | CIFAR-10 / LSUN-256 | FashionMNIST 32×32 (faster training) |
| Channels | 128 base, deep U-Net | 64 base, 3 stages (compact) |
| EMA | Yes, decay 0.9999 | No (simpler) |
| Attention | At 16×16, 8×8 | At mid-block only |
| Loss weighting | Weighted by 1/σ² | Simple unweighted MSE |
| Guidance | Unconditional | Unconditional |
| Training time | Hours/days on TPU/GPU | ~10 min on Kaggle GPU |
These simplifications preserve the core algorithm (forward noising, noise prediction, iterative reverse sampling) while fitting within Kaggle’s 30-minute GPU limit.