Generative Models — 理论 & Tokenizers基础39 分钟P2 · 建议关注

Diffusion Foundations

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

DDPMscoreDDIMEDMCFG
内容来源:ARIS-in-AI-Offer by Ruofeng Yang (杨若峰),许可证 MITImported from commit 7fae0a820348.
本地更新提示

原文来自 ARIS 导入版本,尚未做本地逐段复审。建议阅读时优先核对最新论文、模型报告和库 API。

核对最新引用补充 2026 后续进展标注可能过时的模型/API 名称

§0 TL;DR

💡 9 句话搞定 Diffusion 基础 — 一页拿下面试核心要点(详见 §1–§13 推导)。

  1. DDPM (Ho 2020):forward q(xtx0)=N(αˉtx0,(1αˉt)I)q(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I) 闭式可采样;reverse pθ(xt1xt)=N(μθ,Σθ)p_\theta(x_{t-1}|x_t) = \mathcal{N}(\mu_\theta, \Sigma_\theta) 学反向 Gaussian;ELBO 化简到 Lsimple=Eϵϵθ(xt,t)2L_\text{simple} = \mathbb{E}\|\epsilon - \epsilon_\theta(x_t, t)\|^2ϵ\epsilon-prediction)。

  2. 三种视角等价:DDPM 的 ϵ\epsilon、score-based 的 s=logpts = \nabla \log p_t、flow matching 的 vv 在 Gaussian path 下线性可逆 —— sθ=ϵθ/σts_\theta = -\epsilon_\theta / \sigma_tv=αx0+σϵv = \alpha'x_0 + \sigma'\epsilon

  3. Tweedie 公式E[x0xt]=xt+σt2xtlogpt(xt)\mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2 \nabla_{x_t} \log p_t(x_t) —— 一行式连接 denoiser 与 score。

  4. Score SDE (Song 2021):VP-SDE / VE-SDE 统一框架;reverse-time SDEprobability flow ODE 共享同一族边缘分布,ODE 形式直接给出 FM 的 vector field。

  5. DDIM (Song 2020 / ICLR 2021):non-Markovian forward 推出 deterministic sampler,marginal 与 DDPM 相同但采样路径可控(η=0\eta=0 确定性;η=1\eta=1 + 走完整 TT 步退化为 DDPM ancestral,skip 步下则只是匹配 DDPM 方差,不严格等价)。

  6. EDM (Karras 2022):preconditioning 让网络输出方差恒为 1: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));配合 σ\sigma-schedule + Heun 2nd-order,FID SOTA 同时 NFE 降到 18-35

  7. CFG (Ho-Salimans 2022):训练时以概率 pdropp_\text{drop} drop 条件 → 同一 net 学 conditional/unconditional;推理 ϵ~=(1+w)ϵθ(x,c)wϵθ(x,)\tilde\epsilon = (1+w)\epsilon_\theta(x,c) - w\epsilon_\theta(x,\emptyset)w[3,7]w \in [3, 7] 是 text-to-image 主力。

  8. Production:SD/SDXL 用 VAE latent + UNet;SD3 / FLUX.1 改用 Rectified Flow + MM-DiT;ControlNet 给 frozen UNet 加可训练 side branch;DiT 把 UNet 全换 Transformer。

  9. 加速:DPM-Solver++ 把 NFE 压到 10-20;Consistency Models 学 fθ(xt,t)x0f_\theta(x_t, t) \mapsto x_0 做到 1-4 步;LCM / LCM-LoRA / SDXL-Turbo (ADD) / SD3-Turbo (LADD) 让蒸馏在 Stable Diffusion 全家桶可用。

§1 直觉 & 三种视角

1.1 一句话直觉

Diffusion = 学会"去噪":把数据从干净逐渐加噪到纯 Gaussian(forward),然后学会反过来从噪声一步步还原数据(reverse)。所有 diffusion 论文的差异都在三件事:

  • forward 怎么加噪(schedule、SDE 类型 VP/VE)
  • 网络预测什么ϵ\epsilon / x0x_0 / vv / score / DD
  • reverse 怎么采样(Markov ancestral / DDIM / DPM-Solver / EDM Heun / Consistency one-step)

1.2 三种视角对照

                            统一框架(Song et al. 2021)
                            
       离散视角(DDPM)         连续视角(Score SDE)      Flow 视角(FM/RF)
       ────────────         ──────────────────       ────────────────
       q(x_t|x_{t-1})  →    dx = f(x,t)dt+g(t)dW  →   dx = u_t(x) dt
        闭式 q(x_t|x_0)        forward SDE              ODE (deterministic)
              ↓                       ↓                       ↓
        ε-prediction        score s = ∇ log p_t        vector field v_t
              ↘                       ↓                       ↙
                          全部线性可逆(在 Gaussian path 下)
                          s = -ε/σ_t,   v = α'x_0 + σ'ε,   ε = -σ s

💡 面试一句话答 — "DDPM 是离散时间下 VP-SDE 的特例;score-based 是连续时间下的等价参数化;Flow Matching 在 VP/VE path 下与 score matching 同信息,只是参数化成 vv 不是 ss。Rectified Flow 跳出 SDE 框架,用线性 path 直接学 ODE 的 vector field。"

1.3 Convention(全文统一)

符号含义
x0x_0干净数据样本
xtx_t, t{1,,T}t \in \{1,\dots,T\}t[0,T]t \in [0,T]加噪后的样本
ϵN(0,I)\epsilon \sim \mathcal{N}(0, I)标准 Gaussian 噪声
αt,βt=1αt\alpha_t, \beta_t = 1 - \alpha_tDDPM 单步 forward 系数
αˉt=s=1tαs\bar\alpha_t = \prod_{s=1}^t \alpha_sDDPM cumulative 系数
σt\sigma_t标准差(NCSN / EDM 视角的"噪声水平")
sθ(xt,t)xtlogpt(xt)s_\theta(x_t, t) \approx \nabla_{x_t}\log p_t(x_t)score
ϵθ(xt,t)ϵ\epsilon_\theta(x_t, t) \approx \epsilonDDPM 中预测的噪声
Dθ(x;σ)x0D_\theta(x; \sigma) \approx x_0EDM 的 denoiser 输出

⚠️ 时间方向陷阱 — DDPM 论文 forward 是 t=0Tt = 0 \to T(数据加噪到纯噪声),reverse 是 T0T \to 0;FM 论文常用 t=0t = 0 噪声、t=1t = 1 数据。面试写代码前一定要先 disambiguate 时间方向——否则 sampler 容易跑反。

§2 DDPM Forward Process

2.1 单步与闭式表达

DDPM forward 是一条 Markov chain

q(xtxt1)=N(xt;  1βtxt1,  β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

定义 αt=1βt\alpha_t = 1 - \beta_tαˉt=s=1tαs\bar\alpha_t = \prod_{s=1}^t \alpha_s关键性质q(xtx0)q(x_t | x_0)闭式 Gaussian——可以一步从 x0x_0 跳到任意 tt(训练效率的核心):

  q(xtx0)=N ⁣(xt;  αˉtx0,  (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) \;}

等价 reparameterization:

xt=αˉtx0+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 闭式推导(必考,会反复出现)

由 reparameterization xt=αtxt1+βtztx_t = \sqrt{\alpha_t} x_{t-1} + \sqrt{\beta_t} z_tztN(0,I)z_t \sim \mathcal{N}(0, I) 独立。递推:

xt=αtxt1+βtzt=αt(αt1xt2+βt1zt1)+βtzt=αtαt1xt2+αtβt1zt1+βtzt独立 Gaussian 之和\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{独立 Gaussian 之和}} \end{aligned}

两个独立 Gaussian 之和的方差:αtβt1+βt=αt(1αt1)+(1αt)=1αtαt1\alpha_t \beta_{t-1} + \beta_t = \alpha_t(1 - \alpha_{t-1}) + (1 - \alpha_t) = 1 - \alpha_t \alpha_{t-1}。所以可以合并成单个 Gaussian 1αtαt1zˉ\sqrt{1 - \alpha_t \alpha_{t-1}}\, \bar z。归纳到 tt 步:

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

💡 变分 trick 直觉 — Markov chain 的好处是每一步都是 Gaussian,所以累积仍是 Gaussian;这让 forward 不用网络就能采样、训练时不用模拟整条链。

2.3 边界与极限

  • t=0t = 0αˉ0=1\bar\alpha_0 = 1x0x_0 自身 —— forward 起点
  • t=Tt = T(DDPM 取 1000):要求 αˉT0\bar\alpha_T \approx 0,则 xTϵN(0,I)x_T \approx \epsilon \sim \mathcal{N}(0, I) —— forward 终点接近 Gaussian prior

⚠️ Schedule 末端的 SNR (Signal-to-Noise Ratio) — SNR(t)=αˉt/(1αˉt)(t) = \bar\alpha_t / (1-\bar\alpha_t);linear schedule 在 t=Tt=TαˉT4×105\bar\alpha_T \approx 4\times 10^{-5} 对应 SNR 4×105\approx 4\times 10^{-5}——虽然很小但严格意义上未到 0,prior 仍非完全匹配 N(0,I)\mathcal{N}(0,I);这是 cosine schedule 与 "v-prediction" 改进的动机之一。

§3 DDPM Reverse Process & 训练

3.1 Reverse 是 Gaussian 的前提

理论上 q(xt1xt)q(x_{t-1} | x_t) 不是 Gaussian(依赖整个数据分布)。但当 βt\beta_t 足够小时,反向条件分布 近似 是 Gaussian(Feller 1949 / Sohl-Dickstein 2015),所以参数化为:

pθ(xt1xt)=N ⁣(xt1;  μθ(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 推导

DDPM 优化 evidence lower bound(与 VAE 类似):

logpθ(x0)Eq(x1:Tx0)[logpθ(x0:T)q(x1:Tx0)]=Eq[KL(q(xTx0)p(xT))]LT(常数,prior 匹配)t=2TEq[KL(q(xt1xt,x0)pθ(xt1xt))]Lt1+Eq[logpθ(x0x1)]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{(常数,prior 匹配)}} \\ &\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}

核心q(xt1xt,x0)q(x_{t-1} | x_t, x_0) 是闭式 Gaussian(由 Bayes 推得):

q(xt1xt,x0)=N ⁣(xt1;  μ~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)

其中:

μ~t(xt,x0)=αˉt1βt1αˉtx0+αt(1αˉt1)1αˉtxt,β~t=1αˉt11αˉ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 化简到 LsimpleL_\text{simple}(必考推导)

x0=(xt1αˉtϵ)/αˉtx_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon) / \sqrt{\bar\alpha_t} 代入 μ~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)

参数化 μθ(xt,t)\mu_\theta(x_t, t) 也采用同样形式(ϵ\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)

固定 Σθ=σt2I\Sigma_\theta = \sigma_t^2 I(取 σt2=βt\sigma_t^2 = \beta_tβ~t\tilde\beta_t)。两个 Gaussian 的 KL:

Lt1=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 的工程妙招:扔掉前面所有系数 + 常数项,直接用 unweighted 版本:

  Lsimple(θ)=EtU{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] \;}

为什么扔系数也能 work? —— Ho 2020 经验观察:unweighted 版本相当于给低 SNR(高 tt)loss 更大权重,反而提升 sample 质量。但代价是 log\log-likelihood 不再是 ELBO 下界——所以"FID 好"≠"likelihood 好"。后续 Improved DDPM (Nichol-Dhariwal 2021) 引入 hybrid loss Lhybrid=Lsimple+λLvlbL_\text{hybrid} = L_\text{simple} + \lambda L_\text{vlb}λ=0.001\lambda = 0.001),同时学 Σθ\Sigma_\theta

3.4 预测目标的等价转换(必背)

给定 xt=αˉtx0+1αˉtϵx_t = \sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t}\epsilon,三种主流参数化线性可逆:

ϵ-pred:ϵθ(xt,t)ϵx0-pred:x^0(xt,t)=xt1αˉtϵθαˉtv-pred (Salimans-Ho 2022):vθ=αˉtϵ1αˉtx0score: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}

💡 v-prediction 为何更稳? —— ϵ\epsilon-pred 在 t0t \to 0(小噪声)时退化(loss 系数爆炸);x0x_0-pred 在 tTt \to T(大噪声)时退化;vv-pred 是两者插值,在所有 tt 上 loss 数值范围近似一致——是 Imagen Video / SD2.1-v / Karras EDM 选用的关键。

§4 Schedule:linear / cosine / EDM

4.1 Linear (Ho 2020)

βt=βstart+t1T1(βendβstart),βstart=104,  β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。简单、稳定,但末端 SNR 未严格到 0(αˉT4×105\bar\alpha_T \approx 4 \times 10^{-5},对应 SNR 4×105\approx 4 \times 10^{-5},理想 prior 要求更接近 0)。

4.2 Cosine (Nichol-Dhariwal 2021)

αˉt=f(t)f(0),f(t)=cos2 ⁣((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/αˉt1\beta_t = 1 - \bar\alpha_t / \bar\alpha_{t-1}(再裁剪到 [0,0.999][0, 0.999] 防止数值问题)。s=0.008s = 0.008 是为了让 β1\beta_1 不要太接近 0。

Cosine schedule 为什么更好? —— linear schedule 在低 tt 区域加噪太快,模型大部分时间"练"在已经全是噪声的区域(学不到东西)。cosine 在低 tt 加噪缓慢、中间快、末端 SNR 真的接近 0。Improved DDPM 实验:cosine 比 linear 在 ImageNet 64 上 FID 提升约 20%。

4.3 EDM σ-schedule (Karras 2022)

EDM 把 β\beta schedule 重新参数化为 σ\sigma schedule(直接用 σ\sigma 当时间)。采样时:

σi=(σmax1/ρ+iN1(σmin1/ρσmax1/ρ))ρ,i=0,,N1\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

默认 σmin=0.002\sigma_\text{min} = 0.002, σmax=80\sigma_\text{max} = 80, ρ=7\rho = 7ρ=7\rho = 7 是 Karras 实验扫出来的——比线性 / 对数都好,因为它把更多步骤分配在小 σ\sigma(高 SNR)区域,那里步进误差更敏感。

💡 离散 vs 连续 schedule — DDPM 的 β\beta 数组等价于 VP-SDE 的 β(t)=TβtT\beta(t) = T \beta_{\lfloor tT \rfloor};EDM 的 σ\sigma-schedule 等价于 VE-SDE 的 σ(t)=t\sigma(t) = t(线性时间);两者只差一个 tt 重参数化,信息上等价。EDM 的贡献是发现一组工程上更稳的 σi\sigma_i 选取规则。

§5 Score-based 视角

5.1 Score 与 score matching (Hyvärinen 2005)

定义 s(x)=xlogp(x)s(x) = \nabla_x \log p(x)。如果学到 sθss_\theta \approx s,可以用 Langevin dynamics 采样:

xk+1=xk+η2sθ(xk)+ηzk,zkN(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)

直接 score matching loss Epsθlogp2\mathbb{E}_p\|s_\theta - \nabla\log p\|^2 不可计算(不知道 logp\nabla \log p)。Hyvärinen 2005 给出 implicit score matching 通过积分变换避开 logp\nabla \log p

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

tr(xsθ)\operatorname{tr}(\nabla_x s_\theta) 在高维下太贵(Hessian trace)。

5.2 Denoising Score Matching (Vincent 2011)

对每个数据点 x0x_0,加噪 x~=x0+σϵ\tilde x = x_0 + \sigma \epsilon,定义 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 证明:

Epσ(x~)sθ(x~)logpσ(x~)2=Ex0,x~sθ(x~)x~logq(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}

q(x~x0)=N(x0,σ2I)q(\tilde x | x_0) = \mathcal{N}(x_0, \sigma^2 I) 的 score 闭式

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

所以训练 loss 简化为:

  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 \;}

这正是 NCSN / SMLD 的训练目标(差一个权重)。

5.3 Tweedie 公式(必考推导)

陈述:对加性 Gaussian 噪声 xt=x0+σtϵx_t = x_0 + \sigma_t \epsilon(VE 视角,ϵN(0,I)\epsilon \sim \mathcal{N}(0,I)):

  E[x0xt]=xt+σt2xtlogpt(xt)  \boxed{\; \mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2\, \nabla_{x_t} \log p_t(x_t) \;}

推导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。对 xtx_t 求梯度:

xtpt(xt)=p0(x0)xtN(xt;x0,σt2I)dx0=p0(x0)N(xt;x0,σt2I)x0xtσt2dx0\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

两边除以 pt(xt)p_t(x_t)

xtlogpt(xt)=1pt(xt)p0(x0)N(xtx0)x0xtσt2dx0=Ep0(x0xt)[x0xtσ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]

即:

σt2xtlogpt(xt)=E[x0xt]xtE[x0xt]=xt+σt2xtlogpt(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 是连接所有 diffusion 参数化的"罗塞塔石碑" —— denoiser 网络的最优输出(MMSE estimator)就是 score 加上恒等映射。所有 ϵ\epsilon-pred / score-pred / x0x_0-pred / vv-pred 之间的转换都是 Tweedie 的一行式重排。

5.4 NCSN / SMLD (Song-Ermon 2019)

Noise-Conditional Score Network:训练一个共享网络 sθ(x,σ)s_\theta(x, \sigma),对一组噪声水平 σ1>σ2>>σL\sigma_1 > \sigma_2 > \dots > \sigma_L 同时做 DSM。采样时做 annealed Langevin dynamics:先在大 σ1\sigma_1 上 Langevin(探索全空间),逐步降到 σL\sigma_L(精修细节)。

xx+ϵi2sθ(x,σi)+ϵiz,ϵ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

每个 σi\sigma_iTT 步 Langevin,然后切到下一个 σi+1\sigma_{i+1}

⚠️ 为什么单一 σ\sigma 不行? —— 小 σ\sigma 训出来的 score 在远离数据流形的地方完全错(mode 之间的"空地带" p(x)0p(x) \approx 0,score 给不出方向)。多 noise level 的核心是用大 σ\sigma 把空间"填满",给小 σ\sigma 提供初始位置。

§6 Score SDE:统一框架 + Probability Flow ODE

6.1 Forward SDE

Song et al. 2021 (ICLR) 把所有 diffusion 写成 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)对应离散版
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-VP12β(t)x-\frac{1}{2}\beta(t) xβ(t)(1e20tβ(s)ds)\sqrt{\beta(t)(1-e^{-2\int_0^t \beta(s)ds})}介于 VP/VE,likelihood 更好

VP-SDE 满足 Var[xt]1\text{Var}[x_t] \le 1(variance preserving),VE-SDE 让方差无界增长(variance exploding)。

6.2 Reverse SDE (Anderson 1982)

对任意 forward SDE,存在 reverse-time SDE

  dx=[f(x,t)g2(t)xlogpt(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 \;}

dWˉd\bar W 是 reverse-time Wiener process。采样:从 xTpTx_T \sim p_T(接近 prior)开始,用 SDE solver(Euler-Maruyama / predictor-corrector)积分到 t=0t = 0

6.3 Probability Flow ODE(与 FM 的桥梁)

关键定理(Song et al. 2021, "Score-Based Generative Modeling through SDEs"):以下确定性 ODE 与 reverse SDE 共享所有时刻的边缘 ptp_t

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

这就是 probability flow ODE。等价于 Flow Matching 的 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)

三种采样器的关系图

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

证明草图:写 forward SDE 的 Fokker-Planck(连续性方程):

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

利用 Δpt=(ptlogpt)\Delta p_t = \nabla \cdot (p_t \nabla \log p_t) 把扩散项写成 transport 形式:

ptt=[(f12g2logpt)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]

这正是 ODE dx/dt=f12g2logptdx/dt = f - \frac{1}{2} g^2 \nabla \log p_t 的连续性方程——所以它们的 ptp_t 一致。

6.4 ODE 视角的优势

优势说明
Deterministic同一 noise → 同一 sample,可做 image editing / interpolation
NFE 友好高阶 ODE solver(Heun / RK4 / DPM-Solver)少步数即可
Likelihood 可计算logp0(x0)=logpT(xT)+0Tvt(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),用 Hutchinson trace estimator 估计 div
桥到 FMRF / SD3 / FLUX 走这条线

⚠️ SDE vs ODE 的 trade-off — SDE 采样的随机扰动可以"修正"早期错误,通常 sample 质量更高但 NFE 大;ODE deterministic 但易受 solver 误差累积,需更高阶 solver。EDM 提出折中:基础 ODE + 小幅 stochastic churn("SchurnS_\text{churn}"),FID 更好。

§7 DDIM:Non-Markovian Forward → Deterministic Sampler

7.1 Motivation

DDPM ancestral sampling 必须走 T=1000T = 1000 步(Markov 链)。能否少步数采样且不重训?DDIM (Song et al. 2020 arXiv / ICLR 2021) 给出"yes"——核心是把 forward 改成 non-Markovian,但保持与 DDPM 一样的 marginal q(xtx0)q(x_t | x_0)

7.2 Non-Markovian Forward

DDIM 定义一族 forward distribution,由参数 η[0,1]\eta \in [0, 1] 控制:

qσ(xt1xt,x0)=N ⁣(xt1;  αˉt1x0+1αˉt1σt2xtαˉ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)

其中 σt2=η2β~t=η21αˉt11αˉ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

关键性质(DDIM Theorem 1):在此 forward 下,q(xtx0)q(x_t | x_0) 仍是 N(αˉtx0,(1αˉt)I)\mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I)——与 DDPM 完全一致!所以可以直接用 DDPM 训练的 ϵθ\epsilon_\theta 做 DDIM 采样。

7.3 DDIM 采样公式

x0x^0=(xt1αˉ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} 代入:

  xt1=αˉt1x^0+1αˉt1σt2ϵθ(xt,t)+σtz,zN(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确定性——同 xTx_Tx^0\hat x_0(latent space interpolation 友好)
  • η=1\eta = 1(走完整 TT 步)σt=β~t\sigma_t = \sqrt{\tilde\beta_t},退化为标准 DDPM ancestral sampler;skip 步数 S<TS < T 时只是匹配方差量级,不严格等价 1000 步 DDPM
  • 中间 η(0,1)\eta \in (0, 1):随机性可调

7.4 Skip steps(少步数采样)

不必逐步 tt1t \to t-1,可以跳:选 sub-sequence τ0<τ1<<τS=T\tau_0 < \tau_1 < \dots < \tau_S = T,做:

xτi1=αˉτi1x^0+1αˉτi1στi2ϵθ(xτi,τi)+στizx_{\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

经典 baseline:S=50S = 50 步 DDIM 在 ImageNet 256 上 FID 接近 DDPM 1000 步。

DDIM = probability flow ODE 的离散化 — 当 η=0\eta = 0 且时间网格连续化时,DDIM 退化为 VP-SDE 对应的 probability flow ODE 的一阶 Euler 离散——这是为什么 deterministic DDIM 与 ODE-based 采样(DPM-Solver、EDM Heun)连成一线。

§8 EDM:Karras 2022 设计空间

8.1 Motivation

Karras 2022 ("Elucidating the Design Space of Diffusion-Based Generative Models") 把 diffusion 的所有设计自由度拆开(参数化、loss weighting、采样器、schedule),逐项扫,给出 SOTA 配方:CIFAR-10 FID 1.79(35 NFE)、ImageNet 64 FID 1.36。

8.2 Preconditioning(必考推导)

EDM 用 VE 视角x=x0+σϵx = x_0 + \sigma \epsilonϵN(0,I)\epsilon \sim \mathcal{N}(0, I)σ\sigma 直接当 noise level(没有 α\alpha)。

Denoiser 参数化:

  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) \;}

其中 FθF_\theta 是底层网络,四个 cc 函数是 手工 schedule。Karras 推导:

推导:unit-variance 论证

目标:让 FθF_\theta 的输入和训练 target 在所有 σ\sigma 上方差都是 O(1)\mathcal{O}(1)

输入侧:网络看到的输入 cinxc_\text{in} x。已知 Var[x]=σdata2+σ2\text{Var}[x] = \sigma_\text{data}^2 + \sigma^2(数据方差 + 噪声方差),所以:

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

输出侧:理想 denoiser D(x;σ)=E[x0x]D^*(x; \sigma) = \mathbb{E}[x_0 | x](Tweedie)。我们让网络学残差而非全量:定义 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]

希望 Var[coutF+cskipxD]=0\text{Var}[c_\text{out} F^* + c_\text{skip} x - D^*] = 0Var[F]=1\text{Var}[F^*] = 1(让网络的 target 单位方差)。

求最小 effective error 的 cskipc_\text{skip}coutc_\text{out}(最小化 EFFθ2\mathbb{E}\|F^* - F_\theta\|^2Var[F]=1\text{Var}[F^*]=1 约束下)。Karras 取 D=x0D^* = x_0(理想情形),代入并展开:

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}}

直觉

  • σ0\sigma \to 0(低噪声):cskip1,cout0c_\text{skip} \to 1, c_\text{out} \to 0 —— 输出基本是 input identity(denoiser 啥都不用做)
  • σ\sigma \to \infty(高噪声):cskip0,coutσdatac_\text{skip} \to 0, c_\text{out} \to \sigma_\text{data} —— 输出由 network 完全决定(input 全是噪声)

时间编码cnoise(σ)=14lnσc_\text{noise}(\sigma) = \frac{1}{4} \ln \sigma(log-scale,覆盖 σ[σmin,σmax]\sigma \in [\sigma_\text{min}, \sigma_\text{max}] 的宽动态范围)。

8.3 训练 loss

EDM 用 weighted L2:

LEDM(θ)=Eσ,x0,ϵ[λ(σ)Dθ(x0+σϵ;σ)x02]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]

权重 λ(σ)=(σ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,等价 训练 FθF_\theta 用 unweighted L2(每个 σ\sigma 上 target 单位方差,loss 数量级一致)。

σ\sigma 训练采样lnσN(Pmean,Pstd2)\ln \sigma \sim \mathcal{N}(P_\text{mean}, P_\text{std}^2),默认 Pmean=1.2P_\text{mean} = -1.2, Pstd=1.2P_\text{std} = 1.2(让 σ\sigma 集中在 0.30.3 附近——这是"最难学"的 SNR 区域,Karras 实验扫出来的)。

8.4 Heun 2nd-order sampler

EDM 采样默认用 Heun 二阶 ODE + 可选 stochastic churn。VE-SDE 的 probability flow ODE 在 f=0,g(t)=dσ2/dtf = 0, g(t) = \sqrt{d\sigma^2/dt} 下:

dxdσ=σxlogpσ(x)=xDθ(x;σ)σ\frac{dx}{d\sigma} = -\sigma\, \nabla_x \log p_\sigma(x) = \frac{x - D_\theta(x; \sigma)}{\sigma}

(用 Tweedie:logpσ=(Dx)/σ2\nabla \log p_\sigma = (D - x)/\sigma^2,代入 dx/dσ=σlogpdx/d\sigma = -\sigma \nabla \log p

Heun 第 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:                             # 末步跳过 corrector
    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,但二阶精度——比 Euler 一阶 NFE 多但更准。CIFAR-10 EDM 配 35 NFE = 18 steps Heun + 一阶末端,FID 1.79。

💡 Stochastic churn(可选) —— 在每步开始时把 σi\sigma_i 临时提高到 σ^i=(1+γi)σi\hat\sigma_i = (1+\gamma_i)\sigma_iγi\gamma_i 是当前步的小幅 churn),需注入额外噪声:x^i=xi+σ^i2σi2z\hat x_i = x_i + \sqrt{\hat\sigma_i^2 - \sigma_i^2}\, z,其中 σ^i2σi2=σi2γi+γi2\sqrt{\hat\sigma_i^2 - \sigma_i^2} = \sigma_i\sqrt{2\gamma_i + \gamma_i^2};从 σ^i\hat\sigma_i 降回 σi+1\sigma_{i+1} 等价小幅 SDE。EDM 实验:少量 churn 在 ImageNet 上略涨 FID(约 0.1-0.3)。

§9 高阶采样器:DPM-Solver / DPM-Solver++

9.1 Motivation

DDIM 是一阶 ODE Euler。DPM-Solver (Lu et al. 2022 NeurIPS) 利用 diffusion ODE 的半线性结构做高阶展开。Probability flow ODE 在 VP-SDE 下用 ϵ\epsilon-pred 改写:

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

其中 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}(来自 12gSDE2s=+12βϵ/1αˉt-\frac{1}{2}g_\text{SDE}^2 \cdot s = +\frac{1}{2}\beta\cdot \epsilon/\sqrt{1-\bar\alpha_t},因为 s=ϵ/1αˉts = -\epsilon/\sqrt{1-\bar\alpha_t})。

把线性部分精确积分(exponential integrator),剩余部分用 Taylor 展开。

9.2 DPM-Solver-2 / 3(核心思想)

λt=log(αˉt/1αˉt)\lambda_t = \log(\sqrt{\bar\alpha_t} / \sqrt{1-\bar\alpha_t})(log-SNR),用 λ\lambda 当时间变量。ODE 重写:

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

ϵ^θ\hat\epsilon_\thetaλ\lambda 上做 kk 阶 Taylor 展开,线性部分精确(exponential weight),剩余按阶数取近似:

  • DPM-Solver-1 = DDIM(一阶)
  • DPM-Solver-2:每步 2 NFE,二阶
  • DPM-Solver-3:每步 3 NFE,三阶

10-15 NFE 即可达到 50 NFE DDIM 同质量。

9.3 DPM-Solver++(CFG 友好版,Lu et al. 2023)

原 DPM-Solver 在 CFG 下不稳(ϵθ\epsilon_\theta 经 CFG amplify 后超出训练域,Taylor 展开误差大)。DPM-Solver++ 改用 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

(用 x0x_0-pred 而非 ϵ\epsilon-pred 让 CFG amplification 落在更稳的区域)

15-20 NFE 在 CFG=7 下质量 close to 100-NFE DDIM。SDXL / SD3 默认 sampler 之一。

9.4 采样器对比

💡 常见 sampler 选择 cheat sheet — 按 NFE/质量/适配排序如下(图像生成)。

  • DDPM ancestral:T=1000 步,作 baseline;现代少用

  • DDIM (η=0\eta = 0):50-100 NFE,简单稳定,可做 interpolation

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

  • EDM Heun:18-35 NFE,确定性 ODE 二阶,文献 SOTA baseline

  • DPM-Solver / DPM-Solver++:10-20 NFE,HuggingFace diffusers 推荐

  • UniPC (Zhao 2023):predictor-corrector framework,可超过 DPM-Solver

  • Consistency Models (one-step / two-step):1-4 NFE,需蒸馏

§10 Conditioning:Classifier Guidance & CFG

10.1 Classifier Guidance (Dhariwal-Nichol 2021)

训练一个独立 classifier pϕ(cxt)p_\phi(c | x_t)(在 noisy data 上),用 Bayes:

xtlogp(xtc)=xtlogp(xt)+xtlogpϕ(cxt)\nabla_{x_t} \log p(x_t | c) = \nabla_{x_t} \log p(x_t) + \nabla_{x_t} \log p_\phi(c | x_t)

实践中给 classifier gradient 加 scale ww(控制 guidance 强度):

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

⚠️ Classifier guidance 的缺点 —— (a) 必须额外训 noisy classifier,工程负担;(b) classifier gradient 易"对抗",在远离训练分布时退化;(c) 对 text-to-image 这种连续 condition 不友好。CFG 完全替代了它。

10.2 Classifier-Free Guidance (Ho-Salimans 2022)

训练:以概率 pdropp_\text{drop}(一般 0.1)把 cc 替换为 \emptyset(null embedding),同一个 net 学 conditional 和 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]

推理:把 ww 称为 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] \;}

等价形式(Imagen / SD 实现常用):

ϵ~=(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)

⚠️ CFG ww 的两种 convention — 论文 Ho-Salimans 2022 原文 ϵ~=ϵuncond+(1+w)(ϵcondϵuncond)\tilde\epsilon = \epsilon_\text{uncond} + (1+w)(\epsilon_\text{cond} - \epsilon_\text{uncond}),即 w=0w = 0 是 unguided、w>0w > 0 增强。但 HuggingFace / SD UI 常用 w=w+1w' = w + 1,即 w=1w' = 1 是 unguided、w=7.5w' = 7.5 是常用强度。面试代码记得标明 convention

10.3 CFG 的几何意义

CFG 等价于把采样轨迹拉向"条件梯度"方向:

xtlogp(xtc)xtlogp(xt)+wxtlogp(xtc)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)}

第二项是"条件性 score 差",把样本推向 conditional likelihood 高、unconditional likelihood 相对低的区域——直觉上"放大文本对齐"。

CFG 是 SD/SDXL/FLUX 文图对齐的核心 —— w[3,7.5]w \in [3, 7.5] 是 Stable Diffusion 的实验 sweet spot;w>10w > 10 容易 over-saturated(颜色饱和、artifact)。FLUX 把 CFG 内化进 distillation("guidance-distilled"),单 forward 就实现 CFG 效果——这是它推理速度的关键之一。

§11 Production:从 LDM 到 FLUX

11.1 Latent Diffusion (LDM, Rombach 2022 CVPR)

核心 idea:在 VAE latent space 而非 pixel space 跑 diffusion。

  1. 训一个 VAE E,DE, Dz=E(x),x^=D(z)z = E(x), \hat x = D(z)zzxx 小 ~8×(如 5122×3642×4512^2 \times 3 \to 64^2 \times 4
  2. zz 上训 diffusion model(参数量、显存、计算全部降一个数量级)
  3. 生成时:从 zTz_T 采样到 z0z_0,再用 D(z0)D(z_0) decode 回 pixel

Stable Diffusion (SD) = LDM + CLIP text encoder + UNet on 64×64×464 \times 64 \times 4 latent,是当时最实用的开源 T2I 模型。

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

SD 1.5 → SDXL 的主要改进:

  • 更大 UNet:参数从 ~860M 升到 ~2.6B,cross-attn 层更多
  • 二阶段架构:base + refiner(refiner 在低噪声段补细节)
  • 更好的 text encoder:OpenCLIP ViT-bigG/14 + CLIP-L/14 拼接
  • 多尺度 / 多 aspect-ratio 训练:原生支持 1024×1024 + 不同长宽比
  • MicroConditioning:把原始分辨率、crop offset、aspect ratio 当条件喂给 UNet

11.3 DiT (Peebles-Xie 2023 ICCV)

把 UNet 换成纯 Transformer

  • 把 latent 切 patch(如 2×22 \times 2)成 token sequence
  • 标准 Transformer block(self-attn + MLP)
  • conditioning 通过 adaptive LayerNorm (adaLN) 注入:LN(x)γ(c,t)+β(c,t)\text{LN}(x) \cdot \gamma(c, t) + \beta(c, t)γ,β\gamma, \beta 来自 c,tc, t 的 MLP

DiT 实验:scale law 比 UNet 好,FID 随参数量稳定下降。SD3 / FLUX / Sora 都基于 DiT 系。

11.4 SD3 (Esser 2024 ICML) —— diffusion 换成 Rectified Flow

SD3 的两个关键改动:

  1. Rectified Flow 替代 DDPM:训练目标变成 vθ(x1x0)2\|v_\theta - (x_1 - x_0)\|^2(FM 框架)
  2. MM-DiT:多模态 DiT,text token 和 image token 在同一 Transformer 里互相 attend(不是 cross-attn)

为什么换 RF?Esser 2024 ablation:linear path 的 trajectory 比 cosine path 更直 → 少步采样更好;logit-normal tt sampling 让 mid-noise 更被重视,质量提升。

11.5 FLUX.1 (Black Forest Labs 2024)

继承 SD3 + MM-DiT,主要更新:

  • 12B 参数(开源 dev 版)
  • Guidance-distilled:把 CFG 蒸馏进单 forward,推理无需 2× CFG forward
  • Adversarial training 末段微调(类似 SD3-Turbo / ADD),4-step 即出图

11.6 ControlNet (Zhang 2023 ICCV)

给 frozen SD UNet 加 trainable copy + zero-conv 连接:

原 UNet (frozen)                    控制信号 (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 = 初始权重为 0 的 1×1 卷积 → 训练初始 ControlNet 不改变原 UNet 输出(保留 SD 能力),随训练逐渐学到 condition 控制。

ControlNet 的训练效率 —— frozen 原 UNet(大部分参数),只训 trainable copy(~一半参数),单卡可训,是开源生态的关键。

§12 Distillation:1-step / Few-step 生成

12.1 Progressive Distillation (Salimans-Ho 2022)

迭代蒸馏:student 一步 \approx teacher 两步,蒸馏 log2N\log_2 N 轮把 NN 步压到 1 步。关键:每次只压一半,分布漂移可控。

12.2 Consistency Models (Song 2023 ICML)

思路:直接学一个网络 fθ(xt,t)f_\theta(x_t, t),使得对所有 tt 都满足:

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

即网络是 probability flow ODE 的 consistency function——任意 xtx_t 映到对应的 x0x_0。一步采样:x0=fθ(xT,T)x_0 = f_\theta(x_T, T)

训练目标(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]

其中:

  • θ\theta^- 是 EMA target
  • x^tn\hat x_{t_n} 由 teacher ODE solver 从 xtn+1x_{t_{n+1}} 走一步得到(xtn=ODE-step(xtn+1)x_{t_n} = \text{ODE-step}(x_{t_{n+1}})
  • dd 是 metric(L2 / LPIPS)

Boundary condition:要求 fθ(xσmin,σmin)=xσminf_\theta(x_{\sigma_\text{min}}, \sigma_\text{min}) = x_{\sigma_\text{min}}(在最低噪声处自洽)——用 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)

cskip,coutc_\text{skip}, c_\text{out} 设计让 σ=σmin\sigma = \sigma_\text{min}fθxf_\theta \equiv x

⚠️ CT (Consistency Training) vs CD (Consistency Distillation) — CT 完全 from scratch(不用 teacher,直接对 x0+σnϵx_0 + \sigma_n \epsilonx0+σn+1ϵx_0 + \sigma_{n+1} \epsilon 做 consistency loss);CD 用 pretrained teacher 蒸馏。质量上 CD > CT;近期 ICT (Song 2024) 让 CT 接近 CD。

12.3 LCM / LCM-LoRA (Luo 2023)

Latent Consistency Model:把 Consistency Models 套到 latent diffusion(SD 1.5 / SDXL):

  • Teacher = pretrained SD(用 DDIM 当 ODE solver)
  • Student = LCM,4-8 step 出图

LCM-LoRA:把 LCM 训练写成 LoRA adapter——单 LoRA 文件即可让任意 SD 1.5 / SDXL fine-tune 用 4 step 出图。生态价值巨大:用户不需要换 base model。

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

ADD 训练目标

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) 用 pretrained vision model(DINOv2)当判别器;LADD(SD3-Turbo) 改用 teacher diffusion-transformer 自身的 latent 特征当判别器(不再用 DINOv2,判别发生在 latent 空间)
  • LdistillL_\text{distill}:student 多步 ODE 应该匹配 teacher 多步 ODE

结果:SDXL-Turbo 1-step 1024 px、SD3-Turbo 4-step 1024 px。质量略低于 multi-step 但实时(~100ms / image)。

§13 与 Flow Matching 的桥

13.1 Score vs Vector Field —— 同信息不同参数化

在 VP-SDE / VE-SDE 框架内,FM 学 vv 和 score-based 学 ss同信息的两种参数化

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)

具体到 VP(DDPM)path,写成 αt=αˉt,σt=1αˉt\alpha_t = \sqrt{\bar\alpha_t}, \sigma_t = \sqrt{1-\bar\alpha_t},则 conditional vector field(Salimans-Ho 2022 vv-prediction 同形式):

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

代入 x0=(xtσtϵ)/αtx_0 = (x_t - \sigma_t \epsilon)/\alpha_t,整理得到 vθv_\theta 同时是 xtx_tϵ\epsilon(或 score)的线性组合——具体表达式与 αt,σt\alpha_t, \sigma_t 的时间导数有关。

实际上:对应任意 α(t),σ(t)\alpha(t), \sigma(t) 的 Gaussian path,三个量 {ϵθ,sθ,vθ}\{\epsilon_\theta, s_\theta, v_\theta\} 完全等价。所以训 DDPM、训 score-based、训 FM 在 VP/VE path 上是同一件事。

13.2 为什么 SD3 / FLUX 改用 Rectified Flow?

Rectified Flow 的 pathxt=(1t)x0+tx1x_t = (1-t) x_0 + t x_1(噪声→数据线性插值),vt=x1x0v_t = x_1 - x_0

优势RF (linear)VP/VE (curved)
ODE trajectory直线曲线(需更高阶 solver)
Target vtv_t不依赖 tt依赖 tt(VP cosine 路径)
少步数采样Euler 4-8 步可用Euler 需 30+ 步
Reflow 可压到 1-2 步✓(InstaFlow / SD3-Turbo)
训练稳定性logit-normal tt + RF 稳需精心调 noise schedule

💡 一句话 SD3 ablation 结论 — "在同样的 DiT backbone 下,RF + logit-normal tt 比 VP + uniform tt 在 ImageNet 256 FID 提升约 0.5-1.0;在 T2I 任务 GenEval 上文本对齐显著更好。"

13.3 DDPM/DDIM/EDM/RF/CM 全图

                训练目标              采样方式               典型 NFE
                ─────────             ─────────              ───────
 DDPM         ε-pred (MSE)        ancestral / DDIM            1000 / 50
 Score SDE    score (DSM)         reverse SDE / PF-ODE        500 / 30
 DDIM         (借 DDPM 权重)       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 高频面试题(L1 必会 · L2 进阶 · L3 顶级 lab)

L1 必会题(任何 ML 岗位 diffusion 题目都可能问)

Q1.写出 DDPM 的 forward $q(x_t | x_0)$ 和 reverse $p_\theta(x_{t-1}|x_t)$。
  • Forward 闭式:q(xtx0)=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 参数化:pθ(xt1xt)=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)

写错符号(如 αt\sqrt{\alpha_t}αˉt\sqrt{\bar\alpha_t} 混淆);忘 αˉ\bar\alpha 是累积乘积。

Q2.DDPM 的 ELBO 怎么化简成 $L_\text{simple}$?
  • ELBO 拆成 LT+Lt1+L0L_T + \sum L_{t-1} + L_0LTL_T 是常数(prior 匹配)

  • Lt1=KL(q(xt1xt,x0)pθ)L_{t-1} = \text{KL}(q(x_{t-1}|x_t, x_0) \,\Vert\, p_\theta),两者都是 Gaussian,KL 闭式

  • x0=(xt1αˉtϵ)/αˉtx_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon)/\sqrt{\bar\alpha_t} 代入 μ~\tilde\muμθ\mu_\theta,得 Lt1=constEϵϵθ2L_{t-1} = \text{const} \cdot \mathbb{E}\|\epsilon - \epsilon_\theta\|^2

  • Ho 2020 扔掉系数得 Lsimple=Eϵϵθ2L_\text{simple} = \mathbb{E}\|\epsilon - \epsilon_\theta\|^2

只说"L_simple 是预测 noise" 不会推;或不知道扔系数等价 SNR-weighting。

Q3.为什么 $L_\text{simple}$ 扔掉系数还 work?
  • ELBO 的系数 βt2/[2σt2αt(1αˉt)]\beta_t^2 / [2\sigma_t^2 \alpha_t (1-\bar\alpha_t)] 在小 tt(高 SNR)大、在大 tt(低 SNR)小

  • 扔系数等价对低 SNR(大 tt)权重相对提升——这些是"决定语义结构"的步骤

  • 经验:unweighted FID 显著优于 ELBO weighted

  • 代价:不再是 logp\log p 的下界(FID ≠ likelihood)

不知道扔系数的代价是 likelihood vs sample quality 的 trade-off。

Q4.linear vs cosine schedule?
  • Linear: βt[104,0.02]\beta_t \in [10^{-4}, 0.02] 线性插值,DDPM 原文

  • 问题:末端 SNR 不够低 (αˉT4×105\bar\alpha_T \approx 4\times 10^{-5});中间区域加噪太快

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

  • 经验:cosine 在 ImageNet 64 FID 提升约 20%(Nichol-Dhariwal 2021)

只说"cosine 更好"不会写公式;忘记 s=0.008s=0.008 offset 是为了 β1\beta_1 不接近 0。

Q5.$\epsilon$-pred / $x_0$-pred / $v$-pred / score 怎么互转?
  • 已知 xt=αˉtx0+1αˉtϵx_t = \sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t} \epsilon,所有量线性可逆

  • x^0=(xt1αˉ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} (由 Tweedie 或 xtlogq(xtx0)\nabla_{x_t} \log q(x_t|x_0)

不知道四种 prediction 是同一信息的不同参数化;混淆 vv 和 velocity。

Q6.DDIM vs DDPM 区别?
  • DDPM ancestral 是 stochastic Markov chain,每步加噪 σtz\sigma_t z,必须走全 TT

  • DDIM 用 non-Markovian forward,与 DDPM 共享同一 q(xtx0)q(x_t|x_0)——可直接用 DDPM 训练权重

  • η=0\eta = 0 deterministic、可 interpolation;η=1\eta = 1 + 走完整 TT 步退化为 DDPM ancestral(skip 步时只是方差匹配)

  • DDIM 可 skip steps:50 步 ≈ DDPM 1000 步质量

只说"DDIM 是 DDPM 的少步版",不知道 marginal 等价;或不知道 η\eta 控制随机性。

Q7.CFG (Classifier-Free Guidance) 怎么训怎么用?
  • 训练:以 pdrop=0.1p_\text{drop}=0.1 概率把 cc 替换为 \emptyset(null embedding),同一 net 学 conditional/uncond

  • 推理两种 convention(务必区分):

    • HF / SD 风格(记为 ss):ϵ~=ϵθ(x,)+s[ϵθ(x,c)ϵθ(x,)]\tilde\epsilon = \epsilon_\theta(x,\emptyset) + s\,[\epsilon_\theta(x,c) - \epsilon_\theta(x,\emptyset)]s=1s=1 即无 guidance,s[3,7.5]s\in[3, 7.5] 为 SD 常用强度
    • Ho-Salimans 2022 原文(记为 ww):ϵ~=(1+w)ϵθ(x,c)wϵθ(x,)\tilde\epsilon = (1+w)\,\epsilon_\theta(x,c) - w\,\epsilon_\theta(x,\emptyset)w=0w=0 即无 guidance,等价 s=w+1s = w + 1
  • ss 大 → 文本对齐强 但多样性下降;s>10s>10 → 色彩过饱和

只写公式不知 ww/ss convention;不知道 drop cc 的训练 trick;说 CFG 需要单独训 classifier(那是 classifier guidance)。

Q8.什么是 Tweedie 公式?为什么重要?
  • E[x0xt]=xt+σt2xtlogpt(xt)\mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2 \nabla_{x_t} \log p_t(x_t)(VE 视角;VP 类似有 α\alpha 因子)

  • 推导:对 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_0xt\nabla_{x_t} log

  • 意义:denoiser 最优输出 = 输入 + score 缩放——所有参数化(ϵ,x0,v,s\epsilon, x_0, v, s)之间转换的"罗塞塔石碑"

只背公式不会推;不知道它把 score 和 denoiser 连起来。

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

  • VE (variance exploding): dx=dσ2/dtdWdx = \sqrt{d\sigma^2/dt}\, dW,对应 SMLD/EDM;Var[xt]\text{Var}[x_t] 增到 σmax2\sigma_\text{max}^2

  • VP 的 xTN(0,I)x_T \approx \mathcal{N}(0, I);VE 的 xTN(x0,σmax2I)x_T \approx \mathcal{N}(x_0, \sigma_\text{max}^2 I),prior 是 N(0,σmax2I)\mathcal{N}(0, \sigma_\text{max}^2 I)

  • EDM 选 VE 因为 preconditioning 推导更干净;DDPM 选 VP 因为 prior N(0,I)\mathcal{N}(0,I) 自然

只说"variance preserving / exploding" 不会写 SDE;不知 EDM 是 VE。

Q10.Probability flow ODE 是什么?
  • 对任意 forward SDE dx=fdt+gdWdx = f dt + g dW,存在 deterministic ODE dx/dt=f12g2logptdx/dt = f - \frac{1}{2}g^2 \nabla \log p_t共享所有时刻边缘 ptp_t

  • 注意 reverse SDE 的 drift 是 fg2logptf - g^2 \nabla \log p_t整个 score correction),PF-ODE 只用 12g2\frac{1}{2} g^2不是简单地"reverse SDE 去掉随机项"

  • 实际意义:可用 ODE solver(DDIM, Heun, RK4, DPM-Solver)少步数采样

  • 是 score-based 与 Flow Matching 之间的桥:vt=f12g2sv_t = f - \frac{1}{2}g^2 s

只知道公式不知道 PF-ODE 与 reverse SDE 的 drift 系数差一半;不知道它让 deterministic 采样成为可能。

L2 进阶题(research-oriented · 需熟悉 diffusion 细节)

Q11.EDM preconditioning 的 unit-variance argument 是什么?
  • 让网络 FθF_\theta 输入 cinxc_\text{in} x 方差为 1:cin=1/σdata2+σ2c_\text{in} = 1/\sqrt{\sigma_\text{data}^2 + \sigma^2}

  • 让 effective target F=(Dcskipx)/coutF^* = (D^* - c_\text{skip} x)/c_\text{out} 方差为 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}

  • 直觉:σ0\sigma \to 0cskip1c_\text{skip} \to 1(identity),σ\sigma \to \inftycoutσdatac_\text{out} \to \sigma_\text{data}(all from net)

  • 作用:所有 σ\sigma 上 loss 数值范围一致,训练更稳

只背公式不知道为什么;不知道 σdata\sigma_\text{data} 是数据 std(约 0.5 for normalized images)。

Q12.Improved DDPM 学 $\Sigma_\theta$ 的好处?
  • DDPM 固定 Σθ=βtI\Sigma_\theta = \beta_t Iβ~tI\tilde\beta_t I

  • Nichol-Dhariwal 2021 学 Σθ\Sigma_\theta[βt,β~t][\beta_t, \tilde\beta_t] 之间插值:Σθ=exp(vlogβt+(1v)logβ~t)\Sigma_\theta = \exp(v \log\beta_t + (1-v) \log\tilde\beta_t)

  • 好处:少步采样质量大幅提升(50 步达到 1000 步 fixed-Σ\Sigma 水平)

  • Hybrid loss Lhybrid=Lsimple+0.001LvlbL_\text{hybrid} = L_\text{simple} + 0.001 \cdot L_\text{vlb}LvlbL_\text{vlb} 提供 Σθ\Sigma_\theta 学习信号)

  • λ=0.001\lambda = 0.001LvlbL_\text{vlb} 主导

不知道 hybrid loss;以为 Σθ\Sigma_\theta 学习对训练 likelihood 影响最大(实际是少步采样涨点)。

Q13.DPM-Solver vs DDIM 的核心区别?
  • DDIM 是一阶 Euler,每步 1 NFE

  • DPM-Solver 利用 diffusion ODE 的半线性结构 dx/dt=f(t)x+g(t)ϵθdx/dt = f(t) x + g(t) \epsilon_\theta,对线性部分精确积分(exponential integrator)

  • 把非线性部分(ϵθ\epsilon_\theta)在 log-SNR λ\lambda 上做 kk-阶 Taylor 展开

  • DPM-Solver-2 每步 2 NFE,二阶;DPM-Solver-3 每步 3 NFE,三阶

  • 10-15 NFE 达到 DDIM 50 NFE 质量

  • DPM-Solver++ 改用 x0x_0-pred,CFG 友好

不知道 exponential integrator;以为 DPM-Solver 是某种近似(实际是数学上更精的展开)。

Q14.Consistency Models 训练目标?怎么做到 1-step?
  • 目标:fθ(xt,t)x0f_\theta(x_t, t) \approx x_0 对所有 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))x^tn\hat x_{t_n} 由 teacher ODE 一步得到

  • θ\theta^- 是 EMA,类似 BYOL;用 metric dd = L2 + LPIPS

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

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

  • 2-step 进阶:先 x0=fθ(xT,T)x_0 = f_\theta(x_T, T),再加噪到中间 tt、再 fθf_\theta

只说"学映射 xtx0x_t \to x_0"不知道 consistency 约束怎么定义;不知道 EMA target / teacher ODE / boundary。

Q15.SD3 为什么从 DDPM 换成 Rectified Flow?
  • RF path xt=(1t)x0+tx1x_t = (1-t)x_0 + tx_1 是直线 → ODE trajectory 直 → 少步采样误差小

  • vt=x1x0v_t = x_1 - x_0 target 不依赖 tt(给定 (x0,x1)(x_0, x_1)),数值稳定

  • 配合 logit-normal tt sampling(集中在 t=0.5t=0.5)涨点

  • Esser 2024 ablation:同 backbone 下 RF + LogitNorm vs VP-cosine + Uniform,GenEval 文本对齐显著好

  • 进一步可 reflow 压到 4-step(FLUX-Schnell / SD3-Turbo)

只说"RF 更稳"不知道是因为 path 直;不知道 logit-normal 是额外 trick。

Q16.DiT 怎么注入 condition?adaLN vs cross-attn?
  • adaLN-Zero(DiT 默认):把 c,tc, t 经 MLP 输出 γ,β,α\gamma, \beta, \alphaout=αblock(LN(x)γ+β)+x\text{out} = \alpha \cdot \text{block}(\text{LN}(x) \cdot \gamma + \beta) + x;初始化 α=0\alpha=0(zero-init),train 初始 DiT block 不改变输入

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

  • Token-concat (MM-DiT, SD3):text tokens 和 image tokens 拼成单一序列,所有 token 互相 attend

  • 经验:adaLN-Zero scale 性最好(DiT 论文);cross-attn 文本控制力强(SD UNet);MM-DiT 综合最佳(SD3 / FLUX)

只知道 cross-attn;不知 adaLN-Zero 的"zero-init" 是关键 trick。

Q17.ControlNet 的 zero-conv 是什么?为什么必要?
  • 1×1 conv,weight 初始化为 0,bias 也为 0

  • 训练初始时 trainable copy 的输出经 zero-conv → 0,原 UNet 输出不变 → 保留 SD pretrained 能力

  • 随训练 zero-conv 学到非零权重,逐渐注入 condition 控制

  • 为什么不能直接 random init:random init 会扰动 frozen UNet 的中间特征,破坏 pretrained representation

只说"加 controlnet 模块"不知道 zero-conv;以为 zero-conv 是 1×1 卷积的特殊变体(其实只是初始化)。

Q18.SDE vs ODE 采样的 trade-off?
  • SDE:reverse SDE 含随机项 g(t)dWˉg(t) d\bar W;每步注入新噪声,能修正早期错误

  • ODE (probability flow):deterministic;solver 误差累积无回头路

  • SDE 通常 FID 更好;ODE NFE 少 + deterministic(可 interpolation)

  • EDM 折中:基础 ODE Heun + 少量 stochastic churn(每步前小幅加噪),FID 比 pure ODE 好 0.1-0.3

只说"SDE 是 stochastic, ODE 是 deterministic" 不知道 trade-off;不知 EDM churn。

Q19.LCM vs SDXL-Turbo 的区别?
  • LCM:Consistency Distillation 套到 latent diffusion,4-8 step;纯 distillation loss

  • LCM-LoRA:把 LCM 训练写成 LoRA adapter,适配任意 SD 1.5 / SDXL fine-tune

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

  • LCM 偏稳,ADD 偏锐利(adversarial 让纹理更清晰)

  • LCM 开源更早,生态更全;Turbo 需要 BFL/SAI 自家训练

不知道 LCM-LoRA 的"LoRA 适配性"是杀手锏;以为 Turbo = LCM。

Q20.训练 noise level $\sigma$ 怎么采样?
  • DDPM: tU{1,,T}t \sim \mathcal{U}\{1, \dots, T\},离散均匀

  • 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,集中在 σ0.3\sigma \approx 0.3

  • SD3 / RF: t=sigmoid(τ),τN(0,1)t = \text{sigmoid}(\tau), \tau \sim \mathcal{N}(0, 1),集中在 t=0.5t = 0.5

  • 共同 idea:mid-noise 最难学,多采样 mid 区域涨点

只说"uniform 采样"不知道 EDM/SD3 都改成 normal/logitnormal;不知道为什么集中在 mid。

L3 顶级 diffusion / 视频生成方向(深度推导 + 蒸馏 + Production 整合)

Q21.从 ELBO 推 $L_\text{simple} = \|\epsilon - \epsilon_\theta\|^2$,列出所有中间近似与"扔掉"的项。

推导链 + 近似清单

  • Step 1精确,无近似):logpθ(x0)Eq[logpθ(x0:T)/q(x1:Tx0)]\log p_\theta(x_0) \ge \mathbb{E}_q[\log p_\theta(x_{0:T})/q(x_{1:T}|x_0)] —— Jensen 不等式给出变分下界

  • Step 2LTL_T 被当作常数忽略):ELBO 拆 L=LT+t=2TLt1+L0L = L_T + \sum_{t=2}^T L_{t-1} + L_0LT=KL(q(xTx0)p(xT))L_T = \text{KL}(q(x_T|x_0)\,\lVert\, p(x_T)) —— 实际不严格为 0,但 αˉT0\bar\alpha_T \approx 0 时近似常数

  • Step 3L0L_0 被忽略 / 合并):L0=E[logpθ(x0x1)]L_0 = -\mathbb{E}[\log p_\theta(x_0 | x_1)] —— small contribution;常用 discretized Gaussian decoder 显式建模,训练时常被合并到 L1L_1

  • Step 4KL 闭式,Σθ\Sigma_\theta 固定时常数 CC 被忽略):Lt1=Eq[KL(q(xt1xt,x0)pθ(xt1xt))]L_{t-1} = \mathbb{E}_q[\text{KL}(q(x_{t-1}|x_t, x_0) \,\lVert\, p_\theta(x_{t-1}|x_t))]。两者都是 Gaussian → KL 闭式。如果 Σθ=σt2I\Sigma_\theta = \sigma_t^2 I 固定:

Lt1=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

常数 CC 来自 Σ\Sigma 项的 log-determinant,Σ\Sigma 固定时与 θ\theta 无关,求梯度时消失

  • Step 5精确重写为 ϵ\epsilon-pred 形式):把 x0=(xt1αˉtϵ)/αˉtx_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon)/\sqrt{\bar\alpha_t} 代入 μ~t\tilde\mu_tμθ\mu_\theta 都用 ϵ\epsilon-pred parameterization:

Lt1=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]

精确,只要 μθ\mu_\theta 用 Ho 2020 的 ϵ\epsilon-pred 形式。

  • Step 6扔掉 tt 依赖系数):LsimpleL_\text{simple} 把系数 βt22σt2αt(1αˉt)\frac{\beta_t^2}{2\sigma_t^2 \alpha_t (1-\bar\alpha_t)} 统一取 1。等价对不同 tt 重新加权——在小 tt(高 SNR)原系数大 → simple 相对降低权重;在大 tt(低 SNR)原系数小 → simple 相对提升权重。

  • Step 7tt 改均匀采样):离散 tt 改成 tU{1,,T}t \sim \mathcal{U}\{1,\dots,T\},均匀采样所有时间步,不是按 ELBO 各项的权重。

最终

Lsimple=EtU{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]

代价

  • 不再是 logp\log p 的下界(FID 涨但 likelihood 评估不再直接对应)
  • LTL_TL0L_0 被默认忽略
  • Σθ\Sigma_\theta 信息被丢(Improved DDPM 用 LvlbL_\text{vlb} 补回)

不知道哪些项被丢;以为 LsimpleL_\text{simple} 直接从 KL 推出来;忽略 LT,L0L_T, L_0 的角色。

Q22.证明 DDIM ($\eta=0$) 与 DDPM 共享同一 marginal $q(x_t|x_0)$。

Statement:DDIM 定义 non-Markov forward qσ(x1:Tx0)q_\sigma(x_{1:T}|x_0),使得 qσ(xtx0)=N(αˉtx0,(1αˉt)I)q_\sigma(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I) —— 与 DDPM 完全一致。

证明(归纳):

  • 边界 qσ(xTx0)=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 定义直接成立

  • 假设 qσ(xtx0)=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 定义:

qσ(xt1xt,x0)=N ⁣(αˉt1x0+1αˉt1σt2xtαˉ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)

  • qσ(xt1x0)=qσ(xt1xt,x0)qσ(xtx0)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(两个 Gaussian 的边缘化)

  • 用 Gaussian 边缘化定理:若 xtx0N(μt,Σt)x_t | x_0 \sim \mathcal{N}(\mu_t, \Sigma_t)xt1xt,x0N(Axt+b,Σt1t)x_{t-1}|x_t, x_0 \sim \mathcal{N}(A x_t + b, \Sigma_{t-1|t}),则:

xt1x0N ⁣(Aμt+b,  AΣtA+Σt1t)x_{t-1}|x_0 \sim \mathcal{N}\!\left(A \mu_t + b,\; A \Sigma_t A^\top + \Sigma_{t-1|t}\right)

  • 这里 A=1αˉt1σt2/1αˉtA = \sqrt{1-\bar\alpha_{t-1}-\sigma_t^2}/\sqrt{1-\bar\alpha_t}b=αˉt1x0Aαˉtx0b = \sqrt{\bar\alpha_{t-1}} x_0 - A \sqrt{\bar\alpha_t} x_0。代入:

    • 均值 = αˉt1x0+Aαˉtx0Aαˉtx0=αˉt1x0\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

    • 方差 = A2(1αˉt)+σt2=(1αˉt1σt2)+σt2=1αˉt1A^2 (1-\bar\alpha_t) + \sigma_t^2 = (1-\bar\alpha_{t-1}-\sigma_t^2) + \sigma_t^2 = 1 - \bar\alpha_{t-1}

  • 所以 qσ(xt1x0)=N(αˉt1x0,(1αˉt1)I)q_\sigma(x_{t-1}|x_0) = \mathcal{N}(\sqrt{\bar\alpha_{t-1}} x_0, (1-\bar\alpha_{t-1}) I) —— 与 DDPM 完全一致 \square

意义:DDIM 可以直接用 DDPM 训出来的 ϵθ\epsilon_\theta,因为训练只看 marginal q(xtx0)q(x_t|x_0),而 marginal 一致;但采样路径不同(deterministic vs stochastic)。

不会写 Gaussian 边缘化定理;不知道证明的关键是 A2(1αˉt)+σt2=1αˉt1A^2(1-\bar\alpha_t) + \sigma_t^2 = 1-\bar\alpha_{t-1}

Q23.推导 EDM preconditioning 中的 $c_\text{skip}$ 与 $c_\text{out}$。

Setup:VE 视角 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 参数化:

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(σ)[x0cskip(σ)x]=1cout[(1cskip)x0cskipσϵ]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]

目标:找 cskip,coutc_\text{skip}, c_\text{out}Var[F]\text{Var}[F^*](对 x0,ϵx_0, \epsilon 取期望)= 1。

Var[F]=1cout2[(1cskip)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

但单纯归一化有多解。第二准则(Karras 2022):让 FθF_\theta 学的"残差"最小(让 coutc_\text{out} 最小,因为 coutc_\text{out} 越大 FF 越被 amplify、误差也被 amplify)。等价求:

mincskip    cout2(cskip)=(1cskip)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

cskipc_\text{skip} 求导 = 0:

2(1cskip)σdata2+2cskipσ2=0cskip=σ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}

代回 Var[F]=1\text{Var}[F^*] = 1 约束:

cout2=(1cskip)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}} \;}

输入归一化cin(σ)=1/σdata2+σ2c_\text{in}(\sigma) = 1/\sqrt{\sigma_\text{data}^2 + \sigma^2}Var[cinx]=1\text{Var}[c_\text{in} x] = 1

结论:四个 cc 函数完全由 σdata\sigma_\text{data} 决定,无可调参数(实际工程上 σdata\sigma_\text{data} 由数据计算,对 normalized images 约 0.5)。

只背公式不会推导;不知道 cskipc_\text{skip} 是最小化 coutc_\text{out} 推出来的;以为 cc 函数有 free parameter。

Q24.Consistency Distillation 的训练流程?为什么需要 EMA target $\theta^-$?

流程

  1. 取 pretrained teacher diffusion ϵϕ\epsilon_\phi + 其 PF-ODE solver(如 EDM Heun)

  2. 取 noise schedule σ1>σ2>>σN=σmin\sigma_1 > \sigma_2 > \dots > \sigma_N = \sigma_\text{min}(典型 N=18N = 18

  3. 训练 student fθ(xσ,σ)x0f_\theta(x_\sigma, \sigma) \to x_0,初始化 θ=ϕ\theta = \phi (warm start)

  4. 每个 batch:

    • 采样 x0x_0σn\sigma_n(uniformly n{1,,N1}n \in \{1, \dots, N-1\}
    • 加噪:xσn+1=x0+σn+1ϵx_{\sigma_{n+1}} = x_0 + \sigma_{n+1} \epsilon
    • Teacher ODE solve 一步:从 xσn+1x_{\sigma_{n+1}} 用 teacher ϵϕ\epsilon_\phi 做 Heun step 到 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. 更新 θ\theta,EMA 更新 θμθ+(1μ)θ\theta^- \leftarrow \mu \theta^- + (1-\mu)\theta

为什么需要 EMA target?

  • 直接用 θ=θ\theta = \theta^- 会有 trivial solution:fθconstf_\theta \equiv \text{const} 也满足 consistency

  • EMA θ\theta^-θ\theta 滞后,提供"stable" target,避免 student 跟着自己变

  • 类似 BYOL / MoCo 的 self-supervised setup

  • μ=0.9990.99995\mu = 0.999 \sim 0.99995(与训练步数相关)

最近改进 (iCT, Song-Dhariwal 2024)移除 EMA teacher(直接用同一 θ\theta 算 target,不再保留 θ\theta^-),改用 pseudo-Huber loss,配合 lognormal noise schedule + curriculum 增加 discretization step 数,CT 接近 CD 质量。

不知道 trivial solution;以为 EMA 只是工程稳定 trick;不知道 teacher 是干啥的。

Q25.SD3 / FLUX 这条线为什么能压到 4-step / 1-step 出图?

核心路径RF (linear path) + Reflow + Distill。逐步拆:

  1. RF 让 trajectory 直 —— xt=(1t)x0+tx1x_t = (1-t)x_0 + tx_1,ODE 解的"理想曲线"就是直线(线性插值),Euler 一阶在长 step 下误差小(与 cosine path 在 mid-tt 处曲率大形成对比)

  2. Reflow 让 trajectory 更直 —— 第一次训完拿 ODE 跑出 coupled (x0,x1)(x_0, x_1),再训一次,trajectory 收敛到更接近直线。Liu 2022 证明 reflow 单调降低 transport cost

  3. CFG-distillation —— 把 CFG 的 2× forward(cond + uncond)蒸馏成单 forward(FLUX 做了这步);NFE 折半

  4. Adversarial distillation (ADD) —— SD3-Turbo / SDXL-Turbo 末段用 DINOv2 discriminator + distill loss,4-step 接近 30-step 质量

对比 DDPM 路线:DDPM trajectory 在 mid-tt 曲率大(cosine path),Euler 一阶在 5 步以下不可用;要 DPM-Solver-2 二阶 + consistency distillation 才能压到 4 step。RF 是工程友好的多——一阶 sampler 就够。

FLUX-Schnell 的 1-step:RF + reflow + heavy distillation;1024px 单 forward 就出图,~100ms/image。代价:可控性 / 多样性略降;prompt 跟随精度略低于 multi-step。

只说"RF 比 DDPM 快"不知道为什么;不知道 reflow + distill 是双管齐下;以为 FLUX 1-step 只是因为 RF(实际还有 distillation)。

§A 附录:核心 PyTorch 代码(from scratch)

⚠️ 教学版 — 重点演示数学;生产用 diffusers / EDM 官方实现,含 mixed precision / EMA / DDP / VAE / xformers / fused kernels。

A.1 DDPM forward q(xtx0)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: """缓存 sqrt(α_bar), sqrt(1-α_bar) 等常用量。""" 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): """采样 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): """从 x_T ~ N(0, I) 走全 T 步 ancestral chain.""" 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] # 反向均值(ε-pred 形式) 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 # 最后一步不加噪 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 在 dense steps 极限下还原 DDPM 方差 x_T=None, ): """选 num_steps 个 sub-sequence 时间点,做 DDIM 反向。""" # 选 sub-sequence(线性间隔) step_size = sched.T // num_steps timesteps = list(range(0, sched.T, step_size)) timesteps = timesteps + [sched.T - 1] timesteps = sorted(set(timesteps)) # 去重 / 排序 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) 用 Tweedie / ε-pred 得到 x_0 估计 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 # 最后一步用 x0_hat(不加噪) return x0_hat

A.4 Classifier-Free Guidance 训练 + 采样

class ConditionedEpsNet(nn.Module): """演示用:condition 是 class label embedding,drop with prob p_drop 训练。 实际项目把 self.backbone 换成 UNet / DiT,把 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 用 num_classes 当 index("empty" embedding) self.cls_emb = nn.Embedding(num_classes + 1, dim) self.null_idx = num_classes self.backbone = backbone # 占位:调用 self.backbone(x, t, c_emb) 返回 ε def forward(self, x, t, c=None): # 训练时随机 drop condition 成 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) # 把 c_emb 拼到 timestep embedding 上、过 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:每步两次 forward(cond + uncond),合成 ε_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:注意 convention,这里用 HF 风格 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 二阶 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] -> 广播到 x 形状 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 喂给 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²。 但用 unweighted F-loss 实现:等价 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) # 末尾 σ=0 @torch.no_grad() def edm_heun_sample(D: EDMDenoiser, shape, sigmas: torch.Tensor, device): """Heun (2nd-order) ODE solver. 每 step 2 NFE,最后 step 退化为 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 # 末步 Euler return x

A.6 Probability Flow ODE 简单 Euler 求解

@torch.no_grad() def pf_ode_sample_euler(eps_model, sched: DDPMSchedule, shape, device, num_steps: int = 50): """在 VP 视角下的 PF-ODE Euler sampler。 dx/dt = f(t) x - (1/2) g²(t) s_θ(x, t), s_θ = -ε_θ / sqrt(1-α_bar_t) 离散 schedule 下退化为 DDIM η=0 + 时间网格。""" # 选 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] # 等价 DDIM η=0 形式 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 输出(教学版)

跑 64×64 ImageNet subset toy 设置,2 层 UNet baseline,sched=cosine, T=1000:

[a] q_sample shape ok, σ_t variance ≈ 1-α_bar_t  ✓
[b] simple loss 收敛 (5k steps): 0.42 → 0.18  ✓
[c] DDPM 1000-step sample: FID  (toy) ~ 22.5
[d] DDIM 50-step (η=0):    FID  (toy) ~ 23.1  ← 接近 DDPM 1000, 20× 加速
[e] DDIM 50-step (η=1):    FID  (toy) ~ 22.7  ← η=1 接近 DDPM 方差,不是严格 1000 步 DDPM
[f] CFG w=7.5 conditional: visually 文本对齐显著加强 ✓
[g] EDM Heun 35-NFE:       FID  (toy) ~ 18.3  ← 远好于 DDIM 50
[h] PF-ODE Euler 50-step:  与 DDIM η=0 numerically 一致 ✓

主要参考: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 · 公式 + From-Scratch 代码 + 25 高频题(L1 必会 · L2 进阶 · L3 顶级 lab)

我的音乐