Diffusion Reverse Process
Last modified: July 21, 2026
To implement the reverse process (sampling), we essentially perform a “denoising” loop. We start with a tensor of pure Gaussian noise and gradually subtract the noise predicted by our trained model until we reach a clean image.
Here is the step-by-step algorithmic breakdown for the Reverse Diffusion (Sampling). Before starting the loop, you need:
- A Trained Model ($\epsilon_\theta$): Usually a U-Net that takes $(x_t, t)$ as input and outputs the predicted noise.
- Variance Schedule ($\alpha, \bar{\alpha}, \beta$): These must be the exact same values used during the training (Forward) phase.
- Total Steps ($T$): The number of iterations (e.g., 1000).
The Sampling Algorithm
For each image you want to generate:
- Initialize: Start with $x_T \sim \mathcal{N}(0, \mathbf{I})$ (pure white noise).
- Iterate: For $t = T, T-1, \dots, 1$:
- Sample random noise: $z \sim \mathcal{N}(0, \mathbf{I})$ if $t > 1$, else $z = 0$.
- Predict the noise: Pass the current image $x_t$ and the time step $t$ into your model to get $\epsilon_\theta(x_t, t)$.
- Compute the Mean ($\mu_\theta$): This is the “de-noised” version of the current image.$$\mu_\theta(x_t, t) = \frac{1}{\sqrt{\alpha_t}} \left( x_t - \frac{\beta_t}{\sqrt{1 - \bar{\alpha}_t}} \epsilon_\theta(x_t, t) \right)$$
- Update the Image: Calculate the next step $x_{t-1}$ by adding back a tiny bit of controlled variance ($\sigma_t z$) to keep the process stochastic:$$x_{t-1} = \mu_\theta(x_t, t) + \sigma_t z$$
- Result: $x_0$ is your generated image.
Implementation Details (Pseudocode)
def sample(model, schedule, shape):
# 1. Start with pure noise
img = torch.randn(shape)
for t in reversed(range(0, T)):
# t_tensor tells the model which noise level we are at
t_batch = torch.full((shape[0],), t, dtype=torch.long)
# 2. Predict noise using the U-Net
predicted_noise = model(img, t_batch)
# 3. Calculate coefficients (from our pre-defined schedule)
alpha = schedule.alphas[t]
alpha_bar = schedule.alphas_bar[t]
beta = schedule.betas[t]
# 4. Remove a portion of the predicted noise
mean = (1 / sqrt(alpha)) * (img - (beta / sqrt(1 - alpha_bar)) * predicted_noise)
if t > 0:
noise = torch.randn_like(img)
sigma = sqrt(beta) # Or use the posterior variance formula
img = mean + sigma * noise
else:
img = mean
return img
Why do we add noise back ($z$) in the reverse step?
It seems counter-intuitive to add noise while trying to clean an image. However, if we didn’t add that tiny bit of random noise $\sigma_t z$ at each step, the model would be purely deterministic. Adding it allows the model to explore the distribution of “natural images” more effectively, preventing it from getting stuck or collapsing into blurry averages.