Compute the exact reverse-process posterior q(xt−1∣xt,x0) -- the distribution the denoising network is trained to imitate.
The reverse of the forward chain is intractable in general, but if you are also told x0 it is available in closed form and it is Gaussian:
q(xt−1∣xt,x0)=N(xt−1; μ~t(xt,x0), β~tI)
μ~t(xt,x0)=1−αˉtαˉt−1βtx0+1−αˉtαt(1−αˉt−1)xt
β~t=1−αˉt1−αˉt−1βt
with the convention αˉ−1=1 for the very first step.
Both coefficients are positive, so the mean sits between x0 and xt: early in the reverse trajectory (large t) almost all the weight is on xt and the posterior barely moves, while near t=0 it swings onto x0. (They sum to slightly less than 1, not exactly 1 -- the forward process shrinks the signal a little at every step.) At t=0 the convention αˉ−1=1 makes the coefficients exactly (1,0) and β~0=0: knowing x0 pins x−1=x0 with no uncertainty left. Handling that boundary is the part implementations get wrong.
Implement:
def posterior(x0, x_t, t, betas):
Derive alphas and alpha_bars from betas inside the function. Return (mean, var) where mean is an array shaped like x0 and var is a Python float.
A tuple (mean, var): a NumPy array shaped like x0, and a float.
betas = np.array([0.1, 0.2, 0.3])
x0 = np.array([1.0, -1.0])
x_t = np.array([0.4, 0.2])
m, v = posterior(x0, x_t, 1, betas)
print(np.round(m, 4).tolist())
print(round(v, 4))
Here alpha_bar_1 = 0.72, alpha_bar_0 = 0.9, so the coefficients are *sqrt(0.9)0.2/0.28 on x0 and *sqrt(0.8)0.1/0.28 on x_t, and the variance is *(0.1/0.28)0.2.
betas = np.array([0.1, 0.2, 0.3]) x0 = np.array([1.0, -1.0]) x_t = np.array([0.4, 0.2]) m, v = posterior(x0, x_t, 1, betas) print(np.round(m, 4).tolist()) print(round(v, 4))
[0.8054, -0.6137] 0.0714
alphas = [0.9, 0.8, 0.7], so alpha_bar_0 = 0.9 and alpha_bar_1 = 0.72, giving 1 - alpha_bar_1 = 0.28. The x0 coefficient is sqrt(0.9)*0.2/0.28 = 0.6776 and the x_t coefficient is sqrt(0.8)*0.1/0.28 = 0.3194, so the mean is 0.6776*1.0 + 0.3194*0.4 = 0.8054 and 0.6776*(-1.0) + 0.3194*0.2 = -0.6137. The variance is (0.1/0.28)*0.2 = 0.0714.
0 <= t < len(betas).t == 0; the mean is then exactly x0 and the variance exactly 0.0.var as a plain Python float.