Generative models II: Diffusion and flow matching
Destroy data with noise, learn to reverse it — the math behind image and video models
Diffusion models power essentially every image, video and audio generator of the 2020s, and flow matching is their streamlined successor. The core is surprisingly small: a Gaussian forward process you can write in closed form, a denoising network trained with a mean-squared error, and a handful of sampling rules. The ELBO from 8.8 Generative models I: VAEs and GANs and the Gaussian algebra from 1.6 Probability I: random variables and distributions are all you need; the U-Net and transformer backbones come from 5.6 Segmentation and 7.4 Inside the block: residuals, LayerNorm, FFN and embeddings.
You'll be able to
- Derive the closed-form forward process q(x_t | x_0) and explain noise schedules
- Explain how the ELBO reduces to the simple ε-prediction loss and implement a training step
- Write the DDPM and DDIM sampling updates and explain the score-matching view
- Apply classifier-free guidance and explain latent diffusion and transformer backbones
- State the flow-matching objective and why it became the default for new models
Key ideas
- Forward process: x_t = √ᾱ_t x_0 + √(1−ᾱ_t) ε in closed form, with ᾱ_t = Π(1−β_s) and x_T ≈ N(0, I).
- The ELBO reduces to a weighted MSE between posterior and predicted means; dropping weights gives L_simple = E‖ε − ε_θ(x_t, t)‖².
- DDPM sampling runs the stochastic reverse chain for ~1000 steps; DDIM's deterministic rule predicts x̂_0 and jumps, allowing 20–50 steps and reproducible samples.
Show 4 more key ideas
- ε_θ is a scaled score estimate; the SDE/probability-flow-ODE view unifies samplers and connects to flow matching.
- Classifier-free guidance extrapolates between unconditional and conditional predictions with weight w, trading diversity for prompt adherence.
- Latent diffusion runs in a VAE's compressed space; U-Nets gave way to Diffusion Transformers (DiT) as the backbone.
- Flow matching learns a velocity field along straight data–noise paths, v_θ ≈ ε − x_0, and is the default training objective for new models; distillation reduces sampling to 1–4 steps.
Suggested path through this lesson
The idea
Take an image, add a little Gaussian noise, repeat times until nothing but noise is left. That forward process is fixed and needs no learning. The model learns the reverse process: given a noisy image and the step index, predict a slightly less noisy one. Chain the reverse steps from pure noise and you generate. Equivalently, the model learns the score — the direction toward higher data density — at every noise level.
The forward process, in closed form
With variances , each step is . Define and . Then
Math check (two steps). and . Substituting: . The two independent Gaussian noises merge into one with variance (1.6 Probability I: random variables and distributions). Induction gives the formula.
A schedule sets the : DDPM used linear from to over ; cosine schedules keep from collapsing too early. Because , is indistinguishable from — exactly the distribution we can sample at generation time. The demo below lets you drag and watch a dataset dissolve.
From the ELBO to a mean-squared error
The reverse model is trained with an ELBO (8.8 Generative models I: VAEs and GANs) that decomposes into KL terms between Gaussians at each step. The target in each term is the true posterior , which is Gaussian with mean
so each KL is a weighted squared distance between and . Since , predicting the mean is equivalent to predicting the noise: parametrize
Dropping the per-step weights (which empirically improves samples) leaves the celebrated simple loss:
Training is therefore: pick an image, a random step and random noise; noise the image in one shot; ask the network which noise was added; take an MSE gradient step. Alternatives predict directly or the velocity , which is better behaved at both ends of the schedule.
def ddpm_loss(model, x0, alpha_bar): # alpha_bar: tensor of length T
t = torch.randint(0, len(alpha_bar), (x0.shape[0],), device=x0.device)
ab = alpha_bar[t].view(-1, 1, 1, 1)
eps = torch.randn_like(x0)
x_t = ab.sqrt() * x0 + (1 - ab).sqrt() * eps
return F.mse_loss(model(x_t, t), eps)
Sampling: DDPM and DDIM
DDPM (ancestral) sampling runs the learned reverse chain from :
with or the posterior variance . A thousand network evaluations per image is slow. DDIM observed that the same trained network defines a family of non-Markovian processes; the deterministic member () first estimates the clean image, , then jumps:
Because it is deterministic, you can skip steps (20–50 instead of 1000) and the same noise always gives the same image — which enables interpolation and editing. Higher-order ODE solvers (DPM-Solver and relatives) push quality at 10–20 steps.
The score view
From the forward process, , so the noise predictor is a scaled score estimate: . Song et al. unified diffusion as a stochastic differential equation; its probability-flow ODE has the same marginals and is the continuous version of DDIM's deterministic sampler. This view makes "noise levels" continuous and connects directly to flow matching below.
Conditioning and classifier-free guidance
To generate from text, feed a conditioning signal (text-encoder embeddings) into the network — via cross-attention in a U-Net or as extra tokens in a transformer (8.10 Multimodal models). Classifier-free guidance (CFG) trains one network both conditionally and, by randomly dropping (about 10% of the time), unconditionally. At sampling time, extrapolate:
is plain conditional sampling; – sharpens adherence to the prompt at the cost of diversity (in the score view it samples from ). Two network evaluations per step are the price.
Latent diffusion and backbones
Running diffusion on raw pixels at high resolution is expensive. Latent diffusion (Stable Diffusion) first trains a VAE (8.8 Generative models I: VAEs and GANs) that compresses a image into a latent — 48× fewer numbers — then diffuses in latent space and decodes at the end. The denoiser was a U-Net (5.6 Segmentation) with cross-attention to the text encoder; since 2023 Diffusion Transformers (DiT) patchify the latent and apply a transformer (7.4 Inside the block: residuals, LayerNorm, FFN and embeddings), scaling more predictably with compute. Modern image and video systems use transformer backbones with joint text–image attention.
Flow matching: straight lines instead of random walks
Instead of a noising Markov chain, define a straight interpolation between a data point and noise with :
The network learns a velocity field; sampling integrates the ODE from noise () to data () with a few Euler steps. Straight paths mean fewer steps and simpler schedules; the objective is equivalent to diffusion with a particular schedule and the -parametrization, so everything above (CFG, latents, DiT) carries over. Since 2024 most new image and video models — and many audio models — train this way (rectified flow). Distillation — consistency models, progressive and adversarial distillation — then compresses dozens of steps into one to four for interactive use.
Where this shows up later
- Text conditioning, CLIP/T5 encoders and multimodal generation — 8.10 Multimodal models.
- The backbone is a U-Net or a transformer — 5.6 Segmentation, 7.4 Inside the block: residuals, LayerNorm, FFN and embeddings; the compressor is a VAE — 8.8 Generative models I: VAEs and GANs.
- Safety questions of generative media (deepfakes, provenance) — 8.12 Safety, risks and responsible AI.
Common pitfalls (4)
- Implementing the forward process step by step in a loop instead of sampling x_t in one shot from the closed form.
- Forgetting to drop the noise term at the last DDPM step, or using DDIM's update with freshly sampled noise.
- Setting CFG weight very high and mistaking oversaturated, low-diversity outputs for 'better quality'.
- Confusing the diffusion time convention (t = T is noise) with flow matching's (t = 1 is noise) when porting code.
Recap: all 7 key points
- Forward process: x_t = √ᾱ_t x_0 + √(1−ᾱ_t) ε in closed form, with ᾱ_t = Π(1−β_s) and x_T ≈ N(0, I).
- The ELBO reduces to a weighted MSE between posterior and predicted means; dropping weights gives L_simple = E‖ε − ε_θ(x_t, t)‖².
- DDPM sampling runs the stochastic reverse chain for ~1000 steps; DDIM's deterministic rule predicts x̂_0 and jumps, allowing 20–50 steps and reproducible samples.
- ε_θ is a scaled score estimate; the SDE/probability-flow-ODE view unifies samplers and connects to flow matching.
- Classifier-free guidance extrapolates between unconditional and conditional predictions with weight w, trading diversity for prompt adherence.
- Latent diffusion runs in a VAE's compressed space; U-Nets gave way to Diffusion Transformers (DiT) as the backbone.
- Flow matching learns a velocity field along straight data–noise paths, v_θ ≈ ε − x_0, and is the default training objective for new models; distillation reduces sampling to 1–4 steps.
Watch
The DDPM derivation at exactly the level of this lesson.
Read
Try it live
Interactive demo
runs in your browserExercises
- Derive the two-step closed form
Carry out the induction sketched in the notes to show for general . Then compute numerically for the linear schedule with and find the step where falls below 0.5. Expected: a few hundred steps in.
- Diffusion on a 2-D spiral
Train a small MLP on points from a 2-D spiral with . Implement DDPM and DDIM (20 steps) sampling and plot the generated points. Expected: both recover the spiral; DDIM is 10× faster and deterministic given the seed.
- Classifier-free guidance on labels
Extend the spiral model to two interleaved spirals with a class label, dropping the label 10% of the time during training. Sample with . Expected: higher separates classes more sharply but shrinks their spread.
- Flow matching rewrite
Reimplement the spiral model with the flow-matching loss and 10 Euler steps. Compare sample quality and wall-clock time with DDPM/DDIM. Expected: similar quality with the simplest code of the three.
Quiz
Flashcards
These cards are now in your spaced-repetition queue. Review all due cards →