Generative Models — 理论 & Tokenizers基础约 42 分钟P2 · Watch

Diffusion Foundations

DDPM、score matching、DDIM、EDM、CFG、consistency models 与从零代码。

DDPMscoreDDIMEDMCFG
内容来源:ARIS-in-AI-Offer by Ruofeng Yang (杨若峰),许可证 MIT。Imported from commit 7fae0a820348.
Local freshness note

Imported from ARIS and not yet locally reviewed section by section. Cross-check recent papers, model reports, and library APIs while reading.

Check recent citationsAdd post-2026 updatesFlag possibly stale model/API names

§0 TL;DR

💡 Diffusion fundamentals in 9 sentences — one-page interview essentials (full derivations in §1-§13).

  1. DDPM (Ho 2020): forward q(xt∣x0)=N(αˉtx0,(1−αˉt)I)q(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I) admits closed-form sampling; reverse pθ(xt−1∣xt)=N(μθ,Σθ)p_\theta(x_{t-1}|x_t) = \mathcal{N}(\mu_\theta, \Sigma_\theta) learns the reverse Gaussian; the ELBO simplifies to Lsimple=E∥ϵ−ϵθ(xt,t)∥2L_\text{simple} = \mathbb{E}\|\epsilon - \epsilon_\theta(x_t, t)\|^2 (ϵ\epsilon-prediction).

  2. Three equivalent views: DDPM's ϵ\epsilon, score-based s=∇log⁡pts = \nabla \log p_t, and flow matching's vv are linearly invertible under the Gaussian path — sθ=−ϵθ/σts_\theta = -\epsilon_\theta / \sigma_t, v=α′x0+σ′ϵv = \alpha'x_0 + \sigma'\epsilon.

  3. Tweedie's formula: E[x0∣xt]=xt+σt2∇xtlog⁡pt(xt)\mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2 \nabla_{x_t} \log p_t(x_t) — a one-line bridge between denoiser and score.

  4. Score SDE (Song 2021): a unified VP-SDE / VE-SDE framework; the reverse-time SDE and the probability flow ODE share the same family of marginals, and the ODE form directly yields FM's vector field.

  5. DDIM (Song 2020 / ICLR 2021): a non-Markovian forward leads to a deterministic sampler with the same marginals as DDPM but a controllable sampling path (η=0\eta=0 deterministic; η=1\eta=1 over the full TT steps degenerates to DDPM ancestral, but with skipped steps it only matches DDPM variance, not strict equivalence).

  6. EDM (Karras 2022): preconditioning makes the network output have unit variance: Dθ(x;σ)=cskip(σ)x+cout(σ)Fθ(cin(σ)x,cnoise(σ))D_\theta(x;\sigma) = c_\text{skip}(\sigma) x + c_\text{out}(\sigma) F_\theta(c_\text{in}(\sigma) x, c_\text{noise}(\sigma)); combined with a σ\sigma-schedule + Heun 2nd-order sampler, SOTA FID with NFE down to 18-35.

  7. CFG (Ho-Salimans 2022): during training drop the condition with probability pdropp_\text{drop} → the same net learns conditional/unconditional; at inference ϵ~=(1+w)ϵθ(x,c)−wϵθ(x,∅)\tilde\epsilon = (1+w)\epsilon_\theta(x,c) - w\epsilon_\theta(x,\emptyset), with w∈[3,7]w \in [3, 7] being the workhorse range for text-to-image.

  8. Production: SD/SDXL use VAE latent + UNet; SD3 / FLUX.1 switch to Rectified Flow + MM-DiT; ControlNet adds a trainable side branch to a frozen UNet; DiT replaces the UNet entirely with a Transformer.

  9. Acceleration: DPM-Solver++ compresses NFE to 10-20; Consistency Models learn fθ(xt,t)↦x0f_\theta(x_t, t) \mapsto x_0 for 1-4 step sampling; LCM / LCM-LoRA / SDXL-Turbo (ADD) / SD3-Turbo (LADD) bring distillation to the entire Stable Diffusion family.

§1 Intuition & three views

1.1 One-sentence intuition

Diffusion = learning to "denoise": progressively noise data from clean to pure Gaussian (forward), then learn to reverse it step-by-step from noise back to data (reverse). All diffusion papers differ on just three things:

  • how forward adds noise (schedule, SDE type VP/VE)
  • what the network predicts (ϵ\epsilon / x0x_0 / vv / score / DD)
  • how reverse samples (Markov ancestral / DDIM / DPM-Solver / EDM Heun / Consistency one-step)

1.2 Comparison of the three views

                            Unified framework (Song et al. 2021)
                            
       Discrete view (DDPM)    Continuous view (Score SDE)   Flow view (FM/RF)
       ────────────         ──────────────────       ────────────────
       q(x_t|x_{t-1})  →    dx = f(x,t)dt+g(t)dW  →   dx = u_t(x) dt
        closed-form q(x_t|x_0) forward SDE              ODE (deterministic)
              ↓                       ↓                       ↓
        ε-prediction        score s = ∇ log p_t        vector field v_t
              ↘                       ↓                       ↙
                          All linearly invertible (under Gaussian path)
                          s = -ε/σ_t,   v = α'x_0 + σ'ε,   ε = -σ s

💡 One-line interview answer — "DDPM is a special case of VP-SDE in discrete time; score-based is the equivalent parametrization in continuous time; Flow Matching carries the same information as score matching under VP/VE paths but parametrizes as vv instead of ss. Rectified Flow steps outside the SDE framework, using a linear path to directly learn an ODE's vector field."

1.3 Convention (used throughout)

SymbolMeaning
x0x_0clean data sample
xtx_t, t∈{1,…,T}t \in \{1,\dots,T\} or t∈[0,T]t \in [0,T]noised sample
ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0, I)standard Gaussian noise
αt,βt=1−αt\alpha_t, \beta_t = 1 - \alpha_tDDPM single-step forward coefficients
αˉt=∏s=1tαs\bar\alpha_t = \prod_{s=1}^t \alpha_sDDPM cumulative coefficient
σt\sigma_tstandard deviation (the "noise level" in NCSN / EDM view)
sθ(xt,t)≈∇xtlog⁡pt(xt)s_\theta(x_t, t) \approx \nabla_{x_t}\log p_t(x_t)score
ϵθ(xt,t)≈ϵ\epsilon_\theta(x_t, t) \approx \epsilonthe noise predicted in DDPM
Dθ(x;σ)≈x0D_\theta(x; \sigma) \approx x_0EDM's denoiser output

⚠️ Time-direction pitfall — DDPM's paper has forward going t=0→Tt = 0 \to T (data noised to pure noise) and reverse going T→0T \to 0; FM papers often use t=0t = 0 noise and t=1t = 1 data. Before writing code in an interview always disambiguate the time direction — otherwise the sampler is easy to flip.

§2 DDPM Forward Process

2.1 Single step and closed form

The DDPM forward is a Markov chain:

q(xt∣xt−1)=N(xt;  1−βt xt−1,  βtI),t=1,…,Tq(x_t | x_{t-1}) = \mathcal{N}(x_t;\; \sqrt{1-\beta_t}\, x_{t-1},\; \beta_t I), \quad t = 1, \dots, T

Define αt=1−βt\alpha_t = 1 - \beta_t and αˉt=∏s=1tαs\bar\alpha_t = \prod_{s=1}^t \alpha_s. Key property: q(xt∣x0)q(x_t | x_0) is a closed-form Gaussian — you can jump from x0x_0 to any tt in a single step (the core of training efficiency):

  q(xt∣x0)=N ⁣(xt;  αˉt x0,  (1−αˉt)I)  \boxed{\; q(x_t | x_0) = \mathcal{N}\!\left(x_t;\; \sqrt{\bar\alpha_t}\, x_0,\; (1-\bar\alpha_t) I\right) \;}

Equivalent reparameterization:

xt=αˉt x0+1−αˉt ϵ,ϵ∼N(0,I)x_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1-\bar\alpha_t}\, \epsilon, \quad \epsilon \sim \mathcal{N}(0, I)

2.2 Closed-form derivation (mandatory, will keep coming up)

From the reparameterization xt=αtxt−1+βtztx_t = \sqrt{\alpha_t} x_{t-1} + \sqrt{\beta_t} z_t with independent zt∼N(0,I)z_t \sim \mathcal{N}(0, I). Recurse:

xt=αtxt−1+βtzt=αt(αt−1xt−2+βt−1zt−1)+βtzt=αtαt−1 xt−2+αtβt−1zt−1+βtzt⏟sum of independent Gaussians\begin{aligned} x_t &= \sqrt{\alpha_t} x_{t-1} + \sqrt{\beta_t} z_t \\ &= \sqrt{\alpha_t}\left(\sqrt{\alpha_{t-1}} x_{t-2} + \sqrt{\beta_{t-1}} z_{t-1}\right) + \sqrt{\beta_t} z_t \\ &= \sqrt{\alpha_t \alpha_{t-1}}\, x_{t-2} + \underbrace{\sqrt{\alpha_t \beta_{t-1}} z_{t-1} + \sqrt{\beta_t} z_t}_{\text{sum of independent Gaussians}} \end{aligned}

Variance of the sum of two independent Gaussians: αtβt−1+βt=αt(1−αt−1)+(1−αt)=1−αtαt−1\alpha_t \beta_{t-1} + \beta_t = \alpha_t(1 - \alpha_{t-1}) + (1 - \alpha_t) = 1 - \alpha_t \alpha_{t-1}. So it merges into a single Gaussian 1−αtαt−1 zˉ\sqrt{1 - \alpha_t \alpha_{t-1}}\, \bar z. Induct to step tt:

xt=αˉt x0+1−αˉt ϵx_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1 - \bar\alpha_t}\, \epsilon

💡 Variational trick intuition — the benefit of a Markov chain with Gaussian steps is that the cumulative distribution is still Gaussian; this lets forward sample without a network, and training doesn't need to simulate the whole chain.

2.3 Boundary cases and limits

  • t=0t = 0: αˉ0=1\bar\alpha_0 = 1, x0x_0 itself — the forward start point
  • t=Tt = T (DDPM uses 1000): we want αˉT≈0\bar\alpha_T \approx 0, so xT≈ϵ∼N(0,I)x_T \approx \epsilon \sim \mathcal{N}(0, I) — the forward endpoint is close to the Gaussian prior

⚠️ SNR (Signal-to-Noise Ratio) at schedule end — SNR(t)=αˉt/(1−αˉt)(t) = \bar\alpha_t / (1-\bar\alpha_t); the linear schedule has αˉT≈4×10−5\bar\alpha_T \approx 4\times 10^{-5} at t=Tt=T, corresponding to SNR ≈4×10−5\approx 4\times 10^{-5} — small but strictly speaking not zero, so the prior doesn't fully match N(0,I)\mathcal{N}(0,I); this is one motivation behind the cosine schedule and "v-prediction" improvements.

§3 DDPM Reverse Process & Training

3.1 The premise that reverse is Gaussian

In theory q(xt−1∣xt)q(x_{t-1} | x_t) is not Gaussian (it depends on the entire data distribution). But when βt\beta_t is small enough, the reverse conditional is approximately Gaussian (Feller 1949 / Sohl-Dickstein 2015), so we parametrize:

pθ(xt−1∣xt)=N ⁣(xt−1;  μθ(xt,t),  Σθ(xt,t))p_\theta(x_{t-1} | x_t) = \mathcal{N}\!\left(x_{t-1};\; \mu_\theta(x_t, t),\; \Sigma_\theta(x_t, t)\right)

3.2 ELBO derivation

DDPM optimizes the evidence lower bound (analogous to a VAE):

log⁡pθ(x0)≥Eq(x1:T∣x0)[log⁡pθ(x0:T)q(x1:T∣x0)]=−Eq[KL(q(xT∣x0) ∥ p(xT))]⏟LT (constant, prior matching)−∑t=2TEq[KL(q(xt−1∣xt,x0) ∥ pθ(xt−1∣xt))]⏟Lt−1+Eq[log⁡pθ(x0∣x1)]⏟L0 (decoder log-likelihood)\begin{aligned} \log p_\theta(x_0) &\ge \mathbb{E}_{q(x_{1:T}|x_0)}\left[\log \frac{p_\theta(x_{0:T})}{q(x_{1:T}|x_0)}\right] \\ &= -\underbrace{\mathbb{E}_q[\text{KL}(q(x_T|x_0) \,\Vert\, p(x_T))]}_{L_T \text{ (constant, prior matching)}} \\ &\quad - \sum_{t=2}^T \underbrace{\mathbb{E}_q[\text{KL}(q(x_{t-1}|x_t, x_0) \,\Vert\, p_\theta(x_{t-1}|x_t))]}_{L_{t-1}} \\ &\quad + \underbrace{\mathbb{E}_q[\log p_\theta(x_0 | x_1)]}_{L_0 \text{ (decoder log-likelihood)}} \end{aligned}

Key: q(xt−1∣xt,x0)q(x_{t-1} | x_t, x_0) is a closed-form Gaussian (derived from Bayes):

q(xt−1∣xt,x0)=N ⁣(xt−1;  μ~t(xt,x0),  β~tI)q(x_{t-1} | x_t, x_0) = \mathcal{N}\!\left(x_{t-1};\; \tilde\mu_t(x_t, x_0),\; \tilde\beta_t I\right)

where:

μ~t(xt,x0)=αˉt−1βt1−αˉtx0+αt(1−αˉt−1)1−αˉtxt,β~t=1−αˉt−11−αˉtβt\tilde\mu_t(x_t, x_0) = \frac{\sqrt{\bar\alpha_{t-1}} \beta_t}{1 - \bar\alpha_t} x_0 + \frac{\sqrt{\alpha_t}(1-\bar\alpha_{t-1})}{1-\bar\alpha_t} x_t, \quad \tilde\beta_t = \frac{1-\bar\alpha_{t-1}}{1-\bar\alpha_t}\beta_t

3.3 Simplifying to LsimpleL_\text{simple} (mandatory derivation)

Substitute x0=(xt−1−αˉtϵ)/αˉtx_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon) / \sqrt{\bar\alpha_t} into μ~t\tilde\mu_t:

μ~t=1αt(xt−βt1−αˉtϵ)\tilde\mu_t = \frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{\beta_t}{\sqrt{1-\bar\alpha_t}}\epsilon\right)

Parametrize μθ(xt,t)\mu_\theta(x_t, t) in the same form (ϵ\epsilon-prediction):

μθ(xt,t)=1αt(xt−βt1−αˉtϵθ(xt,t))\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)

Fix Σθ=σt2I\Sigma_\theta = \sigma_t^2 I (take σt2=βt\sigma_t^2 = \beta_t or β~t\tilde\beta_t). The KL between two Gaussians:

Lt−1=E[12σt2∥μ~t−μθ∥2]=E[βt22σt2αt(1−αˉt)∥ϵ−ϵθ(xt,t)∥2]L_{t-1} = \mathbb{E}\left[\frac{1}{2\sigma_t^2} \| \tilde\mu_t - \mu_\theta \|^2\right] = \mathbb{E}\left[\frac{\beta_t^2}{2\sigma_t^2 \alpha_t (1-\bar\alpha_t)} \|\epsilon - \epsilon_\theta(x_t, t)\|^2\right]

Ho 2020's engineering trick: drop all preceding coefficients + constant terms and just use the unweighted version:

  Lsimple(θ)=Et∼U{1,…,T},  x0,  ϵ[∥ϵ−ϵθ ⁣(αˉtx0+1−αˉtϵ,  t)∥2]  \boxed{\; L_\text{simple}(\theta) = \mathbb{E}_{t \sim \mathcal{U}\{1,\dots,T\},\; x_0,\; \epsilon}\Big[\big\|\epsilon - \epsilon_\theta\!\big(\sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t}\epsilon,\; t\big)\big\|^2\Big] \;}

✅ Why does dropping the coefficient still work? — Ho 2020's empirical observation: the unweighted version effectively upweights low-SNR (high tt) loss, which in turn improves sample quality. The cost: log⁡\log-likelihood is no longer the ELBO lower bound — so "good FID" ≠ "good likelihood". Improved DDPM (Nichol-Dhariwal 2021) later introduces a hybrid loss Lhybrid=Lsimple+λLvlbL_\text{hybrid} = L_\text{simple} + \lambda L_\text{vlb} (λ=0.001\lambda = 0.001), simultaneously learning Σθ\Sigma_\theta.

3.4 Equivalent prediction targets (memorize)

Given xt=αˉtx0+1−αˉtϵx_t = \sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t}\epsilon, the three mainstream parametrizations are linearly invertible:

ϵ-pred:ϵθ(xt,t)≈ϵx0-pred:x^0(xt,t)=xt−1−αˉt ϵθαˉtv-pred (Salimans-Ho 2022):vθ=αˉt ϵ−1−αˉt x0score:sθ(xt,t)=−ϵθ(xt,t)1−αˉt\begin{aligned} \epsilon\text{-pred} &:\quad \epsilon_\theta(x_t, t) \approx \epsilon \\ x_0\text{-pred} &:\quad \hat x_0(x_t, t) = \frac{x_t - \sqrt{1-\bar\alpha_t}\, \epsilon_\theta}{\sqrt{\bar\alpha_t}} \\ v\text{-pred (Salimans-Ho 2022)} &:\quad v_\theta = \sqrt{\bar\alpha_t}\, \epsilon - \sqrt{1-\bar\alpha_t}\, x_0 \\ \text{score} &:\quad s_\theta(x_t, t) = -\frac{\epsilon_\theta(x_t, t)}{\sqrt{1-\bar\alpha_t}} \end{aligned}

💡 Why is v-prediction more stable? — ϵ\epsilon-pred degenerates as t→0t \to 0 (small noise; loss coefficient explodes); x0x_0-pred degenerates as t→Tt \to T (large noise); vv-pred interpolates between them and has approximately uniform loss magnitude across all tt — that's the key choice in Imagen Video / SD2.1-v / Karras EDM.

§4 Schedule: linear / cosine / EDM

4.1 Linear (Ho 2020)

βt=βstart+t−1T−1(βend−βstart),βstart=10−4,  βend=0.02\beta_t = \beta_\text{start} + \frac{t-1}{T-1}(\beta_\text{end} - \beta_\text{start}), \quad \beta_\text{start} = 10^{-4},\; \beta_\text{end} = 0.02

T=1000T = 1000. Simple and stable, but the end-SNR isn't strictly zero (αˉT≈4×10−5\bar\alpha_T \approx 4 \times 10^{-5}, SNR ≈4×10−5\approx 4 \times 10^{-5}, while an ideal prior wants this closer to 0).

4.2 Cosine (Nichol-Dhariwal 2021)

αˉt=f(t)f(0),f(t)=cos⁡2 ⁣((t/T)+s1+s⋅π2),s=0.008\bar\alpha_t = \frac{f(t)}{f(0)}, \quad f(t) = \cos^2\!\left(\frac{(t/T) + s}{1 + s} \cdot \frac{\pi}{2}\right), \quad s = 0.008

βt=1−αˉt/αˉt−1\beta_t = 1 - \bar\alpha_t / \bar\alpha_{t-1} (then clip to [0,0.999][0, 0.999] for numerical stability). The s=0.008s = 0.008 offset is to prevent β1\beta_1 from being too close to 0.

✅ Why is the cosine schedule better? — A linear schedule adds noise too fast in the low-tt region, so the model spends most of its time "training" on already-pure-noise regions (and learns nothing). Cosine adds noise slowly at low tt, faster in the middle, and reaches near-zero SNR at the end. Improved DDPM experiments: cosine improves FID on ImageNet 64 by about 20% vs linear.

4.3 EDM σ-schedule (Karras 2022)

EDM reparametrizes the β\beta schedule into a σ\sigma schedule (directly using σ\sigma as time). At sampling:

σi=(σmax1/ρ+iN−1(σmin1/ρ−σmax1/ρ))ρ,i=0,…,N−1\sigma_i = \left(\sigma_\text{max}^{1/\rho} + \frac{i}{N-1}\left(\sigma_\text{min}^{1/\rho} - \sigma_\text{max}^{1/\rho}\right)\right)^\rho, \quad i = 0, \dots, N-1

Defaults: σmin=0.002\sigma_\text{min} = 0.002, σmax=80\sigma_\text{max} = 80, ρ=7\rho = 7. ρ=7\rho = 7 was swept empirically by Karras — beats linear / log spacing because it allocates more steps to the small-σ\sigma (high-SNR) region, where stepping errors are more sensitive.

💡 Discrete vs continuous schedules — DDPM's β\beta array is equivalent to a VP-SDE's β(t)=Tβ⌊tT⌋\beta(t) = T \beta_{\lfloor tT \rfloor}; EDM's σ\sigma-schedule corresponds to a VE-SDE with σ(t)=t\sigma(t) = t (linear time); the two differ only by a tt reparametrization, informationally equivalent. EDM's contribution is discovering an empirically more stable σi\sigma_i selection rule.

§5 Score-based view

5.1 Score and score matching (Hyvärinen 2005)

Define s(x)=∇xlog⁡p(x)s(x) = \nabla_x \log p(x). If we learn sθ≈ss_\theta \approx s, we can sample via Langevin dynamics:

xk+1=xk+η2sθ(xk)+η zk,zk∼N(0,I)x_{k+1} = x_k + \frac{\eta}{2} s_\theta(x_k) + \sqrt{\eta}\, z_k, \quad z_k \sim \mathcal{N}(0, I)

The direct score matching loss Ep∥sθ−∇log⁡p∥2\mathbb{E}_p\|s_\theta - \nabla\log p\|^2 is not computable (we don't know ∇log⁡p\nabla \log p). Hyvärinen 2005 gave implicit score matching that avoids ∇log⁡p\nabla \log p via integration by parts:

Ep[∥sθ(x)∥2+2tr⁡(∇xsθ(x))]\mathbb{E}_p\left[\|s_\theta(x)\|^2 + 2 \operatorname{tr}(\nabla_x s_\theta(x))\right]

But tr⁡(∇xsθ)\operatorname{tr}(\nabla_x s_\theta) is too expensive in high dimensions (Hessian trace).

5.2 Denoising Score Matching (Vincent 2011)

For each data point x0x_0, add noise x~=x0+σϵ\tilde x = x_0 + \sigma \epsilon and define the perturbed distribution pσ(x~)=∫p(x0)N(x~;x0,σ2I)dx0p_\sigma(\tilde x) = \int p(x_0) \mathcal{N}(\tilde x; x_0, \sigma^2 I) dx_0. Vincent 2011 proved:

Epσ(x~)∥sθ(x~)−∇log⁡pσ(x~)∥2=Ex0,x~∥sθ(x~)−∇x~log⁡q(x~∣x0)∥2+const\mathbb{E}_{p_\sigma(\tilde x)}\|s_\theta(\tilde x) - \nabla \log p_\sigma(\tilde x)\|^2 = \mathbb{E}_{x_0, \tilde x}\left\|s_\theta(\tilde x) - \nabla_{\tilde x} \log q(\tilde x | x_0)\right\|^2 + \text{const}

And the score of q(x~∣x0)=N(x0,σ2I)q(\tilde x | x_0) = \mathcal{N}(x_0, \sigma^2 I) has a closed form:

∇x~log⁡q(x~∣x0)=−x~−x0σ2=−ϵσ\nabla_{\tilde x} \log q(\tilde x | x_0) = -\frac{\tilde x - x_0}{\sigma^2} = -\frac{\epsilon}{\sigma}

So the training loss simplifies to:

  LDSM(θ)=Ex0,σ,ϵ∥σ sθ(x~;σ)+ϵ∥2  \boxed{\; L_\text{DSM}(\theta) = \mathbb{E}_{x_0, \sigma, \epsilon}\left\| \sigma\, s_\theta(\tilde x; \sigma) + \epsilon \right\|^2 \;}

This is exactly the NCSN / SMLD training objective (up to a weight).

5.3 Tweedie's formula (mandatory derivation)

Statement: for additive Gaussian noise xt=x0+σtϵx_t = x_0 + \sigma_t \epsilon (VE view, ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0,I)):

  E[x0∣xt]=xt+σt2 ∇xtlog⁡pt(xt)  \boxed{\; \mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2\, \nabla_{x_t} \log p_t(x_t) \;}

Derivation: pt(xt)=∫p0(x0)N(xt;x0,σt2I) dx0p_t(x_t) = \int p_0(x_0) \mathcal{N}(x_t; x_0, \sigma_t^2 I)\, dx_0. Take the gradient w.r.t. xtx_t:

∇xtpt(xt)=∫p0(x0)⋅∇xtN(xt;x0,σt2I) dx0=∫p0(x0)⋅N(xt;x0,σt2I)⋅x0−xtσt2 dx0\nabla_{x_t} p_t(x_t) = \int p_0(x_0) \cdot \nabla_{x_t} \mathcal{N}(x_t; x_0, \sigma_t^2 I)\, dx_0 = \int p_0(x_0) \cdot \mathcal{N}(x_t; x_0, \sigma_t^2 I) \cdot \frac{x_0 - x_t}{\sigma_t^2}\, dx_0

Divide both sides by pt(xt)p_t(x_t):

∇xtlog⁡pt(xt)=1pt(xt)∫p0(x0)N(xt∣x0)x0−xtσt2 dx0=Ep0(x0∣xt)[x0−xtσt2]\nabla_{x_t} \log p_t(x_t) = \frac{1}{p_t(x_t)} \int p_0(x_0) \mathcal{N}(x_t | x_0) \frac{x_0 - x_t}{\sigma_t^2}\, dx_0 = \mathbb{E}_{p_0(x_0 | x_t)}\left[\frac{x_0 - x_t}{\sigma_t^2}\right]

That is:

σt2∇xtlog⁡pt(xt)=E[x0∣xt]−xt⇒E[x0∣xt]=xt+σt2∇xtlog⁡pt(xt)□\sigma_t^2 \nabla_{x_t} \log p_t(x_t) = \mathbb{E}[x_0 | x_t] - x_t \quad \Rightarrow \quad \mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2 \nabla_{x_t} \log p_t(x_t) \quad \square

✅ Tweedie is the "Rosetta Stone" linking all diffusion parametrizations — the denoiser network's optimal output (MMSE estimator) is the score plus an identity map. All conversions among ϵ\epsilon-pred / score-pred / x0x_0-pred / vv-pred are one-line rearrangements of Tweedie.

5.4 NCSN / SMLD (Song-Ermon 2019)

Noise-Conditional Score Network: train a shared network sθ(x,σ)s_\theta(x, \sigma) that does DSM at multiple noise levels σ1>σ2>⋯>σL\sigma_1 > \sigma_2 > \dots > \sigma_L simultaneously. Sampling uses annealed Langevin dynamics: first Langevin at large σ1\sigma_1 (explore the whole space), then decay to σL\sigma_L (refine details).

x←x+ϵi2sθ(x,σi)+ϵi z,ϵi=η⋅(σi/σL)2x \leftarrow x + \frac{\epsilon_i}{2} s_\theta(x, \sigma_i) + \sqrt{\epsilon_i}\, z, \quad \epsilon_i = \eta \cdot (\sigma_i / \sigma_L)^2

Run TT Langevin steps at each σi\sigma_i, then switch to the next σi+1\sigma_{i+1}.

⚠️ Why doesn't a single σ\sigma work? — Scores trained at small σ\sigma are completely wrong in regions far from the data manifold (in the "empty regions" between modes where p(x)≈0p(x) \approx 0, the score gives no direction). The core of multiple noise levels is using large σ\sigma to "fill" the space, providing initial positions for small σ\sigma.

§6 Score SDE: unified framework + Probability Flow ODE

6.1 Forward SDE

Song et al. 2021 (ICLR) write all diffusions as a forward SDE:

dx=f(x,t) dt+g(t) dWdx = f(x, t)\, dt + g(t)\, dW

Typef(x,t)f(x, t)g(t)g(t)Discrete counterpart
VP-SDE (variance preserving)−12β(t)x-\frac{1}{2}\beta(t) xβ(t)\sqrt{\beta(t)}DDPM
VE-SDE (variance exploding)00d[σ2(t)]/dt\sqrt{d[\sigma^2(t)]/dt}SMLD / EDM
sub-VP−12β(t)x-\frac{1}{2}\beta(t) xβ(t)(1−e−2∫0tβ(s)ds)\sqrt{\beta(t)(1-e^{-2\int_0^t \beta(s)ds})}between VP/VE, better likelihood

VP-SDE satisfies Var[xt]≤1\text{Var}[x_t] \le 1 (variance preserving); VE-SDE has unbounded variance growth (variance exploding).

6.2 Reverse SDE (Anderson 1982)

For any forward SDE, there exists a reverse-time SDE:

  dx=[f(x,t)−g2(t) ∇xlog⁡pt(x)]dt+g(t) dWˉ  \boxed{\; dx = \left[f(x, t) - g^2(t)\, \nabla_x \log p_t(x)\right] dt + g(t)\, d\bar W \;}

where dWˉd\bar W is a reverse-time Wiener process. Sampling: start from xT∼pTx_T \sim p_T (close to prior), then integrate to t=0t = 0 with an SDE solver (Euler-Maruyama / predictor-corrector).

6.3 Probability Flow ODE (bridge to FM)

Key theorem (Song et al. 2021, "Score-Based Generative Modeling through SDEs"): the following deterministic ODE shares all marginals ptp_t with the reverse SDE:

  dxdt=f(x,t)−12g2(t) ∇xlog⁡pt(x)  \boxed{\; \frac{dx}{dt} = f(x, t) - \frac{1}{2} g^2(t)\, \nabla_x \log p_t(x) \;}

This is the probability flow ODE. Equivalent to Flow Matching's vector field:

ut(x)=f(x,t)−12g2(t) sθ(x,t)u_t(x) = f(x, t) - \tfrac{1}{2} g^2(t)\, s_\theta(x, t)

✅ The three samplers' relationship —

           forward SDE (training: score matching)
                       ↓
           ┌──────────────────────┐
           ↓                      ↓
     reverse SDE              probability flow ODE
     (stochastic)             (deterministic, ⇔ FM)
           ↓                      ↓
   DDPM ancestral sampler   DDIM (η=0) / EDM / DPM-Solver

Proof sketch: write the forward SDE's Fokker-Planck (continuity equation):

∂pt∂t=−∇⋅(fpt)+12g2Δpt\frac{\partial p_t}{\partial t} = -\nabla \cdot (f p_t) + \frac{1}{2} g^2 \Delta p_t

Using Δpt=∇⋅(pt∇log⁡pt)\Delta p_t = \nabla \cdot (p_t \nabla \log p_t), write the diffusion term in transport form:

∂pt∂t=−∇⋅[(f−12g2∇log⁡pt)pt]\frac{\partial p_t}{\partial t} = -\nabla \cdot \left[\left(f - \tfrac{1}{2} g^2 \nabla \log p_t\right) p_t\right]

This is exactly the continuity equation for the ODE dx/dt=f−12g2∇log⁡ptdx/dt = f - \frac{1}{2} g^2 \nabla \log p_t — so their ptp_t agree.

6.4 Advantages of the ODE view

AdvantageDescription
Deterministicsame noise → same sample, enables image editing / interpolation
NFE-friendlyhigh-order ODE solvers (Heun / RK4 / DPM-Solver) need few steps
Computable likelihoodlog⁡p0(x0)=log⁡pT(xT)+∫0T∇⋅vt(x(t)) dt\log p_0(x_0) = \log p_T(x_T) + \int_0^T \nabla \cdot v_t(x(t))\, dt (PF-ODE instantaneous change-of-variables, Chen et al. 2018), with div estimated via Hutchinson trace estimator
Bridge to FMthe route taken by RF / SD3 / FLUX

⚠️ SDE vs ODE trade-off — the stochastic perturbation in SDE sampling can "correct" early errors, typically yielding better sample quality but at higher NFE; ODE is deterministic but susceptible to solver-error accumulation and needs higher-order solvers. EDM proposes a middle ground: base ODE + small stochastic churn ("SchurnS_\text{churn}"), with better FID.

§7 DDIM: Non-Markovian forward → deterministic sampler

7.1 Motivation

DDPM ancestral sampling must walk all T=1000T = 1000 steps (a Markov chain). Can we sample with fewer steps without retraining? DDIM (Song et al. 2020 arXiv / ICLR 2021) gives a "yes" — the core is making forward non-Markovian while preserving the same marginal q(xt∣x0)q(x_t | x_0) as DDPM.

7.2 Non-Markovian forward

DDIM defines a family of forward distributions, controlled by a parameter η∈[0,1]\eta \in [0, 1]:

qσ(xt−1∣xt,x0)=N ⁣(xt−1;  αˉt−1 x0+1−αˉt−1−σt2 xt−αˉtx01−αˉt,  σt2I)q_\sigma(x_{t-1} | x_t, x_0) = \mathcal{N}\!\left(x_{t-1};\; \sqrt{\bar\alpha_{t-1}}\, x_0 + \sqrt{1 - \bar\alpha_{t-1} - \sigma_t^2}\, \frac{x_t - \sqrt{\bar\alpha_t} x_0}{\sqrt{1-\bar\alpha_t}},\; \sigma_t^2 I\right)

where σt2=η2⋅β~t=η2⋅1−αˉt−11−αˉtβt\sigma_t^2 = \eta^2 \cdot \tilde\beta_t = \eta^2 \cdot \frac{1-\bar\alpha_{t-1}}{1-\bar\alpha_t} \beta_t.

Key property (DDIM Theorem 1): under this forward, q(xt∣x0)q(x_t | x_0) is still N(αˉtx0,(1−αˉt)I)\mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I) — identical to DDPM! So we can directly use a DDPM-trained ϵθ\epsilon_\theta for DDIM sampling.

7.3 DDIM sampling formula

Substitute x0→x^0=(xt−1−αˉt ϵθ(xt,t))/αˉtx_0 \to \hat x_0 = (x_t - \sqrt{1-\bar\alpha_t}\, \epsilon_\theta(x_t, t)) / \sqrt{\bar\alpha_t}:

  xt−1=αˉt−1 x^0+1−αˉt−1−σt2 ϵθ(xt,t)+σt z,z∼N(0,I)  \boxed{\; x_{t-1} = \sqrt{\bar\alpha_{t-1}}\, \hat x_0 + \sqrt{1 - \bar\alpha_{t-1} - \sigma_t^2}\, \epsilon_\theta(x_t, t) + \sigma_t\, z, \quad z \sim \mathcal{N}(0, I) \;}

  • η=0\eta = 0 (DDIM): σt=0\sigma_t = 0, deterministic — same xTx_T gives same x^0\hat x_0 (latent-space interpolation friendly)
  • η=1\eta = 1 (walking the full TT steps): σt=β~t\sigma_t = \sqrt{\tilde\beta_t}, degenerates to standard DDPM ancestral sampling; under fewer skipped steps S<TS < T it only matches DDPM variance order, not a strict 1000-step DDPM equivalent
  • Intermediate η∈(0,1)\eta \in (0, 1): tunable stochasticity

7.4 Skip steps (few-step sampling)

You don't have to step t→t−1t \to t-1; you can skip: pick a sub-sequence τ0<τ1<⋯<τS=T\tau_0 < \tau_1 < \dots < \tau_S = T and do:

xτi−1=αˉτi−1 x^0+1−αˉτi−1−στi2 ϵθ(xτi,τi)+στi zx_{\tau_{i-1}} = \sqrt{\bar\alpha_{\tau_{i-1}}}\, \hat x_0 + \sqrt{1 - \bar\alpha_{\tau_{i-1}} - \sigma_{\tau_i}^2}\, \epsilon_\theta(x_{\tau_i}, \tau_i) + \sigma_{\tau_i}\, z

Classic baseline: S=50S = 50 DDIM steps achieve FID close to 1000-step DDPM on ImageNet 256.

✅ DDIM = discretization of the probability flow ODE — when η=0\eta = 0 and the time grid is taken continuous, DDIM degenerates to the first-order Euler discretization of the probability flow ODE for the VP-SDE — that's why deterministic DDIM lines up with ODE-based samplers (DPM-Solver, EDM Heun).

§8 EDM: Karras 2022 Design Space

8.1 Motivation

Karras 2022 ("Elucidating the Design Space of Diffusion-Based Generative Models") decomposes all diffusion design knobs (parametrization, loss weighting, sampler, schedule) and sweeps each, arriving at SOTA recipes: CIFAR-10 FID 1.79 (35 NFE), ImageNet 64 FID 1.36.

8.2 Preconditioning (mandatory derivation)

EDM adopts the VE view: x=x0+σϵx = x_0 + \sigma \epsilon with ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0, I), treating σ\sigma directly as the noise level (no α\alpha).

Denoiser parametrization:

  Dθ(x; σ)=cskip(σ) x+cout(σ) Fθ ⁣(cin(σ) x,  cnoise(σ))  \boxed{\; D_\theta(x;\, \sigma) = c_\text{skip}(\sigma)\, x + c_\text{out}(\sigma)\, F_\theta\!\left(c_\text{in}(\sigma)\, x,\; c_\text{noise}(\sigma)\right) \;}

where FθF_\theta is the base network and the four cc functions are a hand-designed schedule. Karras' derivation:

Derivation: unit-variance argument

Goal: make FθF_\theta's input and training target have O(1)\mathcal{O}(1) variance at all σ\sigma.

Input side: the network sees cinxc_\text{in} x. Since Var[x]=σdata2+σ2\text{Var}[x] = \sigma_\text{data}^2 + \sigma^2 (data variance + noise variance):

cin(σ)=1σdata2+σ2⇒Var[cinx]=1c_\text{in}(\sigma) = \frac{1}{\sqrt{\sigma_\text{data}^2 + \sigma^2}} \quad \Rightarrow \quad \text{Var}[c_\text{in} x] = 1

Output side: the ideal denoiser is D∗(x;σ)=E[x0∣x]D^*(x; \sigma) = \mathbb{E}[x_0 | x] (Tweedie). Have the network learn the residual instead of the full quantity: define the effective target

F∗(x;σ)=1cout(σ)[D∗(x;σ)−cskip(σ) x]F^*(x; \sigma) = \frac{1}{c_\text{out}(\sigma)}\left[D^*(x;\sigma) - c_\text{skip}(\sigma)\, x\right]

We want Var[coutF∗+cskipx−D∗]=0\text{Var}[c_\text{out} F^* + c_\text{skip} x - D^*] = 0 and Var[F∗]=1\text{Var}[F^*] = 1 (so the network's target has unit variance).

Find cskip,coutc_\text{skip}, c_\text{out} minimizing the effective error (minimize E∥F∗−Fθ∥2\mathbb{E}\|F^* - F_\theta\|^2 under Var[F∗]=1\text{Var}[F^*]=1). Karras takes D∗=x0D^* = x_0 (ideal case), substitutes and expands:

cskip(σ)=σdata2σ2+σdata2,cout(σ)=σ⋅σdataσ2+σdata2c_\text{skip}(\sigma) = \frac{\sigma_\text{data}^2}{\sigma^2 + \sigma_\text{data}^2}, \quad c_\text{out}(\sigma) = \frac{\sigma \cdot \sigma_\text{data}}{\sqrt{\sigma^2 + \sigma_\text{data}^2}}

Intuition:

  • σ→0\sigma \to 0 (low noise): cskip→1,cout→0c_\text{skip} \to 1, c_\text{out} \to 0 — output is basically the input identity (denoiser does nothing)
  • σ→∞\sigma \to \infty (high noise): cskip→0,cout→σdatac_\text{skip} \to 0, c_\text{out} \to \sigma_\text{data} — output is fully determined by the network (input is pure noise)

Time encoding: cnoise(σ)=14ln⁡σc_\text{noise}(\sigma) = \frac{1}{4} \ln \sigma (log-scale, covering the wide dynamic range σ∈[σmin,σmax]\sigma \in [\sigma_\text{min}, \sigma_\text{max}]).

8.3 Training loss

EDM uses a weighted L2:

LEDM(θ)=Eσ,x0,ϵ[λ(σ) ∥Dθ(x0+σϵ; σ)−x0∥2]L_\text{EDM}(\theta) = \mathbb{E}_{\sigma, x_0, \epsilon}\Big[\lambda(\sigma)\, \big\| D_\theta(x_0 + \sigma\epsilon;\, \sigma) - x_0 \big\|^2\Big]

The weight is λ(σ)=(σ2+σdata2)/(σ⋅σdata)2=1/cout2\lambda(\sigma) = (\sigma^2 + \sigma_\text{data}^2) / (\sigma \cdot \sigma_\text{data})^2 = 1/c_\text{out}^2, equivalent to training FθF_\theta with unweighted L2 (the target has unit variance at every σ\sigma, so loss magnitudes are uniform).

Training σ\sigma sampling: ln⁡σ∼N(Pmean,Pstd2)\ln \sigma \sim \mathcal{N}(P_\text{mean}, P_\text{std}^2), defaults Pmean=−1.2P_\text{mean} = -1.2, Pstd=1.2P_\text{std} = 1.2 (concentrates σ\sigma around 0.30.3 — the "hardest to learn" SNR region, swept by Karras).

8.4 Heun 2nd-order sampler

EDM sampling defaults to Heun 2nd-order ODE + optional stochastic churn. The VE-SDE's probability flow ODE, with f=0,g(t)=dσ2/dtf = 0, g(t) = \sqrt{d\sigma^2/dt}:

dxdσ=−σ ∇xlog⁡pσ(x)=x−Dθ(x;σ)σ\frac{dx}{d\sigma} = -\sigma\, \nabla_x \log p_\sigma(x) = \frac{x - D_\theta(x; \sigma)}{\sigma}

(Use Tweedie: ∇log⁡pσ=(D−x)/σ2\nabla \log p_\sigma = (D - x)/\sigma^2, substitute into dx/dσ=−σ∇log⁡pdx/d\sigma = -\sigma \nabla \log p.)

Heun step ii (σi→σi+1\sigma_i \to \sigma_{i+1}, Δσ=σi+1−σi\Delta\sigma = \sigma_{i+1} - \sigma_i):

d_i  = (x_i - D_θ(x_i, σ_i)) / σ_i
x_*  = x_i + Δσ · d_i                       # Euler step (predictor)
if σ_{i+1} > 0:                             # skip corrector at last step
    d_*  = (x_* - D_θ(x_*, σ_{i+1})) / σ_{i+1}
    x_{i+1} = x_i + Δσ · (d_i + d_*) / 2     # Heun trapezoidal (corrector)
else:
    x_{i+1} = x_*

2 NFE per step, but second-order accuracy — more NFE than Euler but much more accurate. CIFAR-10 EDM with 35 NFE = 18 Heun steps + first-order final step yields FID 1.79.

💡 Stochastic churn (optional) — at the start of each step temporarily raise σi\sigma_i to σ^i=(1+γi)σi\hat\sigma_i = (1+\gamma_i)\sigma_i (with γi\gamma_i a small per-step churn), injecting extra noise: x^i=xi+σ^i2−σi2 z\hat x_i = x_i + \sqrt{\hat\sigma_i^2 - \sigma_i^2}\, z, where σ^i2−σi2=σi2γi+γi2\sqrt{\hat\sigma_i^2 - \sigma_i^2} = \sigma_i\sqrt{2\gamma_i + \gamma_i^2}; stepping from σ^i\hat\sigma_i down to σi+1\sigma_{i+1} is equivalent to a small SDE. EDM experiments: a small amount of churn slightly improves FID on ImageNet (about 0.1-0.3).

§9 High-order samplers: DPM-Solver / DPM-Solver++

9.1 Motivation

DDIM is first-order ODE Euler. DPM-Solver (Lu et al. 2022 NeurIPS) exploits the semi-linear structure of the diffusion ODE for high-order expansion. Rewriting the probability flow ODE under VP-SDE with ϵ\epsilon-pred:

dxdt=f(t) x+g(t) ϵθ(x,t)\frac{dx}{dt} = f(t)\, x + g(t)\, \epsilon_\theta(x, t)

where f(t)=−12β(t)f(t) = -\frac{1}{2}\beta(t), g(t)=+12β(t)/1−αˉtg(t) = +\frac{1}{2}\beta(t)/\sqrt{1-\bar\alpha_t} (from −12gSDE2⋅s=+12β⋅ϵ/1−αˉt-\frac{1}{2}g_\text{SDE}^2 \cdot s = +\frac{1}{2}\beta\cdot \epsilon/\sqrt{1-\bar\alpha_t}, since s=−ϵ/1−αˉts = -\epsilon/\sqrt{1-\bar\alpha_t}).

Integrate the linear part exactly (exponential integrator) and Taylor-expand the rest.

9.2 DPM-Solver-2 / 3 (core idea)

Let λt=log⁡(αˉt/1−αˉt)\lambda_t = \log(\sqrt{\bar\alpha_t} / \sqrt{1-\bar\alpha_t}) (log-SNR), use λ\lambda as time. Rewrite the ODE as:

xt=αˉtαˉsxs−αˉt∫λsλte−λϵ^θ(xτ,τ) dλx_{t} = \frac{\sqrt{\bar\alpha_t}}{\sqrt{\bar\alpha_s}} x_s - \sqrt{\bar\alpha_t} \int_{\lambda_s}^{\lambda_t} e^{-\lambda} \hat\epsilon_\theta(x_\tau, \tau)\, d\lambda

Taylor-expand ϵ^θ\hat\epsilon_\theta in λ\lambda to order kk, integrate the linear part exactly (exponential weight), and approximate the rest by order:

  • DPM-Solver-1 = DDIM (first-order)
  • DPM-Solver-2: 2 NFE per step, second-order
  • DPM-Solver-3: 3 NFE per step, third-order

10-15 NFE reaches the same quality as 50 NFE DDIM.

9.3 DPM-Solver++ (CFG-friendly variant, Lu et al. 2023)

The original DPM-Solver is unstable under CFG (ϵθ\epsilon_\theta amplified by CFG goes out-of-distribution, and Taylor-expansion error blows up). DPM-Solver++ switches to x0x_0-prediction:

xt=σtσsxs+σt∫λsλteλx^θ0(xτ,τ) dλx_t = \frac{\sigma_t}{\sigma_s} x_s + \sigma_t \int_{\lambda_s}^{\lambda_t} e^{\lambda} \hat x^0_\theta(x_\tau, \tau)\, d\lambda

(Using x0x_0-pred instead of ϵ\epsilon-pred keeps CFG amplification in a more stable regime.)

15-20 NFE under CFG=7 gives quality close to 100-NFE DDIM. One of the default samplers in SDXL / SD3.

9.4 Sampler comparison

💡 Common sampler cheat sheet — ordered by NFE / quality / compatibility (image generation).

  • DDPM ancestral: T=1000 steps, baseline; rare in modern use

  • DDIM (η=0\eta = 0): 50-100 NFE, simple and stable, supports interpolation

  • PLMS / PNDM: 50 NFE, linear-multistep, the old AUTOMATIC1111 default

  • EDM Heun: 18-35 NFE, deterministic 2nd-order ODE, the SOTA literature baseline

  • DPM-Solver / DPM-Solver++: 10-20 NFE, recommended by HuggingFace diffusers

  • UniPC (Zhao 2023): predictor-corrector framework, can beat DPM-Solver

  • Consistency Models (one-step / two-step): 1-4 NFE, requires distillation

§10 Conditioning: Classifier Guidance & CFG

10.1 Classifier Guidance (Dhariwal-Nichol 2021)

Train a separate classifier pϕ(c∣xt)p_\phi(c | x_t) on noisy data, then apply Bayes:

∇xtlog⁡p(xt∣c)=∇xtlog⁡p(xt)+∇xtlog⁡pϕ(c∣xt)\nabla_{x_t} \log p(x_t | c) = \nabla_{x_t} \log p(x_t) + \nabla_{x_t} \log p_\phi(c | x_t)

In practice we scale the classifier gradient by ww (controlling guidance strength):

ϵ~=ϵθ(xt,t)−w1−αˉt ∇xtlog⁡pϕ(c∣xt)\tilde\epsilon = \epsilon_\theta(x_t, t) - w \sqrt{1-\bar\alpha_t}\, \nabla_{x_t} \log p_\phi(c | x_t)

⚠️ Drawbacks of classifier guidance — (a) needs an extra noisy classifier (engineering overhead); (b) classifier gradients tend toward "adversarial" behavior, degenerating away from the training distribution; (c) unfriendly to continuous conditions like text-to-image. CFG fully supplants it.

10.2 Classifier-Free Guidance (Ho-Salimans 2022)

Training: with probability pdropp_\text{drop} (typically 0.1), replace cc with ∅\emptyset (null embedding), so the same net learns both conditional and unconditional:

LCFG(θ)=E[∥ϵ−ϵθ(xt,t,c or ∅)∥2]L_\text{CFG}(\theta) = \mathbb{E}\big[\|\epsilon - \epsilon_\theta(x_t, t, c \text{ or } \emptyset)\|^2\big]

Inference: call ww the guidance scale:

  ϵ~=ϵθ(xt,t,∅)+(1+w)[ϵθ(xt,t,c)−ϵθ(xt,t,∅)]  \boxed{\; \tilde\epsilon = \epsilon_\theta(x_t, t, \emptyset) + (1 + w)\big[\epsilon_\theta(x_t, t, c) - \epsilon_\theta(x_t, t, \emptyset)\big] \;}

Equivalent form (common in Imagen / SD implementations):

ϵ~=(1+w) ϵθ(xt,t,c)−w ϵθ(xt,t,∅)\tilde\epsilon = (1 + w)\, \epsilon_\theta(x_t, t, c) - w\, \epsilon_\theta(x_t, t, \emptyset)

⚠️ Two CFG ww conventions — the original Ho-Salimans 2022 paper has ϵ~=ϵuncond+(1+w)(ϵcond−ϵuncond)\tilde\epsilon = \epsilon_\text{uncond} + (1+w)(\epsilon_\text{cond} - \epsilon_\text{uncond}), so w=0w = 0 is unguided and w>0w > 0 is amplified. HuggingFace / SD UIs commonly use w′=w+1w' = w + 1, so w′=1w' = 1 is unguided and w′=7.5w' = 7.5 is the common amplified value. State the convention in interview code.

10.3 Geometric meaning of CFG

CFG is equivalent to pulling the sampling trajectory toward the "conditional gradient" direction:

∇xtlog⁡p(xt∣c)≈∇xtlog⁡p(xt)+w∇xtlog⁡p(xt∣c)p(xt)\nabla_{x_t} \log p(x_t | c) \approx \nabla_{x_t} \log p(x_t) + w \nabla_{x_t} \log \frac{p(x_t | c)}{p(x_t)}

The second term is the "conditional score difference", which pushes samples toward regions of high conditional likelihood and relatively low unconditional likelihood — intuitively "amplifying text alignment".

✅ CFG is the core of SD/SDXL/FLUX text-image alignment — w∈[3,7.5]w \in [3, 7.5] is the empirical sweet spot for Stable Diffusion; w>10w > 10 tends to over-saturate (color saturation, artifacts). FLUX internalizes CFG into distillation ("guidance-distilled") so a single forward pass yields the CFG effect — one of the keys to its inference speed.

§11 Production: from LDM to FLUX

11.1 Latent Diffusion (LDM, Rombach 2022 CVPR)

Core idea: run diffusion in VAE latent space rather than pixel space.

  1. Train a VAE E,DE, D: z=E(x),x^=D(z)z = E(x), \hat x = D(z), with zz ~8× smaller than xx (e.g., 5122×3→642×4512^2 \times 3 \to 64^2 \times 4)
  2. Train a diffusion model on zz (params, memory, and compute all drop by an order of magnitude)
  3. To generate: sample from zTz_T to z0z_0, then D(z0)D(z_0) decodes back to pixels

Stable Diffusion (SD) = LDM + CLIP text encoder + UNet on 64×64×464 \times 64 \times 4 latent — at the time the most practical open-source T2I model.

11.2 SDXL (Podell et al. 2023 arXiv / ICLR 2024 spotlight)

Main improvements from SD 1.5 to SDXL:

  • Larger UNet: params from ~860M to ~2.6B, more cross-attn layers
  • Two-stage architecture: base + refiner (the refiner handles low-noise detail)
  • Better text encoder: OpenCLIP ViT-bigG/14 + CLIP-L/14 concatenated
  • Multi-scale / multi-aspect-ratio training: native support for 1024×1024 + multiple aspect ratios
  • MicroConditioning: feed original resolution, crop offset, aspect ratio as conditions to the UNet

11.3 DiT (Peebles-Xie 2023 ICCV)

Replace the UNet with a pure Transformer:

  • Patch the latent (e.g., 2×22 \times 2) into a token sequence
  • Standard Transformer block (self-attn + MLP)
  • Inject conditioning via adaptive LayerNorm (adaLN): LN(x)⋅γ(c,t)+β(c,t)\text{LN}(x) \cdot \gamma(c, t) + \beta(c, t) with γ,β\gamma, \beta produced by an MLP on c,tc, t

DiT experiments: better scaling laws than the UNet, FID decreases steadily with parameters. SD3 / FLUX / Sora are all in the DiT family.

11.4 SD3 (Esser 2024 ICML) — diffusion replaced by Rectified Flow

Two key SD3 changes:

  1. Rectified Flow replaces DDPM: training objective becomes ∥vθ−(x1−x0)∥2\|v_\theta - (x_1 - x_0)\|^2 (FM framework)
  2. MM-DiT: multimodal DiT, where text tokens and image tokens attend to each other in the same Transformer (rather than via cross-attn)

Why switch to RF? Esser 2024 ablations: linear paths have straighter trajectories than cosine paths → better few-step sampling; logit-normal tt sampling emphasizes mid-noise and improves quality.

11.5 FLUX.1 (Black Forest Labs 2024)

Inherits SD3 + MM-DiT with major updates:

  • 12B parameters (open-source dev version)
  • Guidance-distilled: CFG distilled into a single forward, no 2× CFG forwards at inference
  • Adversarial training late-stage fine-tune (like SD3-Turbo / ADD), 4-step generation

11.6 ControlNet (Zhang 2023 ICCV)

Adds a trainable copy + zero-conv connections to a frozen SD UNet:

Original UNet (frozen)                   Control signal (canny / depth / pose)
     ↓                                       ↓
[encoder blocks]                      [trainable copy of encoder]
     ↓ ──────── zero-conv ──────────────────↓
[mid block]                           [trainable mid]
     ↓ ──────── zero-conv ──────────────────↓
[decoder blocks (frozen)]    +   [trainable copy outputs]
     ↓
   output

Zero-conv = 1×1 convolution initialized to zero → at start, ControlNet doesn't change the original UNet's output (preserving SD's capability); as training proceeds, it learns to apply condition control.

✅ ControlNet's training efficiency — the original UNet (most parameters) is frozen, only the trainable copy (~half the params) is trained; single-GPU trainable, which is key to the open-source ecosystem.

§12 Distillation: 1-step / Few-step generation

12.1 Progressive Distillation (Salimans-Ho 2022)

Iterative distillation: student one step ≈\approx teacher two steps; distill for log⁡2N\log_2 N rounds to compress NN steps to 1. Key: halve each round, keeping distribution drift controlled.

12.2 Consistency Models (Song 2023 ICML)

Idea: directly learn a network fθ(xt,t)f_\theta(x_t, t) such that for all tt:

fθ(xt,t)≈x0f_\theta(x_t, t) \approx x_0

i.e., the network is the consistency function of the probability flow ODE — any xtx_t maps to the corresponding x0x_0. One-step sampling: x0=fθ(xT,T)x_0 = f_\theta(x_T, T).

Training objective (Consistency Distillation, CD):

LCD(θ)=E[d(fθ(xtn+1,tn+1),  fθ−(x^tn,tn))]L_\text{CD}(\theta) = \mathbb{E}\left[d\big(f_\theta(x_{t_{n+1}}, t_{n+1}),\; f_{\theta^-}(\hat x_{t_n}, t_n)\big)\right]

where:

  • θ−\theta^- is an EMA target
  • x^tn\hat x_{t_n} is obtained by a teacher ODE solver one step from xtn+1x_{t_{n+1}} (xtn=ODE-step(xtn+1)x_{t_n} = \text{ODE-step}(x_{t_{n+1}}))
  • dd is a metric (L2 / LPIPS)

Boundary condition: requires fθ(xσmin,σmin)=xσminf_\theta(x_{\sigma_\text{min}}, \sigma_\text{min}) = x_{\sigma_\text{min}} (self-consistency at lowest noise) — enforced by EDM-style preconditioning:

fθ(x,σ)=cskip(σ)x+cout(σ)Fθ(x,σ)f_\theta(x, \sigma) = c_\text{skip}(\sigma) x + c_\text{out}(\sigma) F_\theta(x, \sigma)

with cskip,coutc_\text{skip}, c_\text{out} designed so that fθ≡xf_\theta \equiv x at σ=σmin\sigma = \sigma_\text{min}.

⚠️ CT (Consistency Training) vs CD (Consistency Distillation) — CT is fully from scratch (no teacher; apply consistency loss directly to x0+σnϵx_0 + \sigma_n \epsilon vs x0+σn+1ϵx_0 + \sigma_{n+1} \epsilon); CD distills with a pretrained teacher. Quality-wise CD > CT; recent ICT (Song 2024) brings CT close to CD.

12.3 LCM / LCM-LoRA (Luo 2023)

Latent Consistency Model: apply Consistency Models to latent diffusion (SD 1.5 / SDXL):

  • Teacher = pretrained SD (DDIM as ODE solver)
  • Student = LCM, 4-8 step generation

LCM-LoRA: package LCM training as a LoRA adapter — a single LoRA file lets any SD 1.5 / SDXL fine-tune generate in 4 steps. Huge ecosystem value: users don't need to swap base models.

12.4 Adversarial Diffusion Distillation: ADD (SDXL-Turbo) / LADD (SD3-Turbo) (Sauer 2023/2024)

ADD training objective:

LADD=Ladv(student)+λLdistill(student,teacher)L_\text{ADD} = L_\text{adv}(\text{student}) + \lambda L_\text{distill}(\text{student}, \text{teacher})

  • LadvL_\text{adv}: ADD (SDXL-Turbo) uses a pretrained vision model (DINOv2) as the discriminator; LADD (SD3-Turbo) instead uses the teacher diffusion-transformer's own latent features as the discriminator (no DINOv2; discrimination happens in latent space)
  • LdistillL_\text{distill}: student multi-step ODE should match teacher multi-step ODE

Results: SDXL-Turbo 1-step 1024 px, SD3-Turbo 4-step 1024 px. Quality slightly below multi-step but real-time (~100ms / image).

§13 The bridge to Flow Matching

13.1 Score vs vector field — same information, different parametrization

Inside the VP-SDE / VE-SDE framework, FM learning vv and score-based learning ss are two parametrizations of the same information:

vθ(t,x)=f(x,t)−12g2(t) sθ(t,x)v_\theta(t, x) = f(x, t) - \tfrac{1}{2} g^2(t)\, s_\theta(t, x)

Specifically on the VP (DDPM) path, with αt=αˉt,σt=1−αˉt\alpha_t = \sqrt{\bar\alpha_t}, \sigma_t = \sqrt{1-\bar\alpha_t}, the conditional vector field (same form as Salimans-Ho 2022 vv-prediction):

vθVP(t,xt)=αt′ x0+σt′ ϵv_\theta^\text{VP}(t, x_t) = \alpha_t'\, x_0 + \sigma_t'\, \epsilon

Substitute x0=(xt−σtϵ)/αtx_0 = (x_t - \sigma_t \epsilon)/\alpha_t and rearrange: vθv_\theta is simultaneously a linear combination of xtx_t and ϵ\epsilon (or score), the concrete expression depending on the time derivatives of αt,σt\alpha_t, \sigma_t.

In practice: for any Gaussian path with α(t),σ(t)\alpha(t), \sigma(t), the three quantities {ϵθ,sθ,vθ}\{\epsilon_\theta, s_\theta, v_\theta\} are fully equivalent. So training DDPM / score-based / FM on VP/VE paths is the same task.

13.2 Why did SD3 / FLUX switch to Rectified Flow?

Rectified Flow path: xt=(1−t)x0+tx1x_t = (1-t) x_0 + t x_1 (linear noise→data interpolation), vt=x1−x0v_t = x_1 - x_0.

AdvantageRF (linear)VP/VE (curved)
ODE trajectorystraight linecurved (needs higher-order solver)
Target vtv_tdoesn't depend on ttdepends on tt (VP cosine path)
Few-step samplingEuler works at 4-8 stepsEuler needs 30+ steps
Reflow compressible to 1-2 steps✓ (InstaFlow / SD3-Turbo)✗
Training stabilitystable with logit-normal tt + RFrequires careful noise schedule

💡 One-sentence SD3 ablation conclusion — "Under the same DiT backbone, RF + logit-normal tt improves FID on ImageNet 256 by about 0.5-1.0 over VP + uniform tt; on T2I tasks, GenEval text alignment is significantly better."

13.3 DDPM/DDIM/EDM/RF/CM full picture

                Training objective    Sampling                Typical NFE
                ─────────             ─────────              ───────
 DDPM         ε-pred (MSE)        ancestral / DDIM            1000 / 50
 Score SDE    score (DSM)         reverse SDE / PF-ODE        500 / 30
 DDIM         (uses DDPM weights)  deterministic ODE step     20-50
 EDM          D_θ (Tweedie)       Heun ODE 2nd-order         18-35
 RF / SD3     v = x_1-x_0         Euler ODE                  4-50
 FLUX         v + CFG-distill     Euler                      1-4
 ConsistMod   f_θ(x_t,t)→x_0      direct map                 1-4
 LCM-LoRA     consistency on SD   direct                     4-8

§14 25 frequently-asked interview questions (L1 must-know · L2 intermediate · L3 top lab)

L1 must-know (likely on any ML role with diffusion)

Q1. Write out DDPM's forward $q(x_t | x_0)$ and reverse $p_\theta(x_{t-1}|x_t)$.
  • Forward closed form: q(xt∣x0)=N(αˉtx0,(1−αˉt)I)q(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t)I), αˉt=∏s=1t(1−βs)\bar\alpha_t = \prod_{s=1}^t (1-\beta_s)

  • Reverse parametrization: pθ(xt−1∣xt)=N(μθ(xt,t),Σθ)p_\theta(x_{t-1}|x_t) = \mathcal{N}(\mu_\theta(x_t, t), \Sigma_\theta)

  • μθ=1αt(xt−βt1−αˉtϵθ(xt,t))\mu_\theta = \frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{\beta_t}{\sqrt{1-\bar\alpha_t}} \epsilon_\theta(x_t, t)\right) (ϵ\epsilon-prediction)

Writing the wrong symbols (e.g., confusing αt\sqrt{\alpha_t} with αˉt\sqrt{\bar\alpha_t}); forgetting that αˉ\bar\alpha is the cumulative product.

Q2. How does DDPM's ELBO simplify to $L_\text{simple}$?
  • ELBO splits into LT+∑Lt−1+L0L_T + \sum L_{t-1} + L_0; LTL_T is a constant (prior matching)

  • Lt−1=KL(q(xt−1∣xt,x0) ∥ pθ)L_{t-1} = \text{KL}(q(x_{t-1}|x_t, x_0) \,\Vert\, p_\theta); both are Gaussians, KL is closed-form

  • Substitute x0=(xt−1−αˉtϵ)/αˉtx_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon)/\sqrt{\bar\alpha_t} into μ~\tilde\mu and μθ\mu_\theta, yielding Lt−1=const⋅E∥ϵ−ϵθ∥2L_{t-1} = \text{const} \cdot \mathbb{E}\|\epsilon - \epsilon_\theta\|^2

  • Ho 2020 drops the coefficient to get Lsimple=E∥ϵ−ϵθ∥2L_\text{simple} = \mathbb{E}\|\epsilon - \epsilon_\theta\|^2

Saying only "LsimpleL_\text{simple} predicts noise" without the derivation; not knowing dropping the coefficient equals SNR-weighting.

Q3. Why does $L_\text{simple}$ still work after dropping the coefficient?
  • The ELBO coefficient βt2/[2σt2αt(1−αˉt)]\beta_t^2 / [2\sigma_t^2 \alpha_t (1-\bar\alpha_t)] is large at small tt (high SNR) and small at large tt (low SNR)

  • Dropping the coefficient is equivalent to relatively upweighting low-SNR (large tt) — these are the steps that "determine semantic structure"

  • Empirically: unweighted FID is significantly better than ELBO-weighted

  • Cost: no longer a lower bound on log⁡p\log p (FID ≠ likelihood)

Not knowing the cost is a likelihood vs sample-quality trade-off.

Q4. Linear vs cosine schedule?
  • Linear: βt∈[10−4,0.02]\beta_t \in [10^{-4}, 0.02] linear interpolation, DDPM original

  • Issue: end-SNR isn't low enough (αˉT≈4×10−5\bar\alpha_T \approx 4\times 10^{-5}); the middle region adds noise too fast

  • Cosine: αˉt=cos⁡2(π(t/T+s)/(2(1+s)))\bar\alpha_t = \cos^2(\pi(t/T + s)/(2(1+s))), s=0.008s=0.008, end-SNR ≈ 0

  • Empirically: cosine improves FID on ImageNet 64 by about 20% (Nichol-Dhariwal 2021)

Saying only "cosine is better" without writing the formula; forgetting that the s=0.008s=0.008 offset is to keep β1\beta_1 from being near 0.

Q5. How do $\epsilon$-pred / $x_0$-pred / $v$-pred / score interconvert?
  • Given xt=αˉtx0+1−αˉtϵx_t = \sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t} \epsilon, all quantities are linearly invertible

  • x^0=(xt−1−αˉtϵθ)/αˉt\hat x_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon_\theta) / \sqrt{\bar\alpha_t}

  • v=αˉtϵ−1−αˉtx0v = \sqrt{\bar\alpha_t}\epsilon - \sqrt{1-\bar\alpha_t} x_0 (Salimans-Ho 2022)

  • s=−ϵ/1−αˉts = -\epsilon / \sqrt{1-\bar\alpha_t} (from Tweedie or ∇xtlog⁡q(xt∣x0)\nabla_{x_t} \log q(x_t|x_0))

Not knowing the four predictions are different parametrizations of the same information; confusing vv with velocity.

Q6. DDIM vs DDPM differences?
  • DDPM ancestral is a stochastic Markov chain, adding σtz\sigma_t z noise per step, must walk the full TT steps

  • DDIM uses a non-Markovian forward that shares the same q(xt∣x0)q(x_t|x_0) as DDPM — can directly use DDPM training weights

  • η=0\eta = 0 is deterministic and supports interpolation; η=1\eta = 1 over the full TT steps degenerates to DDPM ancestral (with skips it only matches variance order)

  • DDIM allows skip steps: 50 steps ≈ DDPM 1000-step quality

Saying only "DDIM is a few-step DDPM" without knowing marginals are equivalent; or not knowing η\eta controls stochasticity.

Q7. How is CFG (Classifier-Free Guidance) trained and used?
  • Training: with pdrop=0.1p_\text{drop}=0.1 replace cc with ∅\emptyset (null embedding); the same net learns conditional/unconditional

  • Inference: two conventions (must distinguish):

    • HF / SD style (denoted ss): ϵ~=ϵθ(x,∅)+s [ϵθ(x,c)−ϵθ(x,∅)]\tilde\epsilon = \epsilon_\theta(x,\emptyset) + s\,[\epsilon_\theta(x,c) - \epsilon_\theta(x,\emptyset)], s=1s=1 is unguided, s∈[3,7.5]s\in[3, 7.5] is the common SD amplification
    • Ho-Salimans 2022 original (denoted ww): ϵ~=(1+w) ϵθ(x,c)−w ϵθ(x,∅)\tilde\epsilon = (1+w)\,\epsilon_\theta(x,c) - w\,\epsilon_\theta(x,\emptyset), w=0w=0 is unguided; equivalent to s=w+1s = w + 1
  • Large ss → strong text alignment but lower diversity; s>10s>10 → over-saturated colors

Writing only the formula without knowing the ww/ss convention; not knowing the drop-cc training trick; saying CFG needs a separate classifier (that's classifier guidance).

Q8. What is Tweedie's formula? Why is it important?
  • E[x0∣xt]=xt+σt2∇xtlog⁡pt(xt)\mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2 \nabla_{x_t} \log p_t(x_t) (VE view; the VP version has an α\alpha factor)

  • Derivation: take ∇xtlog⁡\nabla_{x_t} \log of pt(xt)=∫p0(x0)N(xt;x0,σt2I)dx0p_t(x_t) = \int p_0(x_0) \mathcal{N}(x_t; x_0, \sigma_t^2 I) dx_0

  • Significance: the denoiser's optimal output = input + scaled score — the "Rosetta Stone" of conversions among ϵ,x0,v,s\epsilon, x_0, v, s

Memorizing the formula without deriving it; not knowing it links score and denoiser.

Q9. VP-SDE vs VE-SDE?
  • VP (variance preserving): dx=−12β(t)x dt+β(t) dWdx = -\frac{1}{2}\beta(t) x\, dt + \sqrt{\beta(t)}\, dW, corresponds to DDPM; Var[xt]≤1\text{Var}[x_t] \le 1

  • VE (variance exploding): dx=dσ2/dt dWdx = \sqrt{d\sigma^2/dt}\, dW, corresponds to SMLD/EDM; Var[xt]\text{Var}[x_t] grows to σmax2\sigma_\text{max}^2

  • VP: xT≈N(0,I)x_T \approx \mathcal{N}(0, I); VE: xT≈N(x0,σmax2I)x_T \approx \mathcal{N}(x_0, \sigma_\text{max}^2 I), prior is N(0,σmax2I)\mathcal{N}(0, \sigma_\text{max}^2 I)

  • EDM picks VE because preconditioning is cleaner to derive; DDPM picks VP because the N(0,I)\mathcal{N}(0,I) prior is natural

Saying only "variance preserving / exploding" without writing the SDE; not knowing EDM is VE.

Q10. What is the probability flow ODE?
  • For any forward SDE dx=fdt+gdWdx = f dt + g dW, there exists a deterministic ODE dx/dt=f−12g2∇log⁡ptdx/dt = f - \frac{1}{2}g^2 \nabla \log p_t that shares all marginals ptp_t

  • Note: the reverse SDE's drift is f−g2∇log⁡ptf - g^2 \nabla \log p_t (full score correction), while PF-ODE uses only 12g2\frac{1}{2} g^2; not simply "reverse SDE minus the stochastic term"

  • Practical significance: lets you sample in few steps with an ODE solver (DDIM, Heun, RK4, DPM-Solver)

  • Bridge between score-based and Flow Matching: vt=f−12g2sv_t = f - \frac{1}{2}g^2 s

Knowing only the formula without realizing PF-ODE and reverse SDE drift differ by half; not knowing it enables deterministic sampling.

L2 intermediate (research-oriented · need to know diffusion details)

Q11. What is the EDM preconditioning unit-variance argument?
  • Make the network FθF_\theta's input cinxc_\text{in} x have variance 1: cin=1/σdata2+σ2c_\text{in} = 1/\sqrt{\sigma_\text{data}^2 + \sigma^2}

  • Make the effective target F∗=(D∗−cskipx)/coutF^* = (D^* - c_\text{skip} x)/c_\text{out} have variance 1: cskip=σdata2/(σ2+σdata2)c_\text{skip} = \sigma_\text{data}^2/(\sigma^2+\sigma_\text{data}^2), cout=σσdata/σ2+σdata2c_\text{out} = \sigma \sigma_\text{data} / \sqrt{\sigma^2 + \sigma_\text{data}^2}

  • Intuition: at σ→0\sigma \to 0, cskip→1c_\text{skip} \to 1 (identity); at σ→∞\sigma \to \infty, cout→σdatac_\text{out} \to \sigma_\text{data} (all from net)

  • Effect: loss magnitudes are uniform across all σ\sigma, training is more stable

Memorizing the formula without knowing the reason; not knowing σdata\sigma_\text{data} is data std (around 0.5 for normalized images).

Q12. Benefit of Improved DDPM learning $\Sigma_\theta$?
  • DDPM fixes Σθ=βtI\Sigma_\theta = \beta_t I or β~tI\tilde\beta_t I

  • Nichol-Dhariwal 2021 learn Σθ\Sigma_\theta as an interpolation between βt\beta_t and β~t\tilde\beta_t: Σθ=exp⁡(vlog⁡βt+(1−v)log⁡β~t)\Sigma_\theta = \exp(v \log\beta_t + (1-v) \log\tilde\beta_t)

  • Benefit: few-step sampling quality improves dramatically (50 steps reach 1000-step fixed-Σ\Sigma quality)

  • Hybrid loss Lhybrid=Lsimple+0.001⋅LvlbL_\text{hybrid} = L_\text{simple} + 0.001 \cdot L_\text{vlb} (LvlbL_\text{vlb} provides learning signal for Σθ\Sigma_\theta)

  • λ=0.001\lambda = 0.001 prevents LvlbL_\text{vlb} from dominating

Not knowing the hybrid loss; thinking Σθ\Sigma_\theta learning mainly affects training likelihood (actually it's few-step sampling gains).

Q13. Core differences between DPM-Solver and DDIM?
  • DDIM is first-order Euler, 1 NFE per step

  • DPM-Solver exploits the diffusion ODE's semi-linear structure dx/dt=f(t)x+g(t)ϵθdx/dt = f(t) x + g(t) \epsilon_\theta, integrates the linear part exactly (exponential integrator)

  • Taylor-expands the nonlinear part (ϵθ\epsilon_\theta) in log-SNR λ\lambda to order kk

  • DPM-Solver-2: 2 NFE per step, second-order; DPM-Solver-3: 3 NFE per step, third-order

  • 10-15 NFE reaches DDIM 50 NFE quality

  • DPM-Solver++ switches to x0x_0-pred, CFG-friendly

Not knowing the exponential integrator; thinking DPM-Solver is some approximation (actually it's a more precise mathematical expansion).

Q14. Consistency Models' training objective? How to achieve 1-step?
  • Objective: fθ(xt,t)≈x0f_\theta(x_t, t) \approx x_0 for all tt

  • Consistency loss: d(fθ(xtn+1,tn+1),fθ−(x^tn,tn))d(f_\theta(x_{t_{n+1}}, t_{n+1}), f_{\theta^-}(\hat x_{t_n}, t_n)) with x^tn\hat x_{t_n} from one teacher ODE step

  • θ−\theta^- is EMA, like BYOL; metric dd = L2 + LPIPS

  • Boundary: fθ(x,σmin)≡xf_\theta(x, \sigma_\text{min}) \equiv x, enforced via EDM-style cskip,coutc_\text{skip}, c_\text{out}

  • 1-step sampling: x0=fθ(xT,T)x_0 = f_\theta(x_T, T)

  • 2-step variant: first x0=fθ(xT,T)x_0 = f_\theta(x_T, T), then re-noise to intermediate tt, then fθf_\theta again

Saying only "learn the xt→x0x_t \to x_0 map" without defining the consistency constraint; not knowing about EMA target / teacher ODE / boundary.

Q15. Why did SD3 switch from DDPM to Rectified Flow?
  • The RF path xt=(1−t)x0+tx1x_t = (1-t)x_0 + tx_1 is straight → straight ODE trajectory → small few-step sampling error

  • vt=x1−x0v_t = x_1 - x_0 target doesn't depend on tt (given (x0,x1)(x_0, x_1)), numerically stable

  • Combined with logit-normal tt sampling (concentrated at t=0.5t=0.5) for gains

  • Esser 2024 ablation: same backbone, RF + LogitNorm vs VP-cosine + Uniform → GenEval text alignment significantly better

  • Further reflow can compress to 4 steps (FLUX-Schnell / SD3-Turbo)

Saying only "RF is more stable" without knowing it's because the path is straight; not knowing logit-normal is an additional trick.

Q16. How does DiT inject conditioning? adaLN vs cross-attn?
  • adaLN-Zero (DiT default): MLP c,tc, t → γ,β,α\gamma, \beta, \alpha; out=α⋅block(LN(x)⋅γ+β)+x\text{out} = \alpha \cdot \text{block}(\text{LN}(x) \cdot \gamma + \beta) + x; initialize α=0\alpha=0 (zero-init) so the initial DiT block doesn't alter the input

  • Cross-attn: image tokens as Q, text/condition as K/V

  • Token-concat (MM-DiT, SD3): text tokens and image tokens concatenated into one sequence; all tokens attend to each other

  • Empirically: adaLN-Zero scales best (DiT paper); cross-attn has strong text control (SD UNet); MM-DiT is overall best (SD3 / FLUX)

Knowing only cross-attn; not knowing adaLN-Zero's "zero-init" is the key trick.

Q17. What is ControlNet's zero-conv? Why is it necessary?
  • 1×1 conv with weights initialized to 0 and bias also 0

  • At training start, the trainable copy's output passes through zero-conv → 0, original UNet output unchanged → preserves SD pretrained capability

  • As training proceeds, zero-conv learns non-zero weights and gradually injects condition control

  • Why not random init: it would perturb the frozen UNet's intermediate features and destroy the pretrained representation

Saying only "add a ControlNet module" without knowing zero-conv; thinking zero-conv is a special 1×1 conv variant (it's just the initialization).

Q18. SDE vs ODE sampling trade-off?
  • SDE: reverse SDE contains a stochastic term g(t)dWˉg(t) d\bar W; injects new noise each step, can correct early errors

  • ODE (probability flow): deterministic; solver error accumulates with no way back

  • SDE typically gives better FID; ODE has lower NFE + is deterministic (supports interpolation)

  • EDM compromise: base ODE Heun + small stochastic churn (slight re-noising at each step start), improving FID by 0.1-0.3 over pure ODE

Saying only "SDE is stochastic, ODE is deterministic" without knowing the trade-off; not knowing EDM churn.

Q19. LCM vs SDXL-Turbo differences?
  • LCM: Consistency Distillation applied to latent diffusion, 4-8 step; pure distillation loss

  • LCM-LoRA: LCM training packaged as a LoRA adapter, applicable to any SD 1.5 / SDXL fine-tune

  • SDXL-Turbo (ADD): adversarial loss + distill loss, 1-4 step; uses DINOv2 as discriminator

  • LCM is stabler, ADD is sharper (adversarial gives clearer textures)

  • LCM is open-source earlier with a more complete ecosystem; Turbo requires BFL/SAI in-house training

Not knowing LCM-LoRA's "LoRA-compat" is the killer feature; thinking Turbo = LCM.

Q20. How is training noise level $\sigma$ sampled?
  • DDPM: t∼U{1,…,T}t \sim \mathcal{U}\{1, \dots, T\}, discrete uniform

  • EDM: ln⁡σ∼N(Pmean,Pstd2)\ln \sigma \sim \mathcal{N}(P_\text{mean}, P_\text{std}^2), Pmean=−1.2,Pstd=1.2P_\text{mean}=-1.2, P_\text{std}=1.2, concentrated around σ≈0.3\sigma \approx 0.3

  • SD3 / RF: t=sigmoid(τ),τ∼N(0,1)t = \text{sigmoid}(\tau), \tau \sim \mathcal{N}(0, 1), concentrated at t=0.5t = 0.5

  • Common idea: mid-noise is hardest to learn, so more sampling in the middle region gives gains

Saying only "uniform sampling" without knowing EDM/SD3 switched to normal/logit-normal; not knowing why mid-concentration.

L3 top-lab diffusion / video direction (deep derivation + distillation + production integration)

Q21. Derive $L_\text{simple} = \|\epsilon - \epsilon_\theta\|^2$ from the ELBO, listing every intermediate approximation and dropped term.

Derivation chain + approximation log:

  • Step 1 (exact, no approximation): log⁡pθ(x0)≥Eq[log⁡pθ(x0:T)/q(x1:T∣x0)]\log p_\theta(x_0) \ge \mathbb{E}_q[\log p_\theta(x_{0:T})/q(x_{1:T}|x_0)] — Jensen's inequality gives the variational lower bound

  • Step 2 (LTL_T treated as a constant and ignored): split ELBO into L=LT+∑t=2TLt−1+L0L = L_T + \sum_{t=2}^T L_{t-1} + L_0. LT=KL(q(xT∣x0) ∥ p(xT))L_T = \text{KL}(q(x_T|x_0)\,\lVert\, p(x_T)) — not strictly 0, but near-constant when αˉT≈0\bar\alpha_T \approx 0

  • Step 3 (L0L_0 ignored / merged): L0=−E[log⁡pθ(x0∣x1)]L_0 = -\mathbb{E}[\log p_\theta(x_0 | x_1)] — small contribution; in practice often modeled by a discretized Gaussian decoder and merged into L1L_1 during training

  • Step 4 (KL is closed-form; with fixed Σθ\Sigma_\theta, the constant CC is dropped): Lt−1=Eq[KL(q(xt−1∣xt,x0) ∥ pθ(xt−1∣xt))]L_{t-1} = \mathbb{E}_q[\text{KL}(q(x_{t-1}|x_t, x_0) \,\lVert\, p_\theta(x_{t-1}|x_t))]. Both are Gaussian → closed-form KL. If Σθ=σt2I\Sigma_\theta = \sigma_t^2 I is fixed:

Lt−1=E[12σt2∥μ~t(xt,x0)−μθ(xt,t)∥2]+CL_{t-1} = \mathbb{E}\left[\frac{1}{2\sigma_t^2}\|\tilde\mu_t(x_t, x_0) - \mu_\theta(x_t, t)\|^2\right] + C

The constant CC comes from the Σ\Sigma log-determinant, independent of θ\theta when Σ\Sigma is fixed, so it vanishes under the gradient.

  • Step 5 (exact rewrite into ϵ\epsilon-pred form): substitute x0=(xt−1−αˉtϵ)/αˉtx_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon)/\sqrt{\bar\alpha_t} into μ~t\tilde\mu_t and write μθ\mu_\theta in the same ϵ\epsilon-pred parametrization:

Lt−1=E[βt22σt2αt(1−αˉt)∥ϵ−ϵθ(xt,t)∥2]L_{t-1} = \mathbb{E}\left[\frac{\beta_t^2}{2\sigma_t^2 \alpha_t (1-\bar\alpha_t)} \|\epsilon - \epsilon_\theta(x_t, t)\|^2\right]

Exact, as long as μθ\mu_\theta uses Ho 2020's ϵ\epsilon-pred form.

  • Step 6 (drop the tt-dependent coefficient): LsimpleL_\text{simple} sets the coefficient βt22σt2αt(1−αˉt)\frac{\beta_t^2}{2\sigma_t^2 \alpha_t (1-\bar\alpha_t)} uniformly to 1. Equivalent to reweighting across tt — at small tt (high SNR) the original coefficient is large → simple downweights it relatively; at large tt (low SNR) the original coefficient is small → simple upweights it.

  • Step 7 (tt switched to uniform sampling): discrete tt becomes t∼U{1,…,T}t \sim \mathcal{U}\{1,\dots,T\}, uniform over all timesteps, not weighted by ELBO term magnitudes.

Final:

Lsimple=Et∼U{1,…,T}, x0, ϵ[∥ϵ−ϵθ(αˉtx0+1−αˉtϵ,t)∥2]L_\text{simple} = \mathbb{E}_{t \sim \mathcal{U}\{1,\dots,T\},\, x_0,\, \epsilon}\big[\|\epsilon - \epsilon_\theta(\sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t}\epsilon, t)\|^2\big]

Cost:

  • No longer a lower bound on log⁡p\log p (FID improves but likelihood evaluation no longer directly corresponds)
  • LTL_T and L0L_0 are implicitly dropped
  • Σθ\Sigma_\theta information lost (Improved DDPM restores it via LvlbL_\text{vlb})

Not knowing which terms are dropped; thinking LsimpleL_\text{simple} comes directly from the KL; ignoring the role of LT,L0L_T, L_0.

Q22. Prove DDIM ($\eta=0$) shares the same marginal $q(x_t|x_0)$ as DDPM.

Statement: DDIM defines a non-Markov forward qσ(x1:T∣x0)q_\sigma(x_{1:T}|x_0) such that qσ(xt∣x0)=N(αˉtx0,(1−αˉt)I)q_\sigma(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I) — identical to DDPM.

Proof (by induction):

  • Boundary qσ(xT∣x0)=N(αˉTx0,(1−αˉT)I)q_\sigma(x_T|x_0) = \mathcal{N}(\sqrt{\bar\alpha_T} x_0, (1-\bar\alpha_T) I) — holds directly by DDIM definition

  • Assume qσ(xt∣x0)=N(αˉtx0,(1−αˉt)I)q_\sigma(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I). DDIM defines:

qσ(xt−1∣xt,x0)=N ⁣(αˉt−1x0+1−αˉt−1−σt2⋅xt−αˉtx01−αˉt,  σt2I)q_\sigma(x_{t-1}|x_t, x_0) = \mathcal{N}\!\left(\sqrt{\bar\alpha_{t-1}} x_0 + \sqrt{1-\bar\alpha_{t-1} - \sigma_t^2}\cdot \frac{x_t - \sqrt{\bar\alpha_t} x_0}{\sqrt{1-\bar\alpha_t}},\; \sigma_t^2 I\right)

  • Compute qσ(xt−1∣x0)=∫qσ(xt−1∣xt,x0)qσ(xt∣x0) dxtq_\sigma(x_{t-1}|x_0) = \int q_\sigma(x_{t-1}|x_t, x_0) q_\sigma(x_t|x_0)\, dx_t (marginalize two Gaussians)

  • Apply Gaussian marginalization: if xt∣x0∼N(μt,Σt)x_t | x_0 \sim \mathcal{N}(\mu_t, \Sigma_t) and xt−1∣xt,x0∼N(Axt+b,Σt−1∣t)x_{t-1}|x_t, x_0 \sim \mathcal{N}(A x_t + b, \Sigma_{t-1|t}), then:

xt−1∣x0∼N ⁣(Aμt+b,  AΣtA⊤+Σt−1∣t)x_{t-1}|x_0 \sim \mathcal{N}\!\left(A \mu_t + b,\; A \Sigma_t A^\top + \Sigma_{t-1|t}\right)

  • Here A=1−αˉt−1−σt2/1−αˉtA = \sqrt{1-\bar\alpha_{t-1}-\sigma_t^2}/\sqrt{1-\bar\alpha_t}, b=αˉt−1x0−Aαˉtx0b = \sqrt{\bar\alpha_{t-1}} x_0 - A \sqrt{\bar\alpha_t} x_0. Substitute:

    • Mean = αˉt−1x0+Aαˉtx0−Aαˉtx0=αˉt−1x0\sqrt{\bar\alpha_{t-1}} x_0 + A \sqrt{\bar\alpha_t} x_0 - A \sqrt{\bar\alpha_t} x_0 = \sqrt{\bar\alpha_{t-1}} x_0

    • Variance = A2(1−αˉt)+σt2=(1−αˉt−1−σt2)+σt2=1−αˉt−1A^2 (1-\bar\alpha_t) + \sigma_t^2 = (1-\bar\alpha_{t-1}-\sigma_t^2) + \sigma_t^2 = 1 - \bar\alpha_{t-1}

  • So qσ(xt−1∣x0)=N(αˉt−1x0,(1−αˉt−1)I)q_\sigma(x_{t-1}|x_0) = \mathcal{N}(\sqrt{\bar\alpha_{t-1}} x_0, (1-\bar\alpha_{t-1}) I) — identical to DDPM □\square

Significance: DDIM can directly use a DDPM-trained ϵθ\epsilon_\theta because training looks only at marginals q(xt∣x0)q(x_t|x_0), which agree; the sampling paths differ (deterministic vs stochastic).

Not knowing the Gaussian marginalization theorem; not seeing that the proof's key is A2(1−αˉt)+σt2=1−αˉt−1A^2(1-\bar\alpha_t) + \sigma_t^2 = 1-\bar\alpha_{t-1}.

Q23. Derive $c_\text{skip}$ and $c_\text{out}$ in EDM preconditioning.

Setup: VE view x=x0+σϵx = x_0 + \sigma \epsilon, ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0, I), Var[x0]=σdata2\text{Var}[x_0] = \sigma_\text{data}^2. Denoiser parametrization:

Dθ(x;σ)=cskip(σ)x+cout(σ)Fθ(cinx,cnoise)D_\theta(x; \sigma) = c_\text{skip}(\sigma) x + c_\text{out}(\sigma) F_\theta(c_\text{in} x, c_\text{noise})

Effective target for FθF_\theta:

F∗(x0,σ,ϵ)=1cout(σ)[x0−cskip(σ)x]=1cout[(1−cskip)x0−cskipσϵ]F^*(x_0, \sigma, \epsilon) = \frac{1}{c_\text{out}(\sigma)}\big[x_0 - c_\text{skip}(\sigma) x\big] = \frac{1}{c_\text{out}}\big[(1 - c_\text{skip}) x_0 - c_\text{skip} \sigma \epsilon\big]

Goal: find cskip,coutc_\text{skip}, c_\text{out} so that Var[F∗]\text{Var}[F^*] (expectation over x0,ϵx_0, \epsilon) = 1.

Var[F∗]=1cout2[(1−cskip)2σdata2+cskip2σ2]=1\text{Var}[F^*] = \frac{1}{c_\text{out}^2}\big[(1-c_\text{skip})^2 \sigma_\text{data}^2 + c_\text{skip}^2 \sigma^2\big] = 1

But normalization alone is non-unique. Second criterion (Karras 2022): minimize the "residual" the network must learn (minimize coutc_\text{out}, because a larger coutc_\text{out} amplifies both FF and its error). Equivalent problem:

min⁡cskip    cout2(cskip)=(1−cskip)2σdata2+cskip2σ2\min_{c_\text{skip}}\;\; c_\text{out}^2(c_\text{skip}) = (1-c_\text{skip})^2 \sigma_\text{data}^2 + c_\text{skip}^2 \sigma^2

Differentiate w.r.t. cskipc_\text{skip} and set to 0:

−2(1−cskip)σdata2+2cskipσ2=0⇒cskip=σdata2σdata2+σ2-2(1 - c_\text{skip}) \sigma_\text{data}^2 + 2 c_\text{skip} \sigma^2 = 0 \quad \Rightarrow \quad c_\text{skip} = \frac{\sigma_\text{data}^2}{\sigma_\text{data}^2 + \sigma^2}

Substitute back into the Var[F∗]=1\text{Var}[F^*] = 1 constraint:

cout2=(1−cskip)2σdata2+cskip2σ2=σ4σdata2(σ2+σdata2)2+σdata4σ2(σ2+σdata2)2=σ2σdata2σ2+σdata2c_\text{out}^2 = (1-c_\text{skip})^2 \sigma_\text{data}^2 + c_\text{skip}^2 \sigma^2 = \frac{\sigma^4 \sigma_\text{data}^2}{(\sigma^2+\sigma_\text{data}^2)^2} + \frac{\sigma_\text{data}^4 \sigma^2}{(\sigma^2+\sigma_\text{data}^2)^2} = \frac{\sigma^2 \sigma_\text{data}^2}{\sigma^2 + \sigma_\text{data}^2}

  cout(σ)=σ⋅σdataσ2+σdata2  \boxed{\; c_\text{out}(\sigma) = \frac{\sigma \cdot \sigma_\text{data}}{\sqrt{\sigma^2 + \sigma_\text{data}^2}} \;}

Input normalization: cin(σ)=1/σdata2+σ2c_\text{in}(\sigma) = 1/\sqrt{\sigma_\text{data}^2 + \sigma^2} makes Var[cinx]=1\text{Var}[c_\text{in} x] = 1.

Conclusion: the four cc functions are fully determined by σdata\sigma_\text{data}, no tunable parameters (in practice σdata\sigma_\text{data} is computed from data; about 0.5 for normalized images).

Memorizing the formula without derivation; not knowing cskipc_\text{skip} is derived by minimizing coutc_\text{out}; thinking the cc functions have free parameters.

Q24. Consistency Distillation training procedure? Why is the EMA target $\theta^-$ needed?

Procedure:

  1. Take a pretrained teacher diffusion ϵϕ\epsilon_\phi + its PF-ODE solver (e.g., EDM Heun)

  2. Take a noise schedule σ1>σ2>⋯>σN=σmin\sigma_1 > \sigma_2 > \dots > \sigma_N = \sigma_\text{min} (typically N=18N = 18)

  3. Train student fθ(xσ,σ)→x0f_\theta(x_\sigma, \sigma) \to x_0, initializing θ=ϕ\theta = \phi (warm start)

  4. Each batch:

    • Sample x0x_0, σn\sigma_n (uniformly n∈{1,…,N−1}n \in \{1, \dots, N-1\})
    • Add noise: xσn+1=x0+σn+1ϵx_{\sigma_{n+1}} = x_0 + \sigma_{n+1} \epsilon
    • Teacher ODE one-step solve: from xσn+1x_{\sigma_{n+1}} run one Heun step with teacher ϵϕ\epsilon_\phi to get x^σn\hat x_{\sigma_n}
    • Loss: d(fθ(xσn+1,σn+1),fθ−(x^σn,σn))d(f_\theta(x_{\sigma_{n+1}}, \sigma_{n+1}), f_{\theta^-}(\hat x_{\sigma_n}, \sigma_n))
  5. Update θ\theta; EMA-update θ−←μθ−+(1−μ)θ\theta^- \leftarrow \mu \theta^- + (1-\mu)\theta

Why is the EMA target needed?

  • Directly using θ=θ−\theta = \theta^- has a trivial solution: fθ≡constf_\theta \equiv \text{const} also satisfies consistency

  • EMA θ−\theta^- lags θ\theta, providing a "stable" target so the student doesn't chase its own moving target

  • Analogous to BYOL / MoCo self-supervised setups

  • μ=0.999∼0.99995\mu = 0.999 \sim 0.99995 (depending on training steps)

Recent improvement (iCT, Song-Dhariwal 2024): remove EMA teacher (compute target with the same θ\theta, no θ−\theta^-), use pseudo-Huber loss, combine with lognormal noise schedule + curriculum-increasing discretization steps; CT approaches CD quality.

Not knowing the trivial solution; thinking EMA is just an engineering stability trick; not knowing the teacher's role.

Q25. Why can SD3 / FLUX be compressed to 4-step / 1-step generation?

Core pathway: RF (linear path) + Reflow + Distill. Step-by-step:

  1. RF makes trajectories straight — xt=(1−t)x0+tx1x_t = (1-t)x_0 + tx_1, the ODE solution's "ideal curve" is a straight line (linear interpolation), so first-order Euler with long steps has small error (contrast: cosine paths have high curvature near mid-tt)

  2. Reflow makes trajectories even straighter — after first training, run ODE to get coupled (x0,x1)(x_0, x_1), then train again; the trajectory converges closer to a straight line. Liu 2022 proves reflow monotonically reduces transport cost

  3. CFG-distillation — distill CFG's 2× forward (cond + uncond) into a single forward (FLUX does this); halves NFE

  4. Adversarial distillation (ADD) — SD3-Turbo / SDXL-Turbo late-stage fine-tune with DINOv2 discriminator + distill loss, 4-step approaches 30-step quality

Vs the DDPM route: DDPM trajectories have high curvature at mid-tt (cosine path), Euler first-order is unusable below 5 steps; you need DPM-Solver-2 second-order + consistency distillation to get to 4 steps. RF is much more engineering-friendly — a first-order sampler suffices.

FLUX-Schnell's 1-step: RF + reflow + heavy distillation; 1024px single forward generation, ~100ms/image. Cost: slightly reduced controllability / diversity; prompt-following accuracy slightly below multi-step.

Saying only "RF is faster than DDPM" without knowing why; not knowing reflow + distill are two-pronged; thinking FLUX 1-step is solely due to RF (in fact distillation also matters).

§A Appendix: Core PyTorch code (from scratch)

⚠️ Pedagogical version — emphasizes the math; for production use diffusers / EDM official implementations, which include mixed precision / EMA / DDP / VAE / xformers / fused kernels.

A.1 DDPM forward q(xt∣x0)q(x_t | x_0) + simplified loss

import math import torch import torch.nn as nn import torch.nn.functional as F def linear_beta_schedule(T: int, beta_start: float = 1e-4, beta_end: float = 0.02): return torch.linspace(beta_start, beta_end, T, dtype=torch.float64) def cosine_beta_schedule(T: int, s: float = 0.008): """Nichol-Dhariwal 2021""" ts = torch.arange(T + 1, dtype=torch.float64) / T f = torch.cos(((ts + s) / (1 + s)) * math.pi / 2) ** 2 alpha_bar = f / f[0] betas = 1 - alpha_bar[1:] / alpha_bar[:-1] return betas.clamp(max=0.999) class DDPMSchedule: """Cache sqrt(α_bar), sqrt(1-α_bar), and other frequently used quantities.""" def __init__(self, betas: torch.Tensor): self.T = len(betas) self.betas = betas alphas = 1.0 - betas self.alphas = alphas self.alpha_bar = torch.cumprod(alphas, dim=0) self.sqrt_alpha_bar = torch.sqrt(self.alpha_bar) self.sqrt_one_minus_alpha_bar = torch.sqrt(1.0 - self.alpha_bar) # for sampling self.alpha_bar_prev = torch.cat([torch.tensor([1.0]), self.alpha_bar[:-1]]) self.posterior_variance = betas * (1.0 - self.alpha_bar_prev) / (1.0 - self.alpha_bar) def to(self, device): for k, v in self.__dict__.items(): if isinstance(v, torch.Tensor): setattr(self, k, v.to(device)) return self def q_sample(sched: DDPMSchedule, x0: torch.Tensor, t: torch.Tensor, noise: torch.Tensor = None): """Sample x_t ~ q(x_t | x_0) = N(sqrt(α_bar_t) x_0, (1-α_bar_t) I)""" if noise is None: noise = torch.randn_like(x0) sa = sched.sqrt_alpha_bar[t].view(-1, *([1] * (x0.dim() - 1))).to(x0.dtype) so = sched.sqrt_one_minus_alpha_bar[t].view(-1, *([1] * (x0.dim() - 1))).to(x0.dtype) return sa * x0 + so * noise def ddpm_simple_loss(model: nn.Module, sched: DDPMSchedule, x0: torch.Tensor): """L_simple = E ‖ε - ε_θ(x_t, t)‖²""" B = x0.shape[0] t = torch.randint(0, sched.T, (B,), device=x0.device) noise = torch.randn_like(x0) x_t = q_sample(sched, x0, t, noise) eps_pred = model(x_t, t) return F.mse_loss(eps_pred, noise)

A.2 DDPM ancestral sampling

@torch.no_grad() def ddpm_sample(model, sched: DDPMSchedule, shape, device, x_T=None): """Walk the full T-step ancestral chain from x_T ~ N(0, I).""" x = torch.randn(shape, device=device) if x_T is None else x_T.to(device) for t in reversed(range(sched.T)): t_b = torch.full((shape[0],), t, device=device, dtype=torch.long) eps_pred = model(x, t_b) alpha_t = sched.alphas[t] alpha_bar_t = sched.alpha_bar[t] beta_t = sched.betas[t] # Reverse mean (ε-pred form) mean = (x - beta_t / torch.sqrt(1 - alpha_bar_t) * eps_pred) / torch.sqrt(alpha_t) if t > 0: sigma_t = torch.sqrt(sched.posterior_variance[t]) noise = torch.randn_like(x) x = mean + sigma_t * noise else: x = mean # final step does not add noise return x

A.3 DDIM sampling (with η\eta)

@torch.no_grad() def ddim_sample( model, sched: DDPMSchedule, shape, device, num_steps: int = 50, eta: float = 0.0, # 0 = deterministic DDIM; η=1 in dense-step limit matches DDPM variance x_T=None, ): """Pick a sub-sequence of num_steps timesteps and run DDIM reverse.""" # Pick sub-sequence (linearly spaced) step_size = sched.T // num_steps timesteps = list(range(0, sched.T, step_size)) timesteps = timesteps + [sched.T - 1] timesteps = sorted(set(timesteps)) # dedupe / sort x = torch.randn(shape, device=device) if x_T is None else x_T.to(device) for i in reversed(range(1, len(timesteps))): t = timesteps[i] t_prev = timesteps[i - 1] t_b = torch.full((shape[0],), t, device=device, dtype=torch.long) alpha_bar_t = sched.alpha_bar[t] alpha_bar_prev = sched.alpha_bar[t_prev] eps_pred = model(x, t_b) # 1) x_0 estimate via Tweedie / ε-pred x0_hat = (x - torch.sqrt(1 - alpha_bar_t) * eps_pred) / torch.sqrt(alpha_bar_t) # 2) σ_t² = η² · (1-α_bar_prev)/(1-α_bar_t) · (1 - α_bar_t/α_bar_prev) sigma_t_sq = (eta ** 2) * (1 - alpha_bar_prev) / (1 - alpha_bar_t) * \ (1 - alpha_bar_t / alpha_bar_prev) sigma_t = torch.sqrt(sigma_t_sq.clamp(min=0)) # 3) DDIM step dir_xt = torch.sqrt((1 - alpha_bar_prev - sigma_t_sq).clamp(min=0)) * eps_pred noise = torch.randn_like(x) if eta > 0 else 0 x = torch.sqrt(alpha_bar_prev) * x0_hat + dir_xt + sigma_t * noise # Final step uses x0_hat (no noise added) return x0_hat

A.4 Classifier-Free Guidance training + sampling

class ConditionedEpsNet(nn.Module): """Demo: condition is a class label embedding, dropped with prob p_drop during training. In real projects swap self.backbone with UNet / DiT and feed c_emb + t_emb.""" def __init__(self, dim, num_classes, p_drop=0.1, backbone: nn.Module = None): super().__init__() self.p_drop = p_drop # NULL class uses index num_classes ("empty" embedding) self.cls_emb = nn.Embedding(num_classes + 1, dim) self.null_idx = num_classes self.backbone = backbone # placeholder: self.backbone(x, t, c_emb) returns ε def forward(self, x, t, c=None): # During training, randomly drop condition to NULL if self.training and c is not None: mask = torch.rand(c.shape[0], device=c.device) < self.p_drop c = torch.where(mask, torch.full_like(c, self.null_idx), c) elif c is None: c = torch.full((x.shape[0],), self.null_idx, device=x.device, dtype=torch.long) c_emb = self.cls_emb(c) # Concat c_emb onto the timestep embedding, run through UNet / DiT eps_pred = self.backbone(x, t, c_emb) return eps_pred @torch.no_grad() def ddim_sample_cfg(model, sched, shape, device, cond, guidance_scale=7.5, num_steps=50): """CFG-DDIM: two forwards (cond + uncond) per step, composed into ε_tilde.""" step_size = sched.T // num_steps timesteps = sorted(set(list(range(0, sched.T, step_size)) + [sched.T - 1])) x = torch.randn(shape, device=device) null_cond = torch.full_like(cond, model.null_idx) for i in reversed(range(1, len(timesteps))): t, t_prev = timesteps[i], timesteps[i - 1] t_b = torch.full((shape[0],), t, device=device, dtype=torch.long) eps_cond = model(x, t_b, cond) eps_uncond = model(x, t_b, null_cond) # CFG: note convention — here we use HF style guidance_scale=w (w=1 unguided) eps = eps_uncond + guidance_scale * (eps_cond - eps_uncond) alpha_bar_t = sched.alpha_bar[t] alpha_bar_prev = sched.alpha_bar[t_prev] x0_hat = (x - torch.sqrt(1 - alpha_bar_t) * eps) / torch.sqrt(alpha_bar_t) dir_xt = torch.sqrt(1 - alpha_bar_prev) * eps x = torch.sqrt(alpha_bar_prev) * x0_hat + dir_xt # η=0 deterministic return x0_hat

A.5 EDM preconditioning + Heun 2nd-order sampler

class EDMDenoiser(nn.Module): """D_θ(x; σ) = c_skip(σ) x + c_out(σ) F_θ(c_in(σ) x, c_noise(σ))""" def __init__(self, backbone: nn.Module, sigma_data: float = 0.5): super().__init__() self.backbone = backbone # outputs same shape as x self.sigma_data = sigma_data def forward(self, x: torch.Tensor, sigma: torch.Tensor): # σ shape [B] -> broadcast to x shape s = sigma.view(-1, *([1] * (x.dim() - 1))).to(x.dtype) sd2 = self.sigma_data ** 2 c_skip = sd2 / (s ** 2 + sd2) c_out = s * self.sigma_data / torch.sqrt(s ** 2 + sd2) c_in = 1.0 / torch.sqrt(s ** 2 + sd2) c_noise = 0.25 * torch.log(sigma).flatten() # 1D fed to backbone F = self.backbone(c_in * x, c_noise) return c_skip * x + c_out * F def edm_loss(D: EDMDenoiser, x0: torch.Tensor, P_mean: float = -1.2, P_std: float = 1.2): """EDM L = E [ λ(σ) ‖D_θ(x_0 + σε, σ) - x_0‖² ]; λ = 1/c_out². Implemented as unweighted F-loss: equivalent to weighted D-loss.""" B = x0.shape[0] log_sigma = P_mean + P_std * torch.randn(B, device=x0.device) sigma = log_sigma.exp() eps = torch.randn_like(x0) x = x0 + sigma.view(-1, *([1] * (x0.dim() - 1))) * eps D_pred = D(x, sigma) s = sigma.view(-1, *([1] * (x0.dim() - 1))) sd2 = D.sigma_data ** 2 weight = (s ** 2 + sd2) / (s * D.sigma_data) ** 2 # = 1/c_out² loss = (weight * (D_pred - x0) ** 2).mean() return loss def edm_sigma_schedule(N: int, sigma_min: float = 0.002, sigma_max: float = 80.0, rho: float = 7.0, device: str = "cpu"): """Karras ρ-schedule: σ_i = (σ_max^{1/ρ} + i/(N-1) · (σ_min^{1/ρ} - σ_max^{1/ρ}))^ρ""" i = torch.arange(N, device=device, dtype=torch.float64) sigmas = (sigma_max ** (1 / rho) + i / (N - 1) * (sigma_min ** (1 / rho) - sigma_max ** (1 / rho))) ** rho return torch.cat([sigmas, torch.zeros(1, device=device)]).to(torch.float32) # trailing σ=0 @torch.no_grad() def edm_heun_sample(D: EDMDenoiser, shape, sigmas: torch.Tensor, device): """Heun (2nd-order) ODE solver. 2 NFE per step; last step degenerates to Euler.""" x = torch.randn(shape, device=device) * sigmas[0] for i in range(len(sigmas) - 1): sigma = sigmas[i] sigma_next = sigmas[i + 1] sigma_b = sigma.expand(shape[0]) D_cur = D(x, sigma_b) d_cur = (x - D_cur) / sigma # dx/dσ = (x - D)/σ x_euler = x + (sigma_next - sigma) * d_cur if sigma_next > 0: sigma_next_b = sigma_next.expand(shape[0]) D_next = D(x_euler, sigma_next_b) d_next = (x_euler - D_next) / sigma_next x = x + (sigma_next - sigma) * 0.5 * (d_cur + d_next) else: x = x_euler # final-step Euler return x

A.6 Probability Flow ODE simple Euler solver

@torch.no_grad() def pf_ode_sample_euler(eps_model, sched: DDPMSchedule, shape, device, num_steps: int = 50): """PF-ODE Euler sampler under VP view. dx/dt = f(t) x - (1/2) g²(t) s_θ(x, t), s_θ = -ε_θ / sqrt(1-α_bar_t) In discrete schedule this degenerates to DDIM η=0 + time grid.""" # Pick sub-sequence step_size = sched.T // num_steps timesteps = sorted(set(list(range(0, sched.T, step_size)) + [sched.T - 1])) x = torch.randn(shape, device=device) for i in reversed(range(1, len(timesteps))): t, t_prev = timesteps[i], timesteps[i - 1] t_b = torch.full((shape[0],), t, device=device, dtype=torch.long) eps_pred = eps_model(x, t_b) alpha_bar_t = sched.alpha_bar[t] alpha_bar_prev = sched.alpha_bar[t_prev] # Equivalent DDIM η=0 form x0_hat = (x - torch.sqrt(1 - alpha_bar_t) * eps_pred) / torch.sqrt(alpha_bar_t) dir_xt = torch.sqrt(1 - alpha_bar_prev) * eps_pred x = torch.sqrt(alpha_bar_prev) * x0_hat + dir_xt return x0_hat

A.7 Sanity-check output (pedagogical version)

Run a 64×64 ImageNet subset toy setup, 2-layer UNet baseline, sched=cosine, T=1000:

[a] q_sample shape ok, σ_t variance ≈ 1-α_bar_t  ✓
[b] simple loss converges (5k steps): 0.42 → 0.18  ✓
[c] DDPM 1000-step sample: FID  (toy) ~ 22.5
[d] DDIM 50-step (η=0):    FID  (toy) ~ 23.1  ← close to DDPM 1000, 20× speedup
[e] DDIM 50-step (η=1):    FID  (toy) ~ 22.7  ← η=1 matches DDPM variance, not strict 1000-step DDPM
[f] CFG w=7.5 conditional: visually significant text-alignment boost ✓
[g] EDM Heun 35-NFE:       FID  (toy) ~ 18.3  ← much better than DDIM 50
[h] PF-ODE Euler 50-step:  numerically agrees with DDIM η=0 ✓

Main references: Ho 2020 (DDPM, NeurIPS), Nichol-Dhariwal 2021 (Improved DDPM, ICML), Song-Ermon 2019 (NCSN, NeurIPS), Song 2021 (Score SDE, ICLR), Song 2020 arXiv / ICLR 2021 (DDIM), Karras 2022 (EDM, NeurIPS), Lu 2022/2023 (DPM-Solver / DPM-Solver++), Ho-Salimans 2022 arXiv (CFG; short version: NeurIPS 2021 Workshop on DGMs), Dhariwal-Nichol 2021 (Classifier Guidance, NeurIPS), Rombach 2022 (LDM/SD, CVPR), Podell 2023 arXiv / ICLR 2024 (SDXL), Esser 2024 (SD3, ICML), Peebles-Xie 2023 (DiT, ICCV), Zhang 2023 (ControlNet, ICCV), Song 2023 (Consistency Models, ICML), Luo 2023 (LCM, arXiv), Sauer 2023/2024 (SDXL-Turbo / SD3-Turbo, arXiv).

Diffusion Foundations Cheat Sheet · formulas + from-scratch code + 25 frequently-asked questions (L1 must-know · L2 intermediate · L3 top lab)

我的音乐