Diffusion models
Be able to explain the forward and reverse processes, train a small diffusion model and generate images.
Prerequisites
- EConvolutional networks (CNNs)helpful
- EGenerative models — an overviewrequired
Intuition
The forward process destroys an image: add a little Gaussian noise, over and over, in T steps until only noise remains. It is fixed and requires no learning.
The reverse process is what the model learns: given a noisy image at step t, predict the noise that was added. If you can do that you can take a step back towards a cleaner image. Repeat from pure noise and an image emerges.
The training is surprisingly simple: draw an image, draw a random t, add the corresponding noise, let the network guess the noise, and measure with MSE. No discriminator, no instability — which is why diffusion beat GANs.
Formal
Forward: . With and you can jump straight to any step:
The training objective (Ho et al. 2020) — surprisingly, just an MSE:
Sampling: start at and iterate backwards. DDPM uses all T steps (often 1 000); DDIM makes the process deterministic and manages 20–50 steps at almost the same quality.
Classifier-free guidance: train the model both with and without the text condition (the condition is dropped at random in ~10 % of cases) and extrapolate at sampling time: A higher gives stronger adherence to the prompt but less variation — the parameter users know as the «guidance scale».
Latent diffusion (Stable Diffusion) runs the whole process in a compressed latent space from an autoencoder, which cuts the computation ~50× and is why image generation runs on consumer hardware.
Code
import torch, torch.nn as nn
T = 1000
betas = torch.linspace(1e-4, 0.02, T)
alphas = 1.0 - betas
abar = torch.cumprod(alphas, dim=0)
def add_noise(x0, t, eps=None):
"""Jump straight to step t."""
eps = torch.randn_like(x0) if eps is None else eps
a = abar[t].view(-1, 1, 1, 1)
return a.sqrt() * x0 + (1 - a).sqrt() * eps, eps
def train_step(model, x0, opt):
t = torch.randint(0, T, (x0.size(0),))
xt, eps = add_noise(x0, t)
loss = nn.functional.mse_loss(model(xt, t), eps) # predict the noise
loss.backward(); opt.step(); opt.zero_grad()
return loss.item()
@torch.no_grad()
def sample(model, shape, steps=50):
x = torch.randn(shape)
for t in reversed(range(0, T, T // steps)): # DDIM-like jumps
tt = torch.full((shape[0],), t, dtype=torch.long)
eps = model(x, tt)
a = abar[t]
x0_hat = (x - (1 - a).sqrt() * eps) / a.sqrt()
a_prev = abar[max(t - T // steps, 0)]
x = a_prev.sqrt() * x0_hat + (1 - a_prev).sqrt() * eps
return x
Mastery means
- Explains the forward and the reverse process
- Describes the training objective (predict the noise)
- Understands guidance and the sampling steps
Sign in to do the exercises and build your mastery up.
Sources
- arXiv — Denoising Diffusion Probabilistic Models — arXiv (open access; licence per article)
- arXiv — High-Resolution Image Synthesis with Latent Diffusion Models — arXiv (open access; licence per article)
- arXiv — Classifier-Free Diffusion Guidance — arXiv (open access; licence per article)