Build the linear noise schedule used by the original DDPM paper and derive the two quantities every later formula depends on: the per-step signal retention alpha_t and the cumulative signal retention alpha_bar_t.
The forward diffusion process corrupts data one small step at a time:
q(xt∣xt−1)=N(xt; 1−βtxt−1, βtI)
The variances β1,…,βT are the noise schedule. DDPM uses a linear schedule: T values spaced evenly (endpoints included) from beta_start to beta_end, with the defaults β1=10−4 and βT=0.02 for T=1000.
From the betas we define
αt=1−βt,αˉt=∏s=1tαs
αˉt is the running product, not a running sum. It is what makes the forward process collapse into a single closed-form Gaussian, so getting it right is the foundation of everything else in diffusion.
Implement:
def linear_schedule(T, beta_start=1e-4, beta_end=0.02):
Return a tuple (betas, alphas, alpha_bars) of three 1-D NumPy arrays, each of length T, using 0-based indexing (element i corresponds to timestep t=i+1 in the maths above).
A tuple of three NumPy arrays (betas, alphas, alpha_bars).
b, a, ab = linear_schedule(5, 0.1, 0.5)
print(np.round(b, 4).tolist())
print(np.round(a, 4).tolist())
print(np.round(ab, 4).tolist())
[0.1, 0.2, 0.3, 0.4, 0.5]
[0.9, 0.8, 0.7, 0.6, 0.5]
[0.9, 0.72, 0.504, 0.3024, 0.1512]
Betas are evenly spaced from 0.1 to 0.5, alphas are 1 - beta, and alpha_bars is their cumulative product, so it decays quickly toward zero.
b, a, ab = linear_schedule(5, 0.1, 0.5) print(np.round(b, 4).tolist()) print(np.round(a, 4).tolist()) print(np.round(ab, 4).tolist())
[0.1, 0.2, 0.3, 0.4, 0.5] [0.9, 0.8, 0.7, 0.6, 0.5] [0.9, 0.72, 0.504, 0.3024, 0.1512]
np.linspace(0.1, 0.5, 5) gives betas [0.1, 0.2, 0.3, 0.4, 0.5], so alphas are [0.9, 0.8, 0.7, 0.6, 0.5]. The cumulative product runs 0.9, 0.9*0.8 = 0.72, 0.72*0.7 = 0.504, 0.504*0.6 = 0.3024, 0.3024*0.5 = 0.1512 -- the signal that survives to each step. Using a cumulative sum instead would give values above 1, which is impossible for a variance-preserving process.
T >= 1; when T == 1 the array holds beta_start only.betas must include both endpoints (np.linspace, not np.arange).alpha_bars is a cumulative product, not a cumulative sum.float (float64) arrays; do not round inside the function.