Skip to content
  • Diffusion Models · Deep Learning

Diffusion Models

LearnViaNotes @learnvianotes· 4 min read

A diffusion model learns to reverse a gradual noising process. Training: add noise to real data and teach a network to predict that noise. Generation: start from pure noise and remove it step by step. It's the engine behind Stable Diffusion, DALL·E 3 and most image and video generators.

Image x₀ → add a little noise … ×T → Pure noise x_T → denoise step by step (learned) → New image

The forward (noising) process

Each step mixes in a little Gaussian noise according to a schedule β₁ … β_T. Conveniently, you can jump straight to any step t in closed form — no need to loop:

x_t = √ᾱ_t · x₀ + √(1 − ᾱ_t) · ε ε ~ N(0, I), ᾱ_t = Π (1 − β_s)

Python
import numpy as np

T = 1000
betas = np.linspace(1e-4, 0.02, T)          # the original DDPM linear schedule
alpha_bar = np.cumprod(1 - betas)

for_steps = {t: round(float(np.sqrt(alpha_bar[t])), 3) for t in (0, 250, 500, 999)}
print(for_steps)          # {0: 1.0, 250: 0.722, 500: 0.279, 999: 0.006} ← how much of the original signal remains

rng = np.random.default_rng(0)
x0 = np.array([1.0, -1.0, 0.5])
t = 500
xt = np.sqrt(alpha_bar[t]) * x0 + np.sqrt(1 - alpha_bar[t]) * rng.normal(size=3)
print(xt.round(2).tolist())                 # [0.4, -0.41, 0.75] ← x₀ at step 500: mostly noise

Training: predict the noise

loss = ‖ ε − ε_θ(x_t, t) ‖² (t random, ε random, x₀ from the data)

That's the whole objective: a plain mean-squared error. The network (a U-Net or a Transformer — “DiT”) takes the noisy input and the step t, and guesses the noise that was added. A toy version on 1-D data — a mixture of two bumps at −2 and +2:

Python
import torch
from torch import nn

torch.manual_seed(0)
T = 100
betas = torch.linspace(1e-4, 0.1, T)
alphas, alpha_bar = 1 - betas, torch.cumprod(1 - betas, dim=0)
data = lambda n: torch.where(torch.rand(n, 1) < 0.5, -2.0, 2.0) + 0.3 * torch.randn(n, 1)

model = nn.Sequential(nn.Linear(2, 64), nn.SiLU(), nn.Linear(64, 64), nn.SiLU(), nn.Linear(64, 1))
opt = torch.optim.Adam(model.parameters(), lr=2e-3)

for step in range(3000):                                      # training
    x0 = data(256)
    t = torch.randint(0, T, (256, 1))
    eps = torch.randn_like(x0)
    xt = alpha_bar[t].sqrt() * x0 + (1 - alpha_bar[t]).sqrt() * eps   # jump to step t
    loss = ((model(torch.cat([xt, t / T], dim=1)) - eps) ** 2).mean()
    opt.zero_grad(); loss.backward(); opt.step()

with torch.no_grad():                                         # sampling (DDPM)
    x = torch.randn(2000, 1)                                  # start from pure noise
    for t in reversed(range(T)):
        eps_hat = model(torch.cat([x, torch.full_like(x, t / T)], dim=1))
        x = (x - betas[t] / (1 - alpha_bar[t]).sqrt() * eps_hat) / alphas[t].sqrt()
        if t > 0:
            x = x + betas[t].sqrt() * torch.randn_like(x)     # a little fresh noise each step

print(round(alpha_bar[-1].item(), 3))                         # 0.006 ← x_T is essentially pure noise
print(round((x < 0).float().mean().item(), 2))                # 0.49 ← about half the samples in each bump
print(round(x[x < 0].mean().item(), 1), round(x[x > 0].mean().item(), 1))   # -2.0 2.0 ← centred on the two modes

Making it practical

IdeaWhat it does
DDIM & faster samplersDeterministic updates that skip steps: 20–50 steps instead of 1000
Latent diffusionDiffuse in a VAE's small latent space instead of pixels — much cheaper (Stable Diffusion)
Text conditioningFeed a text encoder's embeddings to the denoiser via cross-attention
Classifier-free guidanceε = ε_uncond + w · (ε_cond − ε_uncond), w ≈ 3–8: follow the prompt more strongly
Distillation / consistency modelsFew-step or one-step generation from a trained diffusion model

Comparing generative models

VAEGANDiffusion
Sample qualityBlurrySharpSharp and diverse
Diversity / mode coverageGoodMode collapse riskGood
TrainingStableUnstable (adversarial)Stable (simple MSE)
Sampling speedOne passOne passMany steps (slow) — samplers help
Likelihood / latentYes / yesNo / noise vectorApproximate / no compact latent

How do diffusion models generate images?

Why did diffusion models overtake GANs for image generation?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report