Diffusion and Generative Models
Train and sample a VAE, a GAN, DDPM, latent diffusion and flow matching, then measure them. Toy Python from the lessons.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Defaults and numbers
- Troubleshooting
- Questions and tips
Latent diffusion with CFG#
From Lesson 08.07, Lesson 08.02
DDPM train and sample#
Key formulas#
- Forward
- q(x_t \mid x_0) = \mathcal{N}\left( \sqrt{\bar\alpha_t} \, x_0, (1 - \bar\alpha_t) I \right)
- Alpha bar
- \bar\alpha_t = \prod_{s=1}^{t} (1 - \beta_s)
- Simple loss
- L_{\text{simple}} = \lVert \epsilon - \epsilon_\theta(x_t, t) \rVert^2
- Reverse step
- x_{t-1} = \frac{1}{\sqrt{\alpha_t}} \left( x_t - \frac{\beta_t}{\sqrt{1 - \bar\alpha_t}} \epsilon_\theta(x_t, t) \right) + \sigma_t z
- VAE loss
- \lVert x - \hat{x} \rVert^2 + \beta \sum_i \frac{\sigma_i^2 + \mu_i^2 - \log \sigma_i^2 - 1}{2}
- GAN game
- \min_G \max_D \, \mathbb{E}[\log D(x)] + \mathbb{E}[\log(1 - D(G(z)))]
- CFG
- \epsilon_{\text{cfg}} = (1 + w) \epsilon_{\text{cond}} - w \epsilon_{\text{uncond}}
- Flow matching
- x_t = t x_1 + (1 - t) x_0, L = \lVert v_\theta(x_t, t) - (x_1 - x_0) \rVert^2
From Lesson 08.06, Lesson 08.02, Lesson 08.03, Lesson 08.07, Lesson 08.13
Tasks in code#
| Task | Code |
|---|---|
| Noise x_0 to step t | x_t = |
| VAE reparameterize | [m + s * e for m, |
| Non-saturating G loss | -sum(math.log(max(p, |
| Drop the label for CFG | use_c = |
| Mix with CFG | eps_cfg = |
| Flow matching target | target = |
| Euler step, flow | x -= |
| Tile the VAE decode | pipe.vae.enable_tiling() |
From 08.06 code/main.py, Lesson 08.02, Lesson 08.03, Lesson 08.07, 08.07 code/main.py, Lesson 08.13
DDPM schedule, noise and reverse loop#
betas = [1e-4 + (0.02 - 1e-4) * t / (T - 1) for t in range(T)]linearalphas = [1 - b for b in betas]alpha_bars, cum = [], 1.0for a in alphas: cum *= a alpha_bars.append(cum)cumulative producteps = rng.gauss(0, 1)training noisex_t = math.sqrt(a_bar) * x0 + math.sqrt(1 - a_bar) * epsone shotx = rng.gauss(0, 1)sampling starts at noisefor t in range(T - 1, -1, -1):T-1 down to 0 eps_hat, _ = forward([x], t_emb, net)predicted noise mean = (x - beta_t / math.sqrt(1 - alpha_bars[t]) * eps_hat[0]) / math.sqrt(alphas[t]) x = mean + math.sqrt(beta_t) * rng.gauss(0, 1)only when t > 0From 08.06 code/main.py
Samplers and steps#
| Sampler | Steps |
|---|---|
| DDPM | 1000 reverse steps in the paper |
| DDIM | 20 to 50, same training objective |
| DPM-Solver++ | 10 to 20 |
| UniPC | 8 to 16 |
| Flow matching, Euler | 4 to 8 on straight paths |
| Rectified flow, 2 reflows | 2 to 4 |
| Distillation, consistency | 1 to 4, needs retraining |
| VAE or GAN | one forward pass |
Defaults and numbers#
| Setting | Default | Note |
|---|---|---|
| \beta start | 1e-4 | linear schedule |
| \beta end | 0.02 | x_T is close to \mathcal{N}(0, I) |
| Steps T | 1000 | the toy code uses 40 |
| CFG scale w | 3 | 3 to 7 is the sweet spot |
| CFG label dropout | 10% | null token in training |
| SD 1.x VAE scaling factor | 0.18215 | applied after encoding |
| LoRA rank r | 4-16 | attention layers, 64 to 128 for heavy fine tunes |
| FID sample count | 10k | low N gives a falsely low FID |
From Lesson 08.06, 08.06 code/main.py, Lesson 08.07, Lesson 08.08, Lesson 08.14
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
| z carries no information about x | posterior collapse, KL wins | anneal \beta from 0 to 1, or free bits |
| GAN makes one mode only | mode collapse | noise on D inputs, minibatch discrimination or WGAN-GP |
| G stops improving, D over 95% accuracy | D is too strong | cut the D learning rate by 2-5x |
| Saturated, oily images | CFG too high | keep w at 3 to 7 |
| Denoiser trains on wrong latent variance | VAE scaling factor missing | apply the checkpoint scaling factor |
| NaNs from the SD 1.x VAE at 1024² | fp16 cast | use the fp16-fix VAE or bf16 |
| LoRA has no effect | trained on another base | use the matching base model |
| FID looks too good | N under 10k | use 10k samples or more and 3 seeds |
From Lesson 08.02, Lesson 08.03, Lesson 08.07, Lesson 08.08, Lesson 08.14
Abbreviations#
- VAE
- Variational autoencoder. The encoder outputs a distribution, a KL term shapes the code space.
- ELBO
- Evidence lower bound. Reconstruction minus KL, a lower bound on \log p(x).
- GAN
- Generator G(z) and discriminator D(x) in a minimax game.
- DDPM
- Ho et al. 2020. Linear \beta, 1000 steps, U-Net.
- DDIM
- Non-Markov sampler. 20 to 50 steps, same training objective.
- CFG
- Classifier-free guidance. Mixes conditional and unconditional predictions.
- DiT
- Diffusion Transformer. A transformer over latent patches in place of the U-Net.
- FID
- Fréchet Inception Distance. Distance of Gaussian fits to real and generated features.
From Lesson 08.02, Lesson 08.03, Lesson 08.06, Lesson 08.07, Lesson 08.08, Lesson 08.14
Questions#
- Why output log σ² and not σ?
- The output stays unconstrained. Gradients die at σ near 0 with softplus.
- Why are VAE samples blurry?
- MSE reconstruction gives the mean of the plausible outputs.
- Why diffuse in latent space?
- The 64×64×4 latent needs about 64x fewer FLOPs for comparable quality.
- Are flow matching and DDPM different?
- For Gaussian paths they are algebraically equivalent.
- How is FID computed?
- \lVert \mu_r - \mu_g \rVert^2 + \mathop{\mathrm{Tr}}(\Sigma_r + \Sigma_g - 2 (\Sigma_r \Sigma_g)^{0.5}) on Inception-v3 features. Lower is better.
From Lesson 08.02, Lesson 08.07, Lesson 08.13, Lesson 08.14, Lesson 08.03
Tips#
- Use the closed form KL. A Monte Carlo KL is 3x slower.
- Give G fresh fakes at each step, or its gradients are stale.
- Use a proper time embedding for images. Raw t fails.
- Try the cosine schedule when quality plateaus.
- Keep the sum of ControlNet weights near 1.0. Keep LoRA \alpha \le 1.5.
- Report FID, CLIP score and human preference together.
From Lesson 08.02, Lesson 08.03, Lesson 08.06, Lesson 08.08, Lesson 08.14