One DDPM Ancestral Sampling Step
Problem Statement
Take one step of the DDPM reverse chain: given and the network's predicted noise, produce .
Background
Substituting into the posterior mean and simplifying gives the form Algorithm 2 of DDPM actually uses:
The mean subtracts a small fraction of the predicted noise -- , not all of it -- and then rescales. Removing all the noise at once would be the DDIM jump straight to , not an ancestral step.
For the variance DDPM reports two choices that work about equally well:
- "posterior": (optimal for )
- "beta": (optimal for deterministic )
At no noise is added at all -- the final step must output a clean sample, and is zero anyway.
Your Task
Implement:
def ddpm_step(x_t, eps, t, betas, z, variance_type="posterior"):
Derive alphas and alpha_bars from betas inside the function. Return as an array shaped like x_t. Raise ValueError for an unknown variance_type.
Input Format
- x_t, eps, z: NumPy arrays of matching shape (z is the pre-drawn standard normal sample).
- t (int): 0-based timestep index.
- betas: 1-D NumPy array.
- variance_type (str): "posterior" or "beta".
Output Format
A NumPy array shaped like x_t.
Sample
betas = np.array([0.1, 0.2, 0.3])
x_t = np.array([0.5, -0.5])
eps = np.array([0.2, 0.1])
z = np.array([1.0, -1.0])
print(np.round(ddpm_step(x_t, eps, 1, betas, z), 4).tolist())
alpha_1 = 0.8, alpha_bar_1 = 0.72, so the mean is (x_t - 0.2/sqrt(0.28) * eps) / sqrt(0.8), and *sigma = sqrt((0.1/0.28)0.2) is added times z.
Example
betas = np.array([0.1, 0.2, 0.3]) x_t = np.array([0.5, -0.5]) eps = np.array([0.2, 0.1]) z = np.array([1.0, -1.0]) print(np.round(ddpm_step(x_t, eps, 1, betas, z), 4).tolist())
[0.7418, -0.8685]
- Inputs: is the noisy sample at step , is the network's noise guess, is the fresh Gaussian draw, and the schedule is .
- Schedule at : , , so and , giving .
- Mean: the noise coefficient is . The bracket is ; dividing by gives a mean of .
- Noise: with the posterior variance, , so . Since , add .
- Output:
[0.7418, -0.8685], same shape as . - Notice only about is removed; most of the change comes from the rescale and the noise. At the result would be just the mean.
Constraints
0 <= t < len(betas).- The noise coefficient inside the bracket is , and the whole bracket is divided by (note: , not ).
- When
t == 0, return the mean with no noise term. - Use in .
- An unrecognised
variance_typemust raiseValueError. - Do not round inside the function.