Generative Vision
Diffusion Sampling Methods
How a trained diffusion model is turned into images; ancestral sampling, DDIM with its deterministic and invertible update, Euler and Heun steps on the probability-flow ODE in the EDM parameterization, DPM-Solver's exponential integrators, predictor–corrector samplers, and the trade-off between network evaluations and sample quality.
advanced
A trained diffusion model is a denoiser: given a noisy image and its step , it predicts the noise . A sampler turns it into images by choosing which noise levels to visit, how to move between them, and whether to inject fresh noise. Samplers trade the number of network evaluations (NFE), which dominates generation time, against sample quality, usually measured by the Fréchet Inception Distance (FID). All the samplers here reuse a network trained with the DDPM objective.
Problem
Given , trained with steps, draw images with as few evaluations as possible. In continuous time (Song et al., 2021) the target is fixed by the reverse SDE and the probability-flow ODE, which share the marginals and whose only unknown is the score (see score matching). A sampler discretizes one of them; with an exact score, the error left is, in our reading, discretization error plus the small mismatch between and .
Inputs and Outputs
- Input: (equivalently the clean-image estimate or the score), its schedule, a step budget, and noise .
- Output: an image ; deterministic samplers also give a map from to that can be run backward.
Intuition
Ancestral sampling is a random walk: each step denoises a little and adds new noise, which washes out errors but needs many small steps. Without the injected noise an ODE remains, whose smooth solution curves from noise to images can be followed with large steps.
Algorithm
All samplers run one loop:
- Draw ; choose steps from , or continuous noise levels.
- At each step, evaluate the network one or more times, form and , and move to the next level, adding noise if the sampler is stochastic.
- Return from the last step.
Ancestral sampling (Ho et al., 2020, Algorithm 2; derived in the DDPM article) visits all steps and adds noise of variance or .
DDIM (Song, Meng & Ermon, 2021) replaces the update by
with and (their Eq. 12; their is this site’s ). The noise is set by one parameter (Eq. 16):
With this is DDPM’s ancestral step with on the chosen steps; with no noise is added after and the sampler is deterministic.
Mathematical Formulation
Why DDIM needs no retraining
Song, Meng and Ermon defined non-Markovian forward processes, Gaussian of mean and variance (Eq. 7), which keep every marginal . The DDPM loss depends only on these marginals, so for each process’s variational objective is a weighted noise-prediction loss plus a constant (Theorem 1; is the limit), and a network trained with Ho et al.’s simple loss serves all of them (exactly so if its parameters were not shared across ). DDIM substitutes for in this posterior. The argument holds on any subsequence (§4.2, Appendix C.1), which licenses skipping steps; the authors spaced quadratically for CIFAR-10 and linearly elsewhere (Appendix D.2).
The ODE behind DDIM
Write , the inverse square root of the signal-to-noise ratio, and . The update becomes (Eq. 13), an Euler step for
(Eq. 14), which Song, Meng and Ermon showed is equivalent to the probability-flow ODE of the variance-exploding SDE (Proposition 1). Karras et al. (2022, §3) reached the same form by setting the noise level equal to time, (their is our ), without rescaling: with denoiser , the choice DDIM makes. The ODE’s solution curves are nearly straight at high and low noise and bend in between, where the denoiser’s output changes between images; one Euler step to zero noise lands on .
Euler, Heun and the step schedule
Euler’s method has local error in the step size . Heun’s method (Karras et al., Algorithm 1) takes the Euler step, evaluates the slope at its end, and redoes the step with the average slope, reaching for one extra evaluation; the last step, to zero noise, stays Euler (§3), so steps cost evaluations. Karras et al. placed the levels by
(Eq. 5, where they write ). Large concentrates steps at low noise; nearly equalized the truncation error per step, but 5 to 10 gave better images, and they chose , with and for their models (Table 1).
Exponential integrators in log-SNR
Lu et al. (2022) solved the linear term of the probability-flow ODE exactly and changed variable to , which gives, in this site’s notation,
(Proposition 3.1). DPM-Solver- Taylor-expands in to order , costs evaluations per step, and has global error in the largest step (Theorem 3.2). The first-order solver is exactly DDIM (§4.1), which, Lu et al. argue, explains why DDIM beats Euler steps in . Since , , and dividing by gives : in our reading, DPM-Solver integrates the same ODE as DDIM and EDM and differs in where it evaluates the network, with steps uniform in (§3.3).
Parameters
- Step count. The main cost knob; Heun and DPM-Solver-2 or -3 spend 2 or 3 evaluations per step, so compare samplers at equal NFE.
- Injected noise. for DDIM. EDM’s stochastic sampler (Algorithm 2) first raises the noise level by a factor , inside a window , by adding noise scaled by , then takes a Heun step down.
Complexity
Time is NFE network passes; the solver’s arithmetic is negligible (Lu et al., 2022, Appendix E.7). Multistep solvers also store earlier evaluations.
Implementation
The NumPy script reuses the diffusion model article’s toy data: four shapes (disk, square, plus sign, diagonal bar) with weights 0.4, 0.3, 0.2 and 0.1 and pixel noise of standard deviation 0.1. Every noisy marginal is a Gaussian mixture, so is exact, and all samplers start from the same 1,000 noise images, so they differ only by discretization. It prints the share of samples nearest each shape and the RMS distance to it, and for deterministic samplers the median distance from a 1,000-step Heun reference and the fraction whose nearest shape differs from the reference’s; then it tests determinism and inversion.
import sys
import numpy as np
seed = int(sys.argv[1]) if len(sys.argv) > 1 else 0
T = 1000
beta = np.linspace(1e-4, 0.02, T) # linear schedule of Ho et al. (2020)
ab = np.cumprod(1 - beta) # ab[t-1] = alpha_bar_t
sig = np.sqrt((1 - ab) / ab) # nu_t of the text (EDM's sigma)
# Data of the diffusion model article: four 16x16 shapes (disk, square, plus, diagonal bar)
# in [-1, 1], x0 = mu_k + 0.1 * noise, with mixture weights 0.4, 0.3, 0.2, 0.1.
yy, xx = np.mgrid[:16, :16]
shapes = [(yy - 7.5) ** 2 + (xx - 7.5) ** 2 < 5.5 ** 2,
(abs(yy - 7.5) < 5) & (abs(xx - 7.5) < 5),
(abs(yy - 7.5) < 2) | (abs(xx - 7.5) < 2),
abs(yy - xx) < 2.5]
mu = np.array([np.where(m, 1.0, -1.0).ravel() for m in shapes]) # (4, 256)
w = np.array([0.4, 0.3, 0.2, 0.1])
tau2 = 0.1 ** 2
# In the variable xbar = x_t / sqrt(ab_t) = x0 + sigma * eps the noisy density is a
# mixture of N(mu_k, (tau2 + sigma^2) I), so the optimal noise prediction
# eps*(xbar; sigma) = E[eps | xbar] = (xbar - E[x0 | xbar]) / sigma is exact.
def eps_star(xb, s):
v = tau2 + s ** 2
d2 = (xb ** 2).sum(1, keepdims=True) - 2 * xb @ mu.T + (mu ** 2).sum(1)
lg = np.log(w) - d2 / (2 * v)
r = np.exp(lg - lg.max(1, keepdims=True))
r /= r.sum(1, keepdims=True)
return (xb - r @ mu) * (s / v) # well defined at sigma = 0, where it is 0
def grid(S): # S evenly spaced steps of 1..T, ending at T
return np.round(np.linspace(1, T, S)).astype(int)
def ddim(xT, S, eta, rng=None, ancestral=False):
"""DDIM update (Song, Meng & Ermon, 2021, Eq. 12) on S grid steps. eta = 1 is DDPM
with sigma_t^2 = beta~_t; ancestral=True adds noise of variance beta_t (Ho et al.)."""
tau, x = grid(S), xT.copy()
for i in range(S - 1, -1, -1):
a = ab[tau[i] - 1]
a_prev = ab[tau[i - 1] - 1] if i > 0 else 1.0
e = eps_star(x / np.sqrt(a), sig[tau[i] - 1])
x0_hat = (x - np.sqrt(1 - a) * e) / np.sqrt(a)
s_eta = eta * np.sqrt((1 - a_prev) / (1 - a) * (1 - a / a_prev))
x = np.sqrt(a_prev) * x0_hat + np.sqrt(1 - a_prev - s_eta ** 2) * e
noise_sd = np.sqrt(1 - a / a_prev) if (ancestral and i > 0) else s_eta
if noise_sd > 0: # eta = 0 uses no randomness after x_T
x = x + noise_sd * rng.standard_normal(x.shape)
return x
def edm_sigmas(N, rho=7.0): # Karras et al. (2022), Eq. 5, then sigma = 0
smax, smin, i = sig[-1], sig[0], np.arange(N)
s = (smax ** (1 / rho) + i / (N - 1) * (smin ** (1 / rho) - smax ** (1 / rho))) ** rho
return np.append(s, 0.0)
def ode(xT, sigmas, heun):
"""Euler or Heun (Karras et al., Algorithm 1 with sigma(t) = t, s(t) = 1) on the
probability-flow ODE d xbar / d sigma = eps*(xbar; sigma)."""
x = xT / np.sqrt(ab[-1]) # the same starting noise, as xbar
for s, s_next in zip(sigmas[:-1], sigmas[1:]):
d = eps_star(x, s)
x_next = x + (s_next - s) * d
if heun and s_next > 0: # 2nd-order correction, skipped at sigma = 0
x_next = x + (s_next - s) * 0.5 * (d + eps_star(x_next, s_next))
x = x_next
return x # at sigma = 0, xbar = x
def nearest(x):
d2 = ((x[:, None, :] - mu[None]) ** 2).mean(-1)
return d2.argmin(1), np.sqrt(d2.min(1)) # nearest shape, RMS distance to it
def dist(a, b): # per-image RMS distance
return np.sqrt(((a - b) ** 2).mean(1))
n = 1000
xT = np.random.default_rng(seed).standard_normal((n, 256)) # one x_T for every sampler
ref = ode(xT, edm_sigmas(1000), heun=True) # 1,000-step Heun reference
k_ref = nearest(ref)[0]
print(f"seed {seed}. 1000-step DDIM eta=0 vs reference: median distance "
f"{np.median(dist(ddim(xT, 1000, 0.0), ref)):.1e}")
same = np.append(sig[grid(20)[::-1] - 1], 0.0) # DDIM's 20 noise levels
print(f"max |DDIM eta=0 - Euler on the same 20 noise levels| = "
f"{np.abs(ddim(xT, 20, 0.0) - ode(xT, same, heun=False)).max():.1e}\n")
print(f"{'sampler':15s}{'NFE':>5s}{'disk':>7s}{'square':>7s}{'plus':>7s}{'bar':>7s}"
f"{'RMS':>7s}{'to ref':>8s}{'shape':>7s}")
print(f"{'data':15s}{'':5s}" + "".join(f"{v:7.3f}" for v in w) + f"{0.1:7.3f}")
rng = lambda: np.random.default_rng(seed + 1)
for S in [10, 20, 50, 100]:
for name, nfe, f in [("DDPM ancestral", S, lambda: ddim(xT, S, 1.0, rng(), ancestral=True)),
("DDIM eta=1", S, lambda: ddim(xT, S, 1.0, rng())),
("DDIM eta=0", S, lambda: ddim(xT, S, 0.0)),
("Euler, rho=7", S, lambda: ode(xT, edm_sigmas(S), heun=False)),
("Heun, rho=7", S - 1, lambda: ode(xT, edm_sigmas(S // 2), heun=True))]:
x = f()
k, d = nearest(x)
line = (f"{name:15s}{nfe:5d}" + "".join(f"{v:7.3f}" for v in np.bincount(k, minlength=4) / n)
+ f"{np.sqrt((d ** 2).mean()):7.3f}")
if name not in ("DDPM ancestral", "DDIM eta=1"): # deterministic samplers
line += f"{np.median(dist(x, ref)):8.4f}{np.mean(k != k_ref):7.1%}"
print(line)
k, d = nearest(ddim(xT, 1000, 1.0, rng(), ancestral=True))
print(f"{'DDPM ancestral':15s}{1000:5d}" + "".join(f"{v:7.3f}" for v in np.bincount(k, minlength=4) / n)
+ f"{np.sqrt((d ** 2).mean()):7.3f}")
# Determinism: two runs from the same x_T with different noise generators.
print()
for eta in [0.0, 1.0]:
x1 = ddim(xT, 50, eta, np.random.default_rng(100))
x2 = ddim(xT, 50, eta, np.random.default_rng(200))
print(f"DDIM eta={eta:.0f}, 50 steps, same x_T, two noise draws: max difference "
f"{np.abs(x1 - x2).max():.2f}, nearest shape differs for "
f"{np.mean(nearest(x1)[0] != nearest(x2)[0]):.1%}")
# Inversion: run the eta = 0 update upward in sigma (an Euler step with eps at the
# current point) from data images to x_T, then generate from that x_T with the same S.
def invert(x0, S):
sigmas = np.append(0.0, sig[grid(S) - 1])
x = x0.copy()
for s, s_next in zip(sigmas[:-1], sigmas[1:]):
x = x + (s_next - s) * eps_star(x, s)
return x * np.sqrt(ab[-1]) # x_T = sqrt(ab_T) * xbar_T
r = np.random.default_rng(seed + 2)
x0 = mu[r.choice(4, n, p=w)] + 0.1 * r.standard_normal((n, 256))
print("\ninversion of 1,000 data images, x0 -> x_T -> x0' with S steps each way")
print(f"{'S':>5s}{'median RMS(x0 - x0prime)':>26s}{'shape changed':>15s}{'std of x_T':>12s}")
for S in [10, 20, 50, 100, 1000]:
z = invert(x0, S)
x0p = ddim(z, S, 0.0)
print(f"{S:5d}{np.median(dist(x0, x0p)):26.4f}"
f"{np.mean(nearest(x0)[0] != nearest(x0p)[0]):15.1%}{z.std():12.3f}")
Output:
seed 0. 1000-step DDIM eta=0 vs reference: median distance 1.2e-03
max |DDIM eta=0 - Euler on the same 20 noise levels| = 4.7e-14
sampler NFE disk square plus bar RMS to ref shape
data 0.400 0.300 0.200 0.100 0.100
DDPM ancestral 10 0.461 0.304 0.158 0.077 0.353
DDIM eta=1 10 0.457 0.309 0.160 0.074 0.027
DDIM eta=0 10 0.424 0.312 0.169 0.095 0.035 0.0655 5.6%
Euler, rho=7 10 0.434 0.312 0.166 0.088 0.068 0.0337 7.2%
Heun, rho=7 9 0.385 0.316 0.189 0.110 0.429 0.2604 2.6%
DDPM ancestral 20 0.408 0.320 0.172 0.100 0.196
DDIM eta=1 20 0.405 0.327 0.170 0.098 0.046
DDIM eta=0 20 0.402 0.310 0.182 0.106 0.054 0.0457 2.6%
Euler, rho=7 20 0.408 0.307 0.181 0.104 0.082 0.0179 3.3%
Heun, rho=7 19 0.386 0.306 0.195 0.113 0.143 0.0433 0.3%
DDPM ancestral 50 0.398 0.304 0.195 0.103 0.129
DDIM eta=1 50 0.398 0.305 0.195 0.102 0.068
DDIM eta=0 50 0.392 0.308 0.190 0.110 0.078 0.0214 1.0%
Euler, rho=7 50 0.394 0.306 0.190 0.110 0.092 0.0074 1.2%
Heun, rho=7 49 0.387 0.304 0.195 0.114 0.104 0.0049 0.0%
DDPM ancestral 100 0.383 0.312 0.205 0.100 0.113
DDIM eta=1 100 0.380 0.315 0.205 0.100 0.079
DDIM eta=0 100 0.390 0.306 0.192 0.112 0.088 0.0112 0.5%
Euler, rho=7 100 0.390 0.307 0.192 0.111 0.096 0.0037 0.6%
Heun, rho=7 99 0.387 0.304 0.195 0.114 0.101 0.0011 0.0%
DDPM ancestral 1000 0.384 0.322 0.200 0.094 0.101
DDIM eta=0, 50 steps, same x_T, two noise draws: max difference 0.00, nearest shape differs for 0.0%
DDIM eta=1, 50 steps, same x_T, two noise draws: max difference 2.38, nearest shape differs for 69.9%
inversion of 1,000 data images, x0 -> x_T -> x0' with S steps each way
S median RMS(x0 - x0prime) shape changed std of x_T
10 0.0886 11.2% 0.346
20 0.0709 2.5% 0.541
50 0.0388 1.1% 0.782
100 0.0220 0.9% 0.883
1000 0.0033 0.2% 0.983
The second line confirms that DDIM with is Euler’s method. At 10 evaluations the samplers fail differently: ancestral DDPM leaves samples noisy (RMS 0.353 against the data’s 0.1), DDIM over-smooths them (0.027 for , 0.035 for ), and both stochastic samplers over-produce the common disk and under-produce the rare bar. Heun with 5 steps has not converged, but from about 50 evaluations it leads: its median distance from the reference falls fourfold from 49 to 99 NFE, the second-order rate, to about that of 1,000 DDIM steps, while DDIM’s error at best halves when its steps double. Euler on levels beats DDIM’s uniform steps in median distance but changes more shapes, in our reading because its steps are longer at high noise, where the shape is decided.
From one , gives identical images, while picks a different shape for 69.9%, near the expected for independent runs. Inversion’s median error is 0.0033 with 1,000 steps but 0.0886 with 10, whose latents have standard deviation 0.346, unlike Gaussian noise. Over seeds 0 to 4 (the script’s argument), Heun’s median distance at 99 NFE was always 0.0011, the deterministic samplers at 99 or 100 NFE changed at most 0.7% of the reference’s shapes, and inversion changed the shape of 9.3% to 11.2% of images at 10 steps and 0.0% to 0.2% at 1,000.
Properties and Behavior
- NFE versus FID. On CIFAR-10, DDIM’s FID was 13.36, 6.84, 4.67, 4.16 and 4.04 with 10, 20, 50, 100 and 1,000 steps; with it was 41.07, 18.36, 8.01, 5.78 and 4.73, and ancestral sampling with gave 367.43 at 10 steps but the best value, 3.17, at 1,000 (Table 1, identical in arXiv v1, v2 and v4). The abstract claims wall-clock speed-ups of 10 to 50 times, §5 10 to 100. Karras et al.’s changes cut the NFE for near-best FID by 7.3, 300 and 3.2 times on three pretrained models (§3), and their retrained networks reached CIFAR-10 FIDs of 1.79 (class-conditional) and 1.97 (unconditional) at 35 NFE (Table 2, arXiv v2). DPM-Solver reported 4.70 at 10 NFE and 2.87 at 20 on CIFAR-10, the two with different end times, and (Table 3, arXiv v3).
- Stochastic versus deterministic. Both reach the same distribution in theory, but Karras et al. argue that injected noise corrects earlier errors: on ImageNet their stochastic sampler reached 1.55 at 511 NFE against 2.64 at 79 deterministically (Tables 3–4, arXiv v2). Their text starts from ADM’s published 2.07; Table 4 lists 2.01 for the original sampler. Too much churn lost detail and oversaturated colors, and the settings needed a per-model grid search (§4).
- Determinism and inversion. A deterministic sampler draws all its diversity from . Trajectories of different lengths from one kept the same high-level features, making interpolation in meaningful, and encoding then decoding CIFAR-10 test images gave a per-dimension mean squared error (pixels in ) of 0.014 with 10 steps and 0.0001 with 1,000 (Song, Meng & Ermon, §5.2–5.4, Table 2 of arXiv v2 and later). Mokady et al. (2023) note that inversion works for unconditional models, but the large classifier-free guidance scales used for editing with Stable Diffusion amplify its error; they take the unguided inversion as a pivot and optimize the unconditional text embedding.
Limitations
- Strong guidance destabilizes high-order solvers; Lu et al. (2025) report earlier ones becoming slower than DDIM at large guidance scales.
- Step placement, churn and the last step are tuned per model, so a reported FID reflects the sampler as well as the network.
- Inversion is exact only in the small-step limit; with large steps the toy example regenerates some images as other shapes.
Variants
Predictor–corrector samplers (Song et al., 2021, §4.2) follow each reverse-SDE step with Langevin steps toward ; on CIFAR-10, one corrector step per predictor step always beat the predictor alone.
DPM-Solver++ (Lu et al., 2025, read in arXiv v3) integrates the prediction, adds thresholding and a multistep variant, and reports good guided samples in 15 to 20 steps.
UniPC (Zhao et al., 2023) adds a corrector that raises the order of accuracy without extra evaluations and reduces to DDIM at first order (§3.3).
Distillation trains a new network: progressive distillation reached a CIFAR-10 FID of 3.00 in 4 steps (Salimans & Ho, 2022, Table 2, arXiv v2), and consistency models distilled from a diffusion model reached one-step FIDs of 3.55 on CIFAR-10 and 6.20 on ImageNet (Song et al., 2023). Latent diffusion instead makes each evaluation cheaper.
Related
- Diffusion Model
A generative model that learns to reverse a gradual Gaussian noising of images, generating by starting from pure noise and denoising step by step; the forward and reverse processes, the noise, data and score parameterizations, the U-Net and transformer denoisers, noise schedules, conditioning and the unifying score-SDE view.
- Classifier-Free Guidance
A sampling technique for conditional diffusion models that trains one network with and without the condition and extrapolates from the unconditional prediction toward the conditional one, trading sample diversity for fidelity; classifier guidance, the implicit-classifier view, the guidance-scale conventions, negative prompts, dynamic thresholding and the cost of two passes.
- Latent Diffusion Model
A diffusion model that runs in the compressed latent space of a pretrained autoencoder instead of in pixels; the two-stage design, the downsampling factor and its trade-off, the latent scaling factor, cross-attention and concatenation conditioning, the compute savings, and successors from SDXL to latent transformers and rectified flow.
- Fréchet Inception Distance
The standard sample-based score for image generative models, the Fréchet (2-Wasserstein) distance between Gaussians fitted to Inception-v3 features of real and generated images; its formula, its small-sample bias, its sensitivity to resizing and compression, its ImageNet bias, and the related metrics IS, KID, precision and recall, and FD-DINOv2.
- Generative Model
A model of the probability distribution of images that can draw new samples from it; the overview of the main families (autoregressive models, variational autoencoders, normalizing flows, GANs and diffusion models), how they differ, and how they are compared.
- Image Denoising
Estimating a clean image from a noisy observation, removing random variation while keeping edges, texture, and fine detail.
References
- Song, J., Meng, C. & Ermon, S. (2021). Denoising Diffusion Implicit Models. International Conference on Learning Representations (ICLR). arXiv:2010.02502.
- Ho, J., Jain, A. & Abbeel, P. (2020). Denoising Diffusion Probabilistic Models. Advances in Neural Information Processing Systems 33 (NeurIPS), 6840–6851.
- Song, Y., Sohl-Dickstein, J., Kingma, D. P., Kumar, A., Ermon, S. & Poole, B. (2021). Score-Based Generative Modeling through Stochastic Differential Equations. International Conference on Learning Representations (ICLR). arXiv:2011.13456.
- Karras, T., Aittala, M., Aila, T. & Laine, S. (2022). Elucidating the Design Space of Diffusion-Based Generative Models. Advances in Neural Information Processing Systems 35 (NeurIPS), 26565–26577.
- Lu, C., Zhou, Y., Bao, F., Chen, J., Li, C. & Zhu, J. (2022). DPM-Solver: A Fast ODE Solver for Diffusion Probabilistic Model Sampling in Around 10 Steps. Advances in Neural Information Processing Systems 35 (NeurIPS), 5775–5787.
- Lu, C., Zhou, Y., Bao, F., Chen, J., Li, C. & Zhu, J. (2025). DPM-Solver++: Fast Solver for Guided Sampling of Diffusion Probabilistic Models. Machine Intelligence Research, 22(4), 730–751. Preprint arXiv:2211.01095 (2022); content cited from arXiv v3.
- Zhao, W., Bai, L., Rao, Y., Zhou, J. & Lu, J. (2023). UniPC: A Unified Predictor-Corrector Framework for Fast Sampling of Diffusion Models. Advances in Neural Information Processing Systems 36 (NeurIPS), 49842–49869.
- Mokady, R., Hertz, A., Aberman, K., Pritch, Y. & Cohen-Or, D. (2023). Null-text Inversion for Editing Real Images using Guided Diffusion Models. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 6038–6047.
- Salimans, T. & Ho, J. (2022). Progressive Distillation for Fast Sampling of Diffusion Models. International Conference on Learning Representations (ICLR). arXiv:2202.00512.
- Song, Y., Dhariwal, P., Chen, M. & Sutskever, I. (2023). Consistency Models. Proceedings of the 40th International Conference on Machine Learning (ICML), PMLR 202, 32211–32252.