v9.1.1
Menu

SNR-Weighted Variational Bound Terms

hardFREESignal-to-Noise Ratio and Loss WeightingDiffusion ModelsDiffusion Models 1

Problem Statement

Evaluate the per-sample denoising terms Lt−1L_{t-1} of the diffusion variational lower bound, using the SNR form that makes the bound's structure obvious.

Background

The VLB of a diffusion model decomposes into one KL per timestep. For the denoising terms the KL is between two Gaussians that differ only in their means, and after substituting the DDPM posterior mean and variance it collapses to a remarkably clean statement (Kingma et al., Variational Diffusion Models):

Lt−1  =  12(SNR(t−1)−SNR(t)) ∥x0−x^0(xt,t)∥22L_{t-1} \;=\; \tfrac{1}{2}\left(\mathrm{SNR}(t-1) - \mathrm{SNR}(t)\right)\,\lVert x_0 - \hat{x}_0(x_t, t) \rVert_2^2

where SNR(t)=αˉt/(1−αˉt)\mathrm{SNR}(t) = \bar{\alpha}_t / (1-\bar{\alpha}_t) and x^0\hat{x}_0 is the model's reconstruction of the clean sample.

Two things to read off this formula:

  • The weight is the drop in SNR across the step. Since SNR is strictly decreasing in tt, the weight is always positive, and it is largest for the low-noise steps where the SNR curve is steepest.
  • The bound depends on the schedule only through its SNR values at the endpoints of each step. That is the formal reason the noise schedule is a design choice about weighting, not about the model.

The squared norm is a sum over all the data dimensions of one sample, not a mean.

Your Task

Implement:

def vlb_terms(x0, x0_pred, timesteps, alpha_bars):

Return a 1-D NumPy array of length N holding Lt−1L_{t-1} for each sample in the batch.

Input Format

  • x0: array of shape (N, D), the clean data.
  • x0_pred: array of shape (N, D), the model's reconstruction.
  • timesteps: integer array of shape (N,), one 0-based timestep index per sample.
  • alpha_bars: 1-D array of cumulative alphas, entries in (0, 1).

Every timestep must satisfy t >= 1 (the t=0t = 0 term is the separate reconstruction term L0L_0, which this function does not handle). If any entry is < 1, raise ValueError.

Output Format

A 1-D NumPy array of N floats.

Sample

ab = np.array([0.9, 0.5, 0.1])
x0 = np.array([[1.0, 0.0]])
x0_pred = np.array([[0.5, 0.0]])
print(np.round(vlb_terms(x0, x0_pred, np.array([1]), ab), 4).tolist())

SNR(0) = 9.0, SNR(1) = 1.0, the squared error is 0.25, so the term is 0.5 * (9.0 - 1.0) * 0.25 = 1.0.

Example

Input
ab = np.array([0.9, 0.5, 0.1])
x0 = np.array([[1.0, 0.0]])
x0_pred = np.array([[0.5, 0.0]])
print(np.round(vlb_terms(x0, x0_pred, np.array([1]), ab), 4).tolist())
Output
[1.0]
Reasoning
  • Inputs: one sample (N=1N = 1) with D=2D = 2 dimensions. The clean data is x0=(1.0,0.0)x_0 = (1.0, 0.0), the reconstruction is x^0=(0.5,0.0)\hat{x}_0 = (0.5, 0.0), the timestep is t=1t = 1, and the schedule is αˉ=(0.9,0.5,0.1)\bar{\alpha} = (0.9, 0.5, 0.1) with 0-based positions.
  • SNR at both ends of the step: t−1=0t-1 = 0 reads αˉ0=0.9\bar{\alpha}_0 = 0.9, so SNR(0)=0.9/0.1=9.0\mathrm{SNR}(0) = 0.9 / 0.1 = 9.0; t=1t = 1 reads αˉ1=0.5\bar{\alpha}_1 = 0.5, so SNR(1)=0.5/0.5=1.0\mathrm{SNR}(1) = 0.5 / 0.5 = 1.0. The weight is 12(9.0−1.0)=4.0\tfrac{1}{2}(9.0 - 1.0) = 4.0.
  • Reconstruction error: ∥x0−x^0∥22=(1.0−0.5)2+(0.0−0.0)2=0.25\lVert x_0 - \hat{x}_0 \rVert_2^2 = (1.0 - 0.5)^2 + (0.0 - 0.0)^2 = 0.25, summed over both dimensions.
  • Output: 4.0×0.25=1.04.0 \times 0.25 = 1.0, so the result is [1.0]: one non-negative value per sample in the batch.
  • Notice: averaging the error over DD would give 0.1250.125 and halve the answer. The same error at t=2t = 2 would only get weight 12(1.0−0.1111)≈0.4444\tfrac{1}{2}(1.0 - 0.1111) \approx 0.4444, because the SNR curve is much flatter at the noisy end. A timestep of 00 is not allowed and must raise ValueError.

Constraints

  • x0 and x0_pred both have shape (N, D); the output has shape (N,).
  • Use a sum of squared errors over the D axis, not a mean.
  • Any timestep < 1 must raise ValueError.
  • Handle a whole batch of different timesteps at once (fancy-index alpha_bars).
  • Do not round inside the function.

Learn this in

More from Diffusion Models 1

solution.py

Test results

Sign in to run your code against the test cases.