Diffusion models from the ground up
Sampling step by step
Ancestral sampling walks the chain with fresh noise at every step; DDIM solves the same model as an ODE. The currency is model calls, not steps.
Ancestral sampling
The simplest sampler walks the chain backwards one step at a time. At each step it predicts the noise, computes the reverse mean, and adds Gaussian noise with the posterior variance. The added noise is not a bug. It is what makes the sample a draw from the distribution rather than a single fixed answer. At the final step the noise is switched off, because there is no next step to correct.
Ancestral sampling needs as many passes as there were training steps, usually a thousand. That is slow, and much of the research on samplers is about doing it in fewer. The first big win was recognising that the reverse process can be written as a differential equation. A differential equation can be solved with a better integrator than Euler with a thousand tiny steps. The gap between a thousand steps and fifty is not a small optimisation. It is the difference between a batch job and an interactive product.
DDIM and the deterministic path
DDIM keeps the same trained model and changes the update. It estimates x_0 at each step. Then it jumps straight to an earlier noise level, using that estimate and the predicted noise. No fresh noise is added. With the random term set to zero, the map from noise to image is deterministic. The same seed gives the same image, and interpolating in the initial noise gives a smooth interpolation in the output.
The deterministic version is also what makes 50-step sampling possible. The update is a coarse integration of an ordinary differential equation. A larger step is therefore a guess rather than a different chain. The error shrinks predictably as steps increase. The same model that was trained with a thousand noise levels can be sampled at fifty. The timesteps you jump between only need to be a subset of the ones it knows. The catch is that the model was trained on every timestep, so choose the subset with the schedule in mind. Do not pick it uniformly out of habit.
Fifty steps with DDIM
import torch
from diffusers import DDIMScheduler, UNet2DModel
unet = UNet2DModel.from_pretrained("google/ddpm-cifar10-32")
scheduler = DDIMScheduler.from_pretrained("google/ddpm-cifar10-32")
scheduler.set_timesteps(50)
sample = torch.randn(4, 3, 32, 32)
for t in scheduler.timesteps:
noise_pred = unet(sample, t).sample
sample = scheduler.step(noise_pred, t, sample).prev_sample
print(sample.shape)Fewer steps, more error
Run a deterministic sampler with 10, 25, 50 and 100 steps on the two-point dataset. Compare how close the samples land to the two modes as the step count rises.
Solvers, order and cost
Every sampler is a trade between the number of model calls and the accuracy of the integration. Euler is first order: error falls in a straight line with step size. Heun and midpoint methods evaluate the model twice per step and are second order, which usually wins when the model is the expensive part. DPM-Solver and its descendants use the semi-linear shape of the probability flow to take larger steps without losing accuracy.
What you pay in is model calls, not steps. A 20-step Heun sampler costs 40 calls, and comparing it with a 40-step Euler sampler is the honest comparison. Papers usually report both. When you ship, measure quality against calls at the step count you can afford. Do not assume that a method tuned for 250 steps still wins at 20.
Compare samplers at equal model calls, not equal steps. A second-order method that uses two calls per step is only a win if it beats the first-order method at the same total budget.
Stochastic or deterministic
The choice changes what the sampler is for. Stochastic ancestral sampling explores the distribution and gives variety from the same start. Deterministic DDIM follows one path and is better for editing, interpolation and inversion. Many production samplers sit between the two with a parameter that controls how much noise is put back, usually called eta. Eta is the dial between them. At zero the update is DDIM, at one it is the ancestral sampler, and in between you get a family that trades variety for stability.
Neither is more correct. The score is the same. Only the step size and the amount of added noise differ. What matters is that you know which one you are running. The same seed and the same prompt will not give the same image across them. Record the sampler, the step count and eta alongside the checkpoint, or a result will not be reproducible.
Follow one sample down
Print the value and the clean estimate at every step of a twenty-step run. The value barely moves for most of the run while the estimate sharpens, then both settle in the last few steps.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What makes ancestral sampling stochastic?
