Lab 12: Noise a Point, Then Take One Reverse Step
A 2-D schedule, a known score, one denoising update.
Work in a Jupyter notebook with NumPy (PyTorch is optional). Do notes 12.1–12.2 first. No U-Net and no pretrained weights.
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(0)
x0 = np.array([1.5, -0.8]) # one 2-D data point
T = 40
beta = np.linspace(1e-4, 0.20, T + 1)
beta[0] = 0.0
alpha = 1.0 - beta
alphabar = np.cumprod(alpha)
def q_sample(x0, t, eps):
a = alphabar[t]
return np.sqrt(a) * x0 + np.sqrt(1.0 - a) * eps
The schedule is constructed. You will add noise, then take one reverse step with the true posterior mean (you know ).
1. Forward path
-
Print
alphabar[1],alphabar[T//2],alphabar[T]. They should fall from near 1 toward 0. -
Sample a chain: start at
x0, fort = 1 … Tdraweps ~ N(0, I)and setxt = q_sample(x0, t, eps). Store everyxt. (Independent draws are enough; you do not need the Markov simulation.) -
Scatter the path in 2-D, color by , and mark . At large the points should look like isotropic noise.
2. True posterior mean for one step
The Gaussian has mean
def posterior_mean(x0, xt, t):
c0 = np.sqrt(alphabar[t - 1]) * beta[t] / (1.0 - alphabar[t])
c1 = np.sqrt(alpha[t]) * (1.0 - alphabar[t - 1]) / (1.0 - alphabar[t])
return c0 * x0 + c1 * xt
Pick a mid (try t = 20). Draw one eps, form xt, then xhat = posterior_mean(x0, xt, t).
-
Print and . The second should be smaller.
-
Repeat the draw 50 times at the same . Report mean distances before and after the step.
3. Score form (same geometry)
For this spherical forward kernel the noise is . Plug that known into the DDPM mean from note 12.2 (no extra ) and check it matches posterior_mean up to small numeric error at your .
Write four sentences: what does, why this lab’s reverse step is an oracle, what a trained would replace, and why a GAN does not need a -step chain to sample.