This section is about testing diffusion models and the claims people make about them. It shows how to decide whether one method really beats another, how to measure samples when the true distribution is known, how to catch a model that copies its training data, and how to find the bug when samples come out wrong. At the end it carries diffusion from images to text. These are the skills a lab expects when you report a result or review someone else's, and interviewers ask about them right after the derivations.
Here is the kind of claim the section teaches you to check. A colleague trains two generative models on the same 2D data, with the same network and the same training budget. One predicts the noise that was added to a data point, as the denoising diffusion probabilistic model (DDPM) of §33.2 does. The other predicts a velocity along a straight path from data to noise, as the flow matching of §33.6 does. Each model draws samples with 8 calls of its network, on its usual time grid. Averaged over five training runs, 29 percent of the noise predictor's samples land off the data, against 21 percent for flow matching. Is flow matching the better method?
Hold the answer for one more measurement. Replace both networks by the exact denoiser of the data, the best function any training could produce, and run the same two samplers with the same 8 calls. Now the first puts 10.6 percent of its samples off the data and the second 5.2 percent. Nothing was learned, so the only difference left is where each sampler places its 8 steps. Yet one sampler is already twice as accurate as the other. Before the method gets the credit, we need to know how much of the gap comes from the time grid, how much from the network, and how much from the luck of one training run.
We answer that in steps. First we split a sample's error into the sampler's part and the network's part. Then we show that every training loss of the chapter is one loss with a different weight on each noise level. We build distances between sets of samples, run a fair comparison with confidence intervals, test for copying, and collect the debugging checks that save weeks. The last part builds diffusion for text, the masked diffusion language models that now compete with left-to-right decoding.
Here is the setting, restated so the section stands on its own. A data point is x0∈Rd, with d=2 in this lab. Time runs from data at t=0 to pure noise at t=1. The noisy point at time t is a shrunken copy of the data plus Gaussian noise (§33.1),
xt=αtx0+σtϵ,ϵ∼N(0,I),
where the signal scale αt falls and the noise scale σt rises as t grows. One number says how noisy xt is, the log signal-to-noise ratio λt=log(αt2/σt2), or log-SNR. At λ=0 signal and noise have equal power. At λ=4 the signal has e4≈55 times the power of the noise, and at λ=−4 the noise has 55 times the power of the signal.
The data are the chapter's 8-Gaussian ring. It has eight equally likely clusters, called modes, with centers μ1,…,μ8 on a circle of radius 1.411. Each mode is a round Gaussian with standard deviation s=0.0705, and the scale is chosen so that each coordinate has unit variance. The exact denoiserE[x0∣xt] is the average of all the clean points that could have produced the noisy point xt, each weighted by how likely it is. It is the best possible prediction of x0 under squared error (§33.2). A noised Gaussian mixture is again a Gaussian mixture, so for the ring the exact denoiser, the score, and the velocity all have closed forms. That is what makes an honest lab possible. We can always compare a trained network with the truth.
A sample is wrong for three reasons
A sample from a trained diffusion model can be wrong for three separate reasons, and each one has its own fix.
Discretization. A sampler follows a continuous path from noise to data in a finite number of steps. The deterministic sampler of §33.4, called DDIM, follows the probability flow ODE of §33.3, the differential equation that carries noise to data. It takes Euler steps, each of which moves in a straight line along the current direction. On a curved path that leaves an error, which shrinks as the steps shrink.
Estimation. The network is not the exact denoiser. Its capacity and its training are finite, and its error differs from one noise level to another.
Weighting. Training spreads limited capacity over the noise levels. How much the loss charges for an error at each level decides which levels the network gets right. This choice changes the network's errors without changing the function it aims at.
A fourth error lives outside the model. Every metric is computed from finitely many samples, so it has noise and blind spots of its own.
The first two errors can be separated with one trick. Run the sampler twice, once calling the network and once calling the exact denoiser.
In plain words, run the sampler once with the network and once with the truth. The second run shows the error the sampler makes on its own. Whatever is left over belongs to the network. On a Gaussian mixture the oracle is a few lines of NumPy, so the split costs nothing.
The bound needs a distance with the triangle inequality, which says that the direct distance between two distributions is never longer than a detour through a third. Two distances built below have it, the sliced Wasserstein distance and the square root of the maximum mean discrepancy. A rate such as "the share of samples off the data" is not a distance between distributions, but it still splits usefully in practice, as the bench shows. This split is the first experiment to run when samples look wrong. An interviewer who asks "is it the sampler or the model?" wants to hear it.
Every loss is the noise loss with a weighting
The chapter has trained networks to predict four different things.
The velocity v=αtϵ−σtx0 on a variance-preserving path, one with αt2+σt2=1 (§33.4).
The flow-matching velocity u=ϵ−x0 on the linear path, αt=1−t and σt=t (§33.6).
They look like four methods. At a fixed noisy point they are one prediction written in four units. The identity xt=αtx0+σtϵ ties them together, so a guess of any one of them gives a guess of the other three. An error in one is then an error in the noise times a factor that depends only on the noise level.
Here is the simplest case with numbers. A noise guess ϵ^ implies the data guess x^0=(xt−σtϵ^)/αt, so the data error is the noise error times σt/αt=e−λt/2. At λ=0 the factor is 1, and the two errors are equal. At λ=−4 the factor is e2=7.4, so a noise guess that is off by 0.1 gives a data guess that is off by 0.74. Squaring gives the factor e−λ on squared errors.
So the loss values of two parameterizations can never be compared directly. A flow-matching loss of 4 and a noise loss of 1 can describe the same network.
∂ Proof of the three conversions
Clean data. From xt=αtx0+σtϵ, the prediction is x^0=(xt−σtϵ^)/αt. Subtract the same identity for the true values to get x0−x^0=−αtσt(ϵ−ϵ^). Squaring gives the factor σt2/αt2=e−λt.
Velocity, variance preserving. With αt2+σt2=1 and v=αtϵ−σtx0, the error is
Flow matching on the linear path. With αt=1−t, σt=t, and u=ϵ−x0, the error is u−u^=(ϵ−ϵ^)−(x0−x^0)=(1+1−tt)(ϵ−ϵ^)=1−t1(ϵ−ϵ^). On this path eλt/2=(1−t)/t, so 1/(1−t)=1+e−λt/2.
The per-point factor is half the story. The other half is how often training visits each noise level. A loss draws t from a density p(t), and through the schedule that becomes a density over λ. The product of the factor and that density is the weighting functionw(λ) of the loss, the weight per unit of log-SNR that it puts on the noise error. §33.2 introduced it for the variational bound.
∂ Proof by a change of variables
Write t=t(λ) for the inverse of the strictly decreasing map t↦λt, and change variables in the outer expectation. The density transforms as p(t)dt=p(t(λ))∣dt/dλ∣dλ, and the limits become λmin=λ1 and λmax=λ0. For the second claim, divide the noisy point by αt. The result xt/αt=x0+e−λt/2ϵ has a law given λ that involves no other feature of the path. ∎
In plain words, a loss is described by two things, how much it charges for an error at each noise level and how often it visits that level. Only their product matters for what the loss minimizes. Kingma and Gao (2023) made this point and tabulated the weightings used in the literature. Their w is twice ours for the same loss, because they put a factor 21 in front, and their table also drops constant factors. A constant factor changes neither the minimizer nor the training run, since the steps of the Adam optimizer (§0.6) do not change when the loss is rescaled, apart from its small stabilizing constant.
The table collects the losses of this chapter. The middle columns are what the training code does, and the last two are the weighting it implies. Two rows come from specific recipes. Stable Diffusion 3 draws t from a logit-normal density, meaning that logitt=log1−tt is Gaussian, which concentrates training on middle noise levels (Esser et al. 2024). The design of Karras et al. (2022), called EDM and met in §33.4, draws the noise level from a log-normal density and weights the loss so that the network's target has unit scale at every level. The last column asks whether w never rises as the data get cleaner, a property we use below.
loss
time density and path
per-point factor c
w(λ)
nonincreasing in λ
continuous-time bound
any
(the bound itself)
21
yes (flat)
noise prediction, DDPM's linear β
t∼U(0,1), αt2=e−0.1t−9.95t2
1
∣dt/dλ∣
no, peaks at λ≈−0.9
noise prediction, cosine
t∼U(0,1), αt=cos(πt/2)
1
2π1sech(λ/2)
no, peaks at 0
velocity v, cosine
same
1+e−λ
π1e−λ/2
yes
flow matching, uniform t
t∼U(0,1), αt=1−t
(1+e−λ/2)2
21e−λ/2
yes
flow matching, logit-normal t (Stable Diffusion 3)
logitt∼N(0,1), same path
(1+e−λ/2)2
(1+e−λ/2)2N(λ;0,22)
no, peaks at λ≈−3.4
EDM
lnσ∼N(−1.2,1.22), σdata=0.5
1+e−λ/σdata2
N(λ;2.4,2.42)(1+4e−λ)
no, peaks at λ≈−3.3
∂ Where three of the table's weightings come from
Cosine path. Here e−λ/2=σt/αt=tan(πt/2), so t=π2arctane−λ/2. Differentiating gives ∣dt/dλ∣=2π1sech(λ/2), the density that §33.1 found. For velocity prediction, multiply by 1+e−λ. Since sech(λ/2)=2/(eλ/2+e−λ/2), the product simplifies to π1e−λ/2.
Linear path. Here t=1/(1+eλ/2), so ∣dt/dλ∣=81sech2(λ/4). Multiplying by c=(1+e−λ/2)2=1/(1−t)2 leaves 21e−λ/2. For the logit-normal density, note that λ=2log((1−t)/t)=−2logitt, so λ∼N(0,22) already includes the Jacobian.
EDM. EDM works in variance-exploding coordinates, where the noise level is σ=e−λ/2. So lnσ∼N(−1.2,1.22) means λ=−2lnσ∼N(2.4,2.42). Its loss weight is (σ2+σdata2)/(σσdata)2 on ∥Dθ−x0∥2=σ2∥ϵ−ϵ^∥2, where Dθ is the network's denoised output. The product is the factor in the table.
One row deserves a second look. Velocity prediction with the cosine schedule and flow matching with uniform t have the same weighting up to the constant 2/π. As objectives they are the same, which Kingma and Gao also observed. Two things still differ between them. One is the parameterization, meaning what the network outputs and how its input is scaled. The other is the density from which training draws λ. The bench below shows that, for a small network, these differences are not small.
The cell computes every weighting from its training recipe alone, with a numerical derivative of t(λ), and prints where each one puts its weight.
Python · cell 1
The numbers match the table. Noise prediction with the cosine schedule is symmetric in λ. It charges a noisy level and a clean one equally, w(−4)/w(4)=1. The velocity and flow-matching losses charge the noisy level e4=54.6 times more. EDM sits between them at 7.3, and DDPM's linear schedule at 3.7.
Keep in mind that w multiplies the noise error, and the noise error itself vanishes at high noise. There xt is almost all noise, so the guess ϵ^≈xt is nearly right for free. A large weight at high noise does not mean a large share of the loss.
When a weighting is a true bound
The simple noise loss drops the weights of the variational bound, and §33.2 showed that it then no longer bounds the likelihood of the data. There is still a precise sense in which many weighted losses are likelihoods. If the weight never grows as the data get cleaner, the loss is a likelihood bound for the data after extra Gaussian noise of a random level is added. Kingma and Gao (2023) proved this.
∂ Proof by integration by parts
Apply the continuous-time bound of §33.2 to the interval [t,1]. It gives L(t;x0)=21∫λ1λtE∥ϵ−ϵ^∥2dλ+const, so dtdL(t;x0)=21dtdλE∥ϵ−ϵ^∥2. Hence Lw=−∫01w(λt)dtdL(t;x0)dt. Integrate by parts,
The last term is the prior term, which has no parameters. If w is nondecreasing in t, then pw≥0. Its total mass plus the point mass at 0 is ∫01pwdt+w(λ0)=w(λ1)=1, so it is a probability distribution. ∎
Read the last column of the table with this theorem. The bound itself, velocity prediction with the cosine schedule, and flow matching with uniform t are monotone, so each is a likelihood of noise-augmented data. Noise prediction with either schedule, EDM, and the logit-normal density of Stable Diffusion 3 are not. Kingma and Gao then trained models with new monotone weightings. On 64×64 ImageNet, w(λ)=sigmoid(−λ+2) beat the usual noise loss. A monotone version of EDM's weighting reached state-of-the-art FID at 128, 256, and 512 pixels among models sampled without guidance. FID, the Fréchet Inception distance, compares feature statistics of generated and real images, and lower is better. That is an empirical result. Monotonicity makes an objective interpretable. It does not guarantee better samples.
Core
Weightings in closed form
Write weighting(lam, kind). It returns the weighting w(λ) of a training loss in closed form, the per-point factor c times the density of λ that training draws. Use this section's convention, in which the loss is ∫w(λ)E∥ϵ−ϵ^∥2dλ with no factor 21 in front. The argument lam is an array, and the output has its shape. Support six values of kind.
"elbo", the continuous-time bound.
"eps_cosine" and "v_cosine", noise and velocity prediction on the cosine path with uniform t.
"fm_uniform" and "fm_logit_normal", flow matching on the linear path with uniform t and with logitt∼N(0,1).
"edm", with lnσ∼N(−1.2,1.22) and σdata=0.5.
The tests compare your formulas with c(t)p(t)∣dt/dλ∣ computed by finite differences, as the cell above does.
import numpy as np
def weighting(lam, kind):
return np.ones_like(np.asarray(lam, float)) # placeholder: weight 1 per unit of log-SNR
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Measuring samples when the truth is known
A 2D lab has a luxury that image models never have, the true distribution. We can ask directly whether samples match it, with distances between distributions and with diagnostics that exist only because we know where the modes are. Image models are measured in the evaluation of image generators, with FID and with precision and recall for generative models. Those metrics are the same ideas applied to features of images.
Maximum mean discrepancy
The idea fits in one sentence. If P and Q are the same distribution, points drawn from P look as similar to each other, on average, as they look to points drawn from Q, and the maximum mean discrepancy (MMD) measures how far that fails. "Similar" is measured by a kernel, a similarity score that is 1 for identical points and falls toward 0 as points move apart. It is the same discrepancy that the Wasserstein autoencoder of §32.4 uses to pull a cloud of codes toward its prior, and that section defines it and proves its estimate unbiased. Here is the short version, starting from two single points.
In general, take the Gaussian kernel k(x,y)=exp(−∥x−y∥2/(2ℓ2)) with length scale ℓ>0. The squared discrepancy is
MMD2(P,Q)=Ek(x,x′)+Ek(y,y′)−2Ek(x,y),
with x,x′∼P and y,y′∼Q, all independent. For the Gaussian kernel it is zero if and only if P=Q, and its square root is a metric (Gretton et al. 2012), which is what the error split above needs. In plain words, blur both densities with a Gaussian of standard deviation ℓ/2, and measure the squared distance ∫(p~−q~)2 between the blurred densities p~ and q~. MMD2 equals that up to a constant factor, because two such blurs in a row make the kernel.
From samples x1,…,xn∼P and y1,…,ym∼Q we use the unbiased estimate of §32.4,
whose expectation is exactly MMD2(P,Q), because every pair it averages is a pair of independent draws. The diagonal terms k(xi,xi)=1 are left out because a point is not independent of itself. Keeping them adds a positive bias even when P=Q. Because the unbiased estimate averages to zero when P=Q, it is often negative then. That is correct, not a bug.
The real choice is the length scale ℓ. Much smaller than the spacing of the data, the kernel sees only near-duplicates. Much larger than the data, every distribution looks alike. In practice, pick ℓ near the size of the structure you care about, and report the score of exact data beside the model's.
Sliced Wasserstein distance
In one dimension, the cheapest way to move one sample onto another is to sort both and pair them in order.
So take two samples of the same size, sorted as x(1)≤⋯≤x(n) and y(1)≤⋯≤y(n). Their empirical 2-Wasserstein distance W2, the root mean squared distance of the cheapest pairing, satisfies
W22=n1i∑(x(i)−y(i))2.
§33.6 met W2 in higher dimensions, where finding the cheapest pairing is an assignment problem. The sliced version avoids that problem by looking at many one-dimensional shadows of the data.
It is a metric, it costs one sort per direction, and it is measured in the units of the data. Its weakness is statistical. Two finite samples of the same distribution sit at a positive sliced distance from each other. That floor falls slowly with sample size, especially for data with empty space between modes, where sorted samples disagree most.
Diagnostics that need the truth
Because we know the eight modes, three numbers answer specific questions about the samples.
Even exact samples do not score perfectly. The distance r of a sample of a 2D round Gaussian from its mean satisfies r2/s2∼χ22, which is exponential with mean 2. So an exact sample lands beyond ks with probability P(r>ks)=e−k2/2, which is 1.1 percent at k=3. Sampling noise alone also leaves a small mode-weight error. So every metric needs its exact-data value printed beside it.
A likelihood is the remaining tool. A flow or diffusion model has an exact log-likelihood through the probability flow ODE and the instantaneous change of variables (§33.6). Held-out negative log-likelihood punishes missing modes heavily, because a held-out point in a dropped mode gets almost zero density. It is costly to compute, and §33.2 showed that it is a poor guide to how samples look. The reverse quantity, the average true log-density of the samples, measures fidelity only. A model that puts every sample on the densest point maximizes it, so it rewards collapse.
The cells build the metrics and apply them to six samples whose defects we chose. Each has 600 points and is compared with 600 exact reference points.
Python · cell 2
Python · cell 3
Core
Two distances between samples
Write two functions.
mmd2_unbiased(X, Y, bandwidth) returns the unbiased estimate of MMD2 for X of shape (n, d) and Y of shape (m, d). Use the Gaussian kernel exp(−∥x−y∥2/(2ℓ2)) with ℓ = bandwidth.
sliced_w2(X, Y, n_proj, rng) returns the sliced 2-Wasserstein distance for two samples of the same size. Draw all directions with one call, rng.standard_normal((n_proj, d)), and normalize each row to unit length. Project both samples, sort each projection, and return the square root of the mean squared difference over all sorted positions and directions.
import numpy as np
def mmd2_unbiased(X, Y, bandwidth):
raise NotImplementedError
def sliced_w2(X, Y, n_proj, rng):
raise NotImplementedError
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Core
Fidelity and coverage for a known mixture
Write mode_metrics(samples, means, weights, std, k=3.0, min_frac=0.01) for a mixture of round Gaussians. The centers means have shape (K, d), the weights weights have shape (K,), and every component has standard deviation std. Return (off_rate, covered, weight_error), defined as follows.
A sample is on the manifold if its nearest center is closer than k * std. off_rate is the share of samples that are not.
Each on-manifold sample is assigned to its nearest center only. covered counts the centers that receive at least min_frac of all samples.
weight_error is the total variation distance between weights and the assigned shares, where each share is a count divided by the number of on-manifold samples.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
The sampler alone, measured with an exact denoiser
Now we measure the first term of the error split, the sampler's own error. One change of coordinates makes it easy to reason about. Divide the noisy point by its signal scale,
y=αtxt=x0+σϵ,σ=αtσt=e−λt/2.
In these coordinates every Gaussian path looks the same, clean data plus noise of standard deviation σ. For example, at λ=0 every path has σ=1, and at λ=−10 every path has σ=e5=148. Only the speed at which σ changes with t differs from path to path. §33.4 used these coordinates to show that the samplers of all paths become one sampler.
∂ Proof in the coordinates y
In y coordinates the noisy point is y=x0+σϵ, whose joint law with x0 depends only on σ. So the exact denoiser is one function D(y;σ)=E[x0∣y] for every path, and the exact noise estimate is ϵ^=(y−D)/σ. DDIM's update xs=αsx^0+σsϵ^, divided by αs, reads y(i+1)=D(y(i);σ(i))+σ(i+1)ϵ^. That involves only y(i), σ(i), and σ(i+1). Both runs apply the same map at every step from the same start. ∎
In plain words, with a perfect network, "the DDIM sampler of a DDPM model" and "the Euler sampler of a flow-matching model" are the same algorithm. Both are Euler's method on dy/dσ=ϵ^ (§33.6 proves the flow-matching side). The only thing a method's name can change is where the steps go.
And the steps do go to different places. Uniform steps in t visit different noise levels on different paths. With N steps, the last nonzero level is tan(2π⋅Ntmax)≈1.57/N on the cosine path. On the linear path it is 1−tmax/Ntmax/N≈1/N. So the linear grid takes its final, most delicate step from a smaller noise level.
The stochastic sampler has the same property. DDPM's ancestral step, which adds fresh noise at every step, adds noise with variance σ(i+1)2(1−σ(i+1)2/σ(i)2) in y units. That is the posterior variance β~ of §33.2 for a jump between two levels, divided by the new level's α2, and it too depends only on the levels.
Python · cell 4
Each entry is the off-manifold rate followed by the mode-weight error. With a perfect network the sampler is the whole story, and at small budgets it is a large one.
At 4 calls, 43 to 56 percent of all samples are off the ring.
At 8 calls the linear grid puts 4.7 percent off, against 10.2 percent for the cosine grid. The torch bench below, with other random draws, finds 5.2 and 10.6.
From 16 calls on, both are close to the floor of exact data, 1.1 percent off and a mode-weight error of 0.02 from sampling noise alone.
The mode weights are right at every budget. The coarse structure is decided early, and the oracle decides it correctly. The stochastic sampler is no better at 4 or 8 calls, since its fresh noise has too few steps to be corrected. At 64 calls on the cosine grid it even comes in below the floor (0.000). That is a sign that its modes are too narrow, the under-dispersion of β~ that §33.2 measured. A score better than exact data is a defect too.
The bench with trained networks
Now the second term, the network's error. The torch block trains one small network three ways on the ring, with the same architecture, optimizer, batch size, number of steps, and seed.
The noise predictor uses the cosine variance-preserving path.
The velocity predictor uses the same path.
The flow-matching model uses the linear path.
Each draws t∼U(0,1), as the original recipes do. The block then samples every model, and the oracle, with deterministic DDIM written in y coordinates. It does so on both grids and at 4, 8, 16, and 64 calls, so every factor can be changed while the others are held fixed. Each model's output is turned into a noise estimate with the conversions above, ϵ^=σtxt+αtv^ on the variance-preserving path and ϵ^=xt+(1−t)u^ on the linear path.
🔥 In PyTorchfor a machine with PyTorch
Each cell of the printed table holds the off-manifold rate, the mode-weight error, and MMD with ℓ=0.1. One seed is an anecdote, so we ran the block with five seeds, 0 to 4, and report each mean with a 95 percent interval.
Why 2.776 and not 1.96? The confidence intervals of §0.11 use the normal quantile 1.96, which treats the standard error as known. With five seeds it is estimated from five numbers, which adds uncertainty. For normally distributed results, the standardized mean (xˉ−μ)/(s/5) then follows Student's t distribution with 5−1=4 degrees of freedom, whose 0.975 quantile is 2.776. An interval built with 1.96 would be about 30 percent too narrow.
Read the table with the error split in mind.
At 4 calls the sampler dominates. The oracle itself puts 40 percent of its samples off the ring, and the flow-matching model matches it. A network's errors can even cancel part of the sampler's. The noise predictor puts only 32 percent off the ring at 4 calls, fewer than the oracle, while its mode weights are the worst in the table. One metric improved because two errors partly cancelled. That is why a bench never reports one metric.
From 16 calls on, the network dominates. The oracle reaches the floor. The flow-matching model stays at 10.2 percent off the ring at 64 calls, and the noise predictor at 14.2 percent. More steps cannot remove network error, so the curves flatten at different heights.
The noise predictor gets the mode weights wrong. Its mode-weight error is 0.178 at 8 calls, against 0.033 for flow matching. The error split locates the cause. The first DDIM step starts at λ=−10, where αt=0.0067. It turns the noise estimate into a data estimate with x^0=(xt−σtϵ^)/αt, which multiplies the network's error by σt/αt≈150. That first estimate decides which mode each sample heads for. In our seed-0 run on the cosine grid, replacing only that first of 8 calls by the oracle lowered the mode-weight error from 0.225 to 0.031. This is the failure at low signal-to-noise that §33.1 predicted. Velocity and data prediction were designed to avoid it (§33.4).
The velocity predictor gets the mode weights right and the modes wrong. Its samples are blurred, 46.9 percent off the ring even at 64 calls. The error sits at low noise. At 8 calls on the cosine grid, replacing only its last call by the oracle (the step from σ=0.20 to 0) brought it from 61 to 13 percent off the ring, close to the oracle's 10. Its weighting is the same as flow matching's up to a constant, so the objective cannot explain the gap. The box below has the measurements and what they do and do not say.
So is flow matching better? Comparing the two rows of intervals throws information away. Every seed trains all three models, samples them from the same starting noise, and scores them against the same reference sample. So the right statistic is the per-seed difference, the paired comparison of §0.11. The variance of a difference is the sum of the two variances minus twice their covariance, so whatever a seed does to both models cancels. The five differences are independent across seeds, which together with rough normality is all the t interval needs.
At 8 and 16 calls both paired intervals exclude 0. So on this bench, at those budgets, flow matching beats noise prediction in fidelity and in mode weights. At 64 calls both intervals include 0, and five seeds cannot separate the two. At 4 calls the mode-weight gap is significant and the off-manifold gap is not, because of the cancellation described above.
The error split also says why. It is not the grid, since both models are sampled on the same grid, and the oracle on that grid is at 5 percent at 8 calls, far below both. It is network error, and for the noise predictor much of it enters through a single call, where the parameterization amplifies it. Neither reason is "straight paths" in the sense of §33.6. When noise and data are paired independently, all three models aim at the same probability flow, and its paths curve, whatever path each training pair follows. That is what a careful answer to the colleague sounds like.
The table below collects the design choices the bench separated, and which of them can still be changed after training. The last row is the exponential moving average (EMA) of the weights, which most models use for sampling instead of the raw weights.
choice
what it controls
cheap to change after training
what to watch
target (ϵ, x0, v, u)
how network error is amplified at each noise level
no
ϵ blows up near zero signal, x0 near zero noise
weighting w(λ)
which noise levels get the capacity
no
the flat bound puts relatively more weight on the cleanest levels, uniform-t losses follow the schedule
training density p(λ)
gradient variance at fixed w
no
rare levels are learned slowly
time grid
discretization error
yes
the last step, and the first one for ϵ models
sampler order and stochasticity
error per call
yes
count calls, not steps
averaging horizon of the EMA
noise in the weights
yes, with post-hoc averaging
too short is noisy, too long remembers early weights
The widget holds every run of the bench. Pick a metric, a time grid, and the mean over seeds or one seed, and read each method's curve against the network calls. Click a column to choose the number of calls, and a method to see its samples below the plot.
Which method wins, and does the answer survive changing the network calls, the time grid, or the metric?
One small network trained three ways, five seeds each, sampled by DDIM. The gray oracle has a perfect network.
Loading interactive figure…
A perfect model would copy its training set
A good-looking sample can still be a bad sample, if it is a copy of a training image. The optimum of the training loss shows that this is a real risk. Train a denoiser perfectly on a finite training set, and it learns to reproduce exactly that set.
∂ Proof from the conditional mean
The minimizer of a squared error is the conditional mean (§33.2). Under p^, the posterior probability of training point j given xt is proportional to Ntrain1N(xt;αtx(j),σt2I). That normalizes to the softmax above, and the conditional mean is the posterior-weighted average of the points. The probability flow ODE built from the exact score of the noised p^ has marginal p^t at every time (§33.3), and at t=0 that marginal is p^ itself. ∎
In plain words, the loss is minimized by a denoiser that, at low noise, snaps every point to its nearest training example. Real models generalize because they cannot fit that function. A network smooth enough to interpolate between training points learns something closer to the score of the underlying distribution. Kadkhodaie et al. (2024) trained two denoisers on non-overlapping subsets of a large image dataset and found that they learn nearly the same function, so their samples are new images. On small training sets the same networks memorize.
How do we detect a copy? A sample's nearest training point is not enough, since in dense regions every sample is close to something. Carlini et al. (2023) compared a generated image's nearest distance with its distances to the next-nearest training images. We use the simplest version of that idea.
The test is relative, so it adapts to how dense the training set is near the sample. It also has a clean baseline. Draw a sample from the data's own density, and condition on d2. If the density is roughly uniform nearby, the nearest training point is uniform in the ball of radius d2. So P(d1<τd2)=τd, which is 1/9=0.111 in two dimensions. A model whose flagged rate is well above τd is copying. Run the test against a deduplicated training set. A point stored twice is its own second neighbor, so d1=d2, and a copy of it would never be flagged.
The cell samples twice with the same sampler. Once it uses the exact optimum for a training set of 64 points, and once the exact denoiser of the true ring. It uses the step spacing of Karras et al. (2022), which crowds the steps toward low noise and ends at the noise level 0.002.
Python · cell 5
The sampler with the exact empirical optimum flags 0.998 of its samples, and their median distance to the nearest training point is zero to machine precision. The true-ring oracle and fresh data both sit at about 0.10, close to the baseline of 0.111.
The last noise level of the grid matters here, more than the number of steps. EDM's grid ends at 0.002, far below the spacing between training points, so the final step snaps each sample exactly onto one of them. Eight uniform steps on the linear path end at 0.14 instead. The last step then averages several neighboring training points, and only 0.13 of the perfect copier's samples are flagged, close to the baseline. That is one reason memorization is easy to miss.
In practice, run a nearest-neighbor check against the training set before reporting any sample metric, and deduplicate the training data. An interviewer will ask why a perfect loss would be a disaster here. The theorem is the answer.
Core
A copy detector with a known baseline
Write memorization_ratio(samples, train, tau=1/3). It returns the share of samples whose nearest training point is closer than tau times their second-nearest, with Euclidean distances. The tests check exact copies, the baseline τd for fresh samples, and an exact value on a mixed set.
import numpy as np
def memorization_ratio(samples, train, tau=1/3):
raise NotImplementedError
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Debugging a diffusion model
A diffusion model whose loss goes down can still sample garbage. The loss is an average over noise levels, which can hide a level the network never learned. A sampler chains a hundred small errors into one large one. The list below gives each check and the failure it catches.
On item 7, §33.2 set the averaging horizon 1/(1−m) by hand, where m is the EMA's decay. Karras et al. (2024) made it a choice after training. They store a few snapshots of averages during one run and reconstruct the average for any horizon afterwards. That let them tune the horizon precisely, and it showed that the best horizon interacts with architecture, training length, and guidance. Their redesigned network also keeps the magnitudes of activations and weights under control throughout training. Together these changes improved the record FID on 512×512 ImageNet from 2.41 to 1.81.
Interview
Fix a broken sampler
The setup defines three things.
A DDPM schedule with T=200. The arrays are indexed by t=0,…,T, and index 0 is the clean data.
The exact noise predictor ring_eps(x, t) of the ring.
buggy_ddpm_sample(eps_model, n, rng, beta, alpha, sigma), an ancestral sampler with three planted bugs.
Write fixed_ddpm_sample with the same signature. It should run the ancestral sampler of §33.2, Algorithm 2 of Ho et al. (2020), with noise variance νt2=βt. Each step is xt−1=1−βt1(xt−σtβtϵθ(xt,t))+νtz, with z standard normal and no noise at t=1. Keep the order of random draws. First draw xT = rng.standard_normal((n, 2)), then one rng.standard_normal((n, 2)) per step t>1, after the mean. The tests compare your samples with the correct ones draw for draw, and tell you which bugs remain.
import numpy as np
def fixed_ddpm_sample(eps_model, n, rng, beta, alpha, sigma):
return buggy_ddpm_sample(eps_model, n, rng, beta, alpha, sigma) # find and fix the three bugs
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Masked diffusion brings the idea to text
Text is a sequence of tokens, and a token has no "slightly noisy" version. Adding a Gaussian to a token ID is meaningless. The discrete replacement for adding noise is hiding tokens. Mask a random fraction of the tokens, train a network to fill them in, and generate by starting from an all-mask sequence and filling in a few tokens per step.
Formally, discrete diffusion replaces the Gaussian noise with a random process on the vocabulary. Austin et al. (2021) studied several, including replacement by a random token and moves to nearby tokens in embedding space. One stood out, the absorbing process, in which a token can only turn into a special mask token m and then stays masked. Its forward process is the masking used to train BERT, the encoder of §7.1, which learns to fill in tokens hidden at a fixed rate of 15 percent (§8.1 sets this masked language modeling objective beside next-token prediction). Masked diffusion runs that masking at every rate from 0 to 100 percent.
Here is a small example first. Take the four-token sentence "the cat sat down" at a time where each token survives with probability κt=0.75. On average one token is hidden, giving, say, "the cat m down". At a later time with κt=0.25, on average three are hidden, as in "m cat mm".
Papers on masked diffusion write αt for κt. We rename it because αt is the signal scale in this chapter.
The reverse step needs one fact. Fix s<t and a position that is masked at time t. Given x0i, the probability that it was still clean at time s is
P(masked at t)P(clean at s, masked at t)=1−κtκs(1−κt/κs)=1−κtκs−κt,
and if it was clean, it equals x0i. For example, with κs=0.8 and κt=0.5, a token masked at t was clean at s with probability 0.3/0.5=0.6. So
and an unmasked position at time t is unchanged at time s. The model's reverse step is the same formula with x0i replaced by the denoiser's guess. It gives probability 1−κtκs−κtpθ(xi=v∣xt) to each real token v and 1−κt1−κs to m. Sahoo et al. (2024) call this choice, zero mass on the mask and visible tokens carried over, the SUBS parameterization.
With that reverse step, the variational bound of §33.2 becomes a weighted fill-in-the-blank loss.
∂ The masked diffusion bound, term by term
The bound of §33.2 holds for any Markov chain, with sums in place of integrals. Its denoising terms are EqDKL(q(xs∣xt,x0)∥pθ(xs∣xt)). Both distributions factor over positions, so the KL is a sum over positions.
Unmasked positions. Both distributions put all their mass on xti. The KL is 0.
Masked positions. Both put mass 1−κt1−κs on m, which contributes 1−κt1−κslog1=0. On the real tokens, q puts all of the remaining mass 1−κtκs−κt on x0i, and pθ puts 1−κtκs−κtpθ(x0i∣xt) there, so
DKL=1−κtκs−κtlogpθ(x0i∣xt)1.
Summing over a grid. Take T steps tk=k/T with s=tk−1/T. The denoising part of the bound is
By Taylor's theorem, κt−1/T−κt=−κt′/T+O(1/T2), so as T→∞ the sum becomes a Riemann integral over t∈[0,1].
The other terms. At t=1 every token is masked, so with the prior p(x1)=(m,…,m) the prior term is DKL=0. At t=0 nothing is masked, and in the continuous limit the reconstruction term vanishes.
The schedule drops out. With γ=log(1−κt), dγ=−κt′/(1−κt)dt, and γ runs from −∞ to 0 for every schedule. The masking probability 1−κt=eγ is then a function of γ alone, so the integral is the same for every schedule. That is the discrete analog of the endpoint invariance of §33.2.
In plain words, pick a masking rate t at random, mask that fraction of the tokens, ask the network to fill in the masked ones, and weight the cross-entropy by 1/t. It is BERT's loss with two changes, a random rate instead of 15 percent, and the 1/t factor. The factor is what makes it a likelihood bound. At rate t about nt tokens are masked, so t1∑masked is, on average, the per-token loss times n, and every rate contributes on the same scale.
With the exact denoiser the bound is tight, and that gives masked diffusion a second reading. It is the same as generating the tokens one at a time in a random order.
∂ Proof by counting masks and orders
Fix a set M of masked positions with ∣M∣=k. Rate t masks exactly M with probability tk(1−t)n−k. So M enters the bound with weight ∫01t1tk(1−t)n−kdt=n!(k−1)!(n−k)!=k(kn)1, a Beta integral, multiplying ∑i∈M−logpθ(x0i∣xMˉ), where xMˉ is the sequence with M masked. In a uniformly random order, the set of positions still ungenerated when k remain is uniform over the (kn) subsets, and the next position is uniform within it. So the pair (set M, next position i∈M) has probability (kn)1⋅k1. Both expressions put the same weight on the same terms. ∎
The link to orders was made by Hoogeboom et al. (2022) and, for the absorbing process, by Ou et al. (2025). It also suggests why masked diffusion behaves differently from left-to-right models when data are scarce, the setting of §9.4. Prabhudesai et al. (2025) found that masked diffusion models extract far more from repeated data than autoregressive models. The theorem makes this plausible. Each pass over a sequence trains on a new random order, a form of augmentation that left-to-right training never gets.
Generating in parallel, and what it costs
To sample, start from n masks and run the reverse chain. At each step the network predicts every masked position at once, and the step reveals some of them, each drawn from its own predicted distribution. If a step reveals one token, the chain is an any-order autoregressive model and samples exactly from pθ. If it reveals k>1 tokens, it draws them independently given the current context. It then samples from the product of their marginals, not from their joint distribution. With few steps the model can produce combinations that no training sequence contains.
A two-token example shows the problem. Suppose the only valid pairs are "New York" and "Los Angeles", each with probability one half. Each position on its own is a fair coin between its two words. Revealing both at once draws two independent coins, so half the time it writes "New Angeles" or "Los York".
The cells make that concrete with a grammar of 18 five-token sentences of the form "the ADJ ANIMAL VERB PLACE". The verb depends on the animal, and the place depends on the verb. The denoiser is exact, the posterior over the 18 sentences given the visible tokens, so every error is the sampler's. The first cell checks the theorem. The second generates with 1 to 5 network calls, revealing positions in a random order or most confident first.
Python · cell 6
Python · cell 7
The bound equals −logp(x0) to four decimals, as the theorem says for an exact denoiser. Any excess from the 10−6 smoothing is below the fourth decimal. Generation shows the price of parallelism.
With one call, all five tokens are drawn from their marginals at once, so the order is irrelevant. The valid rate is then the product-of-marginals value 3/32=0.094. The cell's 0.092 and 0.094 are two Monte Carlo estimates of it, each from 20,000 sentences.
In random order each extra call helps, and five calls, one token each, are always valid.
Revealing the most confident positions first is much worse at two calls, 0.094 against 0.631.
The confident order fails because "the" and the adjective are the most confident tokens and also the ones that say nothing about the rest. The first call is spent on them. The three dependent tokens are then drawn together in the second call, each from its own marginal, exactly as one call does. Four calls differ from three only in revealing the adjective on its own instead of beside the place, so they are no better either. Confidence is not independence. What a step can safely reveal together are tokens that are nearly independent given what is already visible.
The widget runs the same grammar. Choose the number of network calls and the order, press Sample, and watch which positions each call reveals and which sentences come out invalid. The large number is the exact valid rate, summed over every order and every draw, so it differs slightly from the cell's estimates.
How does a masked diffusion model write a sentence without going left to right?
An exact denoiser for 18 sentences. Each call predicts every mask, then reveals some, each drawn on its own.
Loading interactive figure…
Interview
The masked diffusion training loss
Write masked_diffusion_loss(logits_fn, x, mask_id, rng) for a batch x of token IDs of shape (B, n), with the schedule κt=1−t, so each token is masked with probability t.
Draw t = rng.uniform(size=B) first, then u = rng.uniform(size=(B, n)).
Mask position i of sequence b where u[b, i] < t[b], by writing mask_id there.
Call logits_fn(xt) once. It returns logits of shape (B, n, V) over the real tokens.
Return the batch mean of t1∑masked i−logpθ(x0i∣xt), using a stable log-softmax.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Revealing only near-certain tokens
The grammar showed that confidence is not independence. But confidence near 1 does force near independence. A practical rule follows. At each step, reveal every masked position whose top probability exceeds a threshold, instead of a fixed number of positions. Wu et al. (2025) use this rule in a system called Fast-dLLM, and the theorem below is why it is safe.
∂ Proof by the union bound
Write z⋆=(z1⋆,…,zk⋆). By the union bound, p(z=z⋆)≤∑jpj(zj=zj⋆)≤kε. Since q has the same marginals, the same bound holds for q. So both put mass at least 1−kε on z⋆. For any set A of outcomes, if z⋆∈A both p(A) and q(A) lie in [1−kε,1], and otherwise both lie in [0,kε]. So ∣p(A)−q(A)∣≤kε, and the total variation distance is the largest such gap. ∎
In the grammar, "the" has probability 1 and could join any call for free, while the animal, at 0.25, could not. A threshold of 1−ε with kε small keeps the parallel step close to the joint. When no position is confident enough, the sampler falls back to one token per call. The Fast-dLLM paper also proves a sharper statement for greedy decoding, which picks the most likely tuple. The product's most likely tuple equals the joint's when (k+1)ε≤1.
Diffusion language models at scale
LLaDA (Nie et al. 2025) scaled the recipe. It is a Transformer with no causal mask and 8B parameters. It was pre-trained from scratch on 2.3 trillion tokens with exactly the loss of the theorem, at a cost of 0.13 million H800 GPU-hours. It was then fine-tuned on 4.5 million instruction pairs by masking only the response. It was competitive with LLaMA 3 8B on many benchmarks and better at a reversal task that left-to-right models find hard.
For sampling, LLaDA predicts all masked tokens at each step and then masks again the least confident fraction of its predictions, which it calls low-confidence remasking. It also supports semi-autoregressive blocks, generated left to right with diffusion inside each block. Its headline results use as many steps as generated tokens, one token per network call. That is the regime where the parallel-decoding error above vanishes.
Commercial systems trade some quality for parallelism. An independent benchmark measured Inception's Mercury Coder Mini and Small at 1,109 and 737 tokens per second on an H100 (Inception Labs 2025). Gemini Diffusion is an experimental Google DeepMind model. Its page reports 1,479 tokens per second of sampling. It scores slightly above Gemini 2.0 Flash-Lite on the LiveCodeBench v6 coding benchmark (30.9 against 28.5 percent), but well behind it on the GPQA Diamond science questions (40.4 against 56.5). ByteDance's Seed Diffusion Preview reports 2,146 tokens per second on H20 GPUs (Song et al. 2025). Each speed was measured on its own hardware and serving setup, so the numbers do not compare directly. Dream 7B (Ye et al. 2025) is an open model that starts from an autoregressive model's weights. That suggests the two kinds of model are closer than their samplers make them look.
The limits are real too.
Fixed length. A diffusion LM generates a fixed-length canvas, so the length must be chosen or padded. LLaDA's instruction model pads with end-of-sequence tokens, which hurt its low-confidence sampler on some tasks.
No exact cache. Attention is bidirectional, so the keys and values of every position change as tokens are revealed. The exact KV cache of §6.5, which stores past keys and values so each new token costs one position, does not apply to a plain masked diffusion LM. A plain sampler recomputes the whole sequence at every step. Fewer calls can still be slower if each call costs more.
A strong rival. Autoregressive models have their own route to parallel decoding, speculative decoding, in which a cheap draft model proposes several tokens and the large model checks them in one pass, keeping its exact distribution.
Work since 2025 has narrowed both gaps. Block diffusion (Arriola et al. 2025), called BD3-LM, splits the sequence into fixed-size blocks. It generates the blocks left to right and runs masked diffusion inside each block, with attention causal across blocks. A finished block never changes, and later blocks cannot alter its keys and values. So it is cached exactly, as in an autoregressive model, and generation can run to any length. Block size 1 is an autoregressive model, and one block covering the whole sequence is the masked diffusion of this section. The block size interpolates between the two.
The state of the art in October 2026
The map changes monthly, so read it by kind of claim. Architectures and objectives of open models come from their papers and are reliable. Benchmark records are reliable for the benchmark and the date. Speeds and comparisons from vendors' pages are claims about their own setups. One thing is constant across all of them, and it is the subject of this chapter. Every one of these systems is trained to remove noise or follow a velocity at many noise levels, and every one is sampled by integrating what it learned.
Where it breaks
Comparisons that fix the wrong thing. Equal steps instead of equal network calls, different time grids, one seed, or one metric. Each one changed a conclusion on our bench.
Metrics with blind spots. Sliced distances have a high floor and miss wrong widths. MMD's verdict depends on its length scale, and it misses small off-manifold mass. Coverage counts miss wrong widths. Report several, with the score of exact data beside each.
Toy results that do not transfer. A 2D ring is a test of algorithms, not of architectures. The velocity predictor's trouble on our bench says nothing about large models. The bench's value is that it can say why a method failed, not which method wins at scale.
Memorization mistaken for quality. A model that copies its training set scores well on every distribution metric computed against the training data. Check nearest neighbors against the baseline of fresh data.
Discrete diffusion's sampler. Parallel unmasking samples products of marginals. Quality falls when too many dependent tokens are revealed in one step, unless only near-certain tokens are revealed together. The bidirectional network also rules out the exact KV cache, unless generation is split into causally attending blocks.
Carlini, Hayes, Nasr, Jagielski, Sehwag, Tramèr, Balle, Ippolito & Wallace (2023). Extracting Training Data from Diffusion Models. How memorization was measured and exploited at scale, and what made models memorize.
Sahoo, Arriola, Schiff, Gokaslan, Marroquin, Chiu, Rush & Kuleshov (2024). Simple and Effective Masked Diffusion Language Models. The masked diffusion bound as weighted masked-LM losses, the SUBS parameterization, and the engineering that closed most of the gap to autoregressive perplexity.
Shi, Han, Wang, Doucet & Titsias (2024). Simplified and Generalized Masked Diffusion for Discrete Data. MD4, the same continuous-time bound found independently as a weighted integral of cross-entropies, with masking schedules that depend on the token.
Nie, Zhu, You, Zhang, Ou, Hu, Zhou, Lin, Wen & Li (2025). Large Language Diffusion Models. LLaDA, masked diffusion at 8B parameters, with its training and remasking algorithms written out.
Gretton, Borgwardt, Rasch, Schölkopf & Smola (2012). A Kernel Two-Sample Test. The theory of MMD, its unbiased estimator, and when it is a metric.
Watch next
🎬 Simple Diffusion Language Models · Sasha RushSasha Rush, an MDLM co-author, walks through masked diffusion language models and their weighted masked-LM loss, the last part of this section.🎬 Diffusion Language Models: The Next Big Shift in GenAI · Jia-Bin HuangA visual explainer of diffusion language models, useful next to the masked diffusion derivation and the parallel-decoding trade-off.🎬 [EEML'24] Sander Dieleman - Generative modelling through iterative refinement · EEML CommunityA summer-school lecture by a Google DeepMind diffusion researcher on diffusion as iterative refinement, good background for the noise-level and objective comparisons here.