§0 TL;DR
💡 9 句话搞定 Diffusion 基础 — 一页拿下面试核心要点(详见 §1–§13 推导)。
-
DDPM (Ho 2020):forward q(xt∣x0)=N(αˉtx0,(1−αˉt)I) 闭式可采样;reverse pθ(xt−1∣xt)=N(μθ,Σθ) 学反向 Gaussian;ELBO 化简到 Lsimple=E∥ϵ−ϵθ(xt,t)∥2(ϵ-prediction)。
-
三种视角等价:DDPM 的 ϵ、score-based 的 s=∇logpt、flow matching 的 v 在 Gaussian path 下线性可逆 —— sθ=−ϵθ/σt,v=α′x0+σ′ϵ。
-
Tweedie 公式:E[x0∣xt]=xt+σt2∇xtlogpt(xt) —— 一行式连接 denoiser 与 score。
-
Score SDE (Song 2021):VP-SDE / VE-SDE 统一框架;reverse-time SDE 与 probability flow ODE 共享同一族边缘分布,ODE 形式直接给出 FM 的 vector field。
-
DDIM (Song 2020 / ICLR 2021):non-Markovian forward 推出 deterministic sampler,marginal 与 DDPM 相同但采样路径可控(η=0 确定性;η=1 + 走完整 T 步退化为 DDPM ancestral,skip 步下则只是匹配 DDPM 方差,不严格等价)。
-
EDM (Karras 2022):preconditioning 让网络输出方差恒为 1:Dθ(x;σ)=cskip(σ)x+cout(σ)Fθ(cin(σ)x,cnoise(σ));配合 σ-schedule + Heun 2nd-order,FID SOTA 同时 NFE 降到 18-35。
-
CFG (Ho-Salimans 2022):训练时以概率 pdrop drop 条件 → 同一 net 学 conditional/unconditional;推理 ϵ~=(1+w)ϵθ(x,c)−wϵθ(x,∅),w∈[3,7] 是 text-to-image 主力。
-
Production:SD/SDXL 用 VAE latent + UNet;SD3 / FLUX.1 改用 Rectified Flow + MM-DiT;ControlNet 给 frozen UNet 加可训练 side branch;DiT 把 UNet 全换 Transformer。
-
加速:DPM-Solver++ 把 NFE 压到 10-20;Consistency Models 学 fθ(xt,t)↦x0 做到 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)
- 网络预测什么(ϵ / x0 / v / score / D)
- 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 同信息,只是参数化成 v 不是 s。Rectified Flow 跳出 SDE 框架,用线性 path 直接学 ODE 的 vector field。"
1.3 Convention(全文统一)
| 符号 | 含义 |
|---|
| x0 | 干净数据样本 |
| xt, t∈{1,…,T} 或 t∈[0,T] | 加噪后的样本 |
| ϵ∼N(0,I) | 标准 Gaussian 噪声 |
| αt,βt=1−αt | DDPM 单步 forward 系数 |
| αˉt=∏s=1tαs | DDPM cumulative 系数 |
| σt | 标准差(NCSN / EDM 视角的"噪声水平") |
| sθ(xt,t)≈∇xtlogpt(xt) | score |
| ϵθ(xt,t)≈ϵ | DDPM 中预测的噪声 |
| Dθ(x;σ)≈x0 | EDM 的 denoiser 输出 |
⚠️ 时间方向陷阱 — DDPM 论文 forward 是 t=0→T(数据加噪到纯噪声),reverse 是 T→0;FM 论文常用 t=0 噪声、t=1 数据。面试写代码前一定要先 disambiguate 时间方向——否则 sampler 容易跑反。
§2 DDPM Forward Process
2.1 单步与闭式表达
DDPM forward 是一条 Markov chain:
q(xt∣xt−1)=N(xt;1−βtxt−1,βtI),t=1,…,T
定义 αt=1−βt,αˉt=∏s=1tαs。关键性质:q(xt∣x0) 是 闭式 Gaussian——可以一步从 x0 跳到任意 t(训练效率的核心):
q(xt∣x0)=N(xt;αˉtx0,(1−αˉt)I)
等价 reparameterization:
xt=αˉtx0+1−αˉtϵ,ϵ∼N(0,I)
2.2 闭式推导(必考,会反复出现)
由 reparameterization xt=αtxt−1+βtzt,zt∼N(0,I) 独立。递推:
xt=αtxt−1+βtzt=αt(αt−1xt−2+βt−1zt−1)+βtzt=αtαt−1xt−2+独立 Gaussian 之和αtβt−1zt−1+βtzt
两个独立 Gaussian 之和的方差:αtβt−1+βt=αt(1−αt−1)+(1−αt)=1−αtαt−1。所以可以合并成单个 Gaussian 1−αtαt−1zˉ。归纳到 t 步:
xt=αˉtx0+1−αˉtϵ
💡 变分 trick 直觉 — Markov chain 的好处是每一步都是 Gaussian,所以累积仍是 Gaussian;这让 forward 不用网络就能采样、训练时不用模拟整条链。
2.3 边界与极限
- t=0:αˉ0=1,x0 自身 —— forward 起点
- t=T(DDPM 取 1000):要求 αˉT≈0,则 xT≈ϵ∼N(0,I) —— forward 终点接近 Gaussian prior
⚠️ Schedule 末端的 SNR (Signal-to-Noise Ratio) — SNR(t)=αˉt/(1−αˉt);linear schedule 在 t=T 时 αˉT≈4×10−5 对应 SNR ≈4×10−5——虽然很小但严格意义上未到 0,prior 仍非完全匹配 N(0,I);这是 cosine schedule 与 "v-prediction" 改进的动机之一。
§3 DDPM Reverse Process & 训练
3.1 Reverse 是 Gaussian 的前提
理论上 q(xt−1∣xt) 不是 Gaussian(依赖整个数据分布)。但当 βt 足够小时,反向条件分布 近似 是 Gaussian(Feller 1949 / Sohl-Dickstein 2015),所以参数化为:
pθ(xt−1∣xt)=N(xt−1;μθ(xt,t),Σθ(xt,t))
3.2 ELBO 推导
DDPM 优化 evidence lower bound(与 VAE 类似):
logpθ(x0)≥Eq(x1:T∣x0)[logq(x1:T∣x0)pθ(x0:T)]=−LT(常数,prior 匹配)Eq[KL(q(xT∣x0)∥p(xT))]−t=2∑TLt−1Eq[KL(q(xt−1∣xt,x0)∥pθ(xt−1∣xt))]+L0(decoder log-likelihood)Eq[logpθ(x0∣x1)]
核心:q(xt−1∣xt,x0) 是闭式 Gaussian(由 Bayes 推得):
q(xt−1∣xt,x0)=N(xt−1;μ~t(xt,x0),β~tI)
其中:
μ~t(xt,x0)=1−αˉtαˉt−1βtx0+1−αˉtαt(1−αˉt−1)xt,β~t=1−αˉt1−αˉt−1βt
3.3 化简到 Lsimple(必考推导)
把 x0=(xt−1−αˉtϵ)/αˉt 代入 μ~t:
μ~t=αt1(xt−1−αˉtβtϵ)
参数化 μθ(xt,t) 也采用同样形式(ϵ-prediction):
μθ(xt,t)=αt1(xt−1−αˉtβtϵθ(xt,t))
固定 Σθ=σt2I(取 σt2=βt 或 β~t)。两个 Gaussian 的 KL:
Lt−1=E[2σt21∥μ~t−μθ∥2]=E[2σt2αt(1−αˉt)βt2∥ϵ−ϵθ(xt,t)∥2]
Ho 2020 的工程妙招:扔掉前面所有系数 + 常数项,直接用 unweighted 版本:
Lsimple(θ)=Et∼U{1,…,T},x0,ϵ[ϵ−ϵθ(αˉtx0+1−αˉtϵ,t)2]
✅ 为什么扔系数也能 work? —— Ho 2020 经验观察:unweighted 版本相当于给低 SNR(高 t)loss 更大权重,反而提升 sample 质量。但代价是 log-likelihood 不再是 ELBO 下界——所以"FID 好"≠"likelihood 好"。后续 Improved DDPM (Nichol-Dhariwal 2021) 引入 hybrid loss Lhybrid=Lsimple+λLvlb(λ=0.001),同时学 Σθ。
3.4 预测目标的等价转换(必背)
给定 xt=αˉtx0+1−αˉtϵ,三种主流参数化线性可逆:
ϵ-predx0-predv-pred (Salimans-Ho 2022)score:ϵθ(xt,t)≈ϵ:x^0(xt,t)=αˉtxt−1−αˉtϵθ:vθ=αˉtϵ−1−αˉtx0:sθ(xt,t)=−1−αˉtϵθ(xt,t)
💡 v-prediction 为何更稳? —— ϵ-pred 在 t→0(小噪声)时退化(loss 系数爆炸);x0-pred 在 t→T(大噪声)时退化;v-pred 是两者插值,在所有 t 上 loss 数值范围近似一致——是 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
T=1000。简单、稳定,但末端 SNR 未严格到 0(αˉT≈4×10−5,对应 SNR ≈4×10−5,理想 prior 要求更接近 0)。
4.2 Cosine (Nichol-Dhariwal 2021)
αˉt=f(0)f(t),f(t)=cos2(1+s(t/T)+s⋅2π),s=0.008
βt=1−αˉt/αˉt−1(再裁剪到 [0,0.999] 防止数值问题)。s=0.008 是为了让 β1 不要太接近 0。
✅ Cosine schedule 为什么更好? —— linear schedule 在低 t 区域加噪太快,模型大部分时间"练"在已经全是噪声的区域(学不到东西)。cosine 在低 t 加噪缓慢、中间快、末端 SNR 真的接近 0。Improved DDPM 实验:cosine 比 linear 在 ImageNet 64 上 FID 提升约 20%。
4.3 EDM σ-schedule (Karras 2022)
EDM 把 β schedule 重新参数化为 σ schedule(直接用 σ 当时间)。采样时:
σi=(σmax1/ρ+N−1i(σmin1/ρ−σmax1/ρ))ρ,i=0,…,N−1
默认 σmin=0.002, σmax=80, ρ=7。ρ=7 是 Karras 实验扫出来的——比线性 / 对数都好,因为它把更多步骤分配在小 σ(高 SNR)区域,那里步进误差更敏感。
💡 离散 vs 连续 schedule — DDPM 的 β 数组等价于 VP-SDE 的 β(t)=Tβ⌊tT⌋;EDM 的 σ-schedule 等价于 VE-SDE 的 σ(t)=t(线性时间);两者只差一个 t 重参数化,信息上等价。EDM 的贡献是发现一组工程上更稳的 σi 选取规则。
§5 Score-based 视角
5.1 Score 与 score matching (Hyvärinen 2005)
定义 s(x)=∇xlogp(x)。如果学到 sθ≈s,可以用 Langevin dynamics 采样:
xk+1=xk+2ηsθ(xk)+ηzk,zk∼N(0,I)
直接 score matching loss Ep∥sθ−∇logp∥2 不可计算(不知道 ∇logp)。Hyvärinen 2005 给出 implicit score matching 通过积分变换避开 ∇logp:
Ep[∥sθ(x)∥2+2tr(∇xsθ(x))]
但 tr(∇xsθ) 在高维下太贵(Hessian trace)。
5.2 Denoising Score Matching (Vincent 2011)
对每个数据点 x0,加噪 x~=x0+σϵ,定义 perturbed distribution pσ(x~)=∫p(x0)N(x~;x0,σ2I)dx0。Vincent 2011 证明:
Epσ(x~)∥sθ(x~)−∇logpσ(x~)∥2=Ex0,x~∥sθ(x~)−∇x~logq(x~∣x0)∥2+const
而 q(x~∣x0)=N(x0,σ2I) 的 score 闭式:
∇x~logq(x~∣x0)=−σ2x~−x0=−σϵ
所以训练 loss 简化为:
LDSM(θ)=Ex0,σ,ϵ∥σsθ(x~;σ)+ϵ∥2
这正是 NCSN / SMLD 的训练目标(差一个权重)。
5.3 Tweedie 公式(必考推导)
陈述:对加性 Gaussian 噪声 xt=x0+σtϵ(VE 视角,ϵ∼N(0,I)):
E[x0∣xt]=xt+σt2∇xtlogpt(xt)
推导:pt(xt)=∫p0(x0)N(xt;x0,σt2I)dx0。对 xt 求梯度:
∇xtpt(xt)=∫p0(x0)⋅∇xtN(xt;x0,σt2I)dx0=∫p0(x0)⋅N(xt;x0,σt2I)⋅σt2x0−xtdx0
两边除以 pt(xt):
∇xtlogpt(xt)=pt(xt)1∫p0(x0)N(xt∣x0)σt2x0−xtdx0=Ep0(x0∣xt)[σt2x0−xt]
即:
σt2∇xtlogpt(xt)=E[x0∣xt]−xt⇒E[x0∣xt]=xt+σt2∇xtlogpt(xt)□
✅ Tweedie 是连接所有 diffusion 参数化的"罗塞塔石碑" —— denoiser 网络的最优输出(MMSE estimator)就是 score 加上恒等映射。所有 ϵ-pred / score-pred / x0-pred / v-pred 之间的转换都是 Tweedie 的一行式重排。
5.4 NCSN / SMLD (Song-Ermon 2019)
Noise-Conditional Score Network:训练一个共享网络 sθ(x,σ),对一组噪声水平 σ1>σ2>⋯>σL 同时做 DSM。采样时做 annealed Langevin dynamics:先在大 σ1 上 Langevin(探索全空间),逐步降到 σL(精修细节)。
x←x+2ϵisθ(x,σi)+ϵiz,ϵi=η⋅(σi/σL)2
每个 σi 跑 T 步 Langevin,然后切到下一个 σi+1。
⚠️ 为什么单一 σ 不行? —— 小 σ 训出来的 score 在远离数据流形的地方完全错(mode 之间的"空地带" p(x)≈0,score 给不出方向)。多 noise level 的核心是用大 σ 把空间"填满",给小 σ 提供初始位置。
§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)dW
| Type | f(x,t) | g(t) | 对应离散版 |
|---|
| VP-SDE (variance preserving) | −21β(t)x | β(t) | DDPM |
| VE-SDE (variance exploding) | 0 | d[σ2(t)]/dt | SMLD / EDM |
| sub-VP | −21β(t)x | β(t)(1−e−2∫0tβ(s)ds) | 介于 VP/VE,likelihood 更好 |
VP-SDE 满足 Var[xt]≤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ˉ
dWˉ 是 reverse-time Wiener process。采样:从 xT∼pT(接近 prior)开始,用 SDE solver(Euler-Maruyama / predictor-corrector)积分到 t=0。
6.3 Probability Flow ODE(与 FM 的桥梁)
关键定理(Song et al. 2021, "Score-Based Generative Modeling through SDEs"):以下确定性 ODE 与 reverse SDE 共享所有时刻的边缘 pt:
dtdx=f(x,t)−21g2(t)∇xlogpt(x)
这就是 probability flow ODE。等价于 Flow Matching 的 vector field:
ut(x)=f(x,t)−21g2(t)sθ(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(连续性方程):
∂t∂pt=−∇⋅(fpt)+21g2Δpt
利用 Δpt=∇⋅(pt∇logpt) 把扩散项写成 transport 形式:
∂t∂pt=−∇⋅[(f−21g2∇logpt)pt]
这正是 ODE dx/dt=f−21g2∇logpt 的连续性方程——所以它们的 pt 一致。
6.4 ODE 视角的优势
| 优势 | 说明 |
|---|
| Deterministic | 同一 noise → 同一 sample,可做 image editing / interpolation |
| NFE 友好 | 高阶 ODE solver(Heun / RK4 / DPM-Solver)少步数即可 |
| Likelihood 可计算 | logp0(x0)=logpT(xT)+∫0T∇⋅vt(x(t))dt(PF-ODE 的 instantaneous change-of-variables,Chen et al. 2018),用 Hutchinson trace estimator 估计 div |
| 桥到 FM | RF / SD3 / FLUX 走这条线 |
⚠️ SDE vs ODE 的 trade-off — SDE 采样的随机扰动可以"修正"早期错误,通常 sample 质量更高但 NFE 大;ODE deterministic 但易受 solver 误差累积,需更高阶 solver。EDM 提出折中:基础 ODE + 小幅 stochastic churn("Schurn"),FID 更好。
§7 DDIM:Non-Markovian Forward → Deterministic Sampler
7.1 Motivation
DDPM ancestral sampling 必须走 T=1000 步(Markov 链)。能否少步数采样且不重训?DDIM (Song et al. 2020 arXiv / ICLR 2021) 给出"yes"——核心是把 forward 改成 non-Markovian,但保持与 DDPM 一样的 marginal q(xt∣x0)。
7.2 Non-Markovian Forward
DDIM 定义一族 forward distribution,由参数 η∈[0,1] 控制:
qσ(xt−1∣xt,x0)=N(xt−1;αˉt−1x0+1−αˉt−1−σt21−αˉtxt−αˉtx0,σt2I)
其中 σt2=η2⋅β~t=η2⋅1−αˉt1−αˉt−1βt。
关键性质(DDIM Theorem 1):在此 forward 下,q(xt∣x0) 仍是 N(αˉtx0,(1−αˉt)I)——与 DDPM 完全一致!所以可以直接用 DDPM 训练的 ϵθ 做 DDIM 采样。
7.3 DDIM 采样公式
把 x0→x^0=(xt−1−αˉtϵθ(xt,t))/αˉt 代入:
xt−1=αˉt−1x^0+1−αˉt−1−σt2ϵθ(xt,t)+σtz,z∼N(0,I)
- η=0(DDIM):σt=0,确定性——同 xT 同 x^0(latent space interpolation 友好)
- η=1(走完整 T 步):σt=β~t,退化为标准 DDPM ancestral sampler;skip 步数 S<T 时只是匹配方差量级,不严格等价 1000 步 DDPM
- 中间 η∈(0,1):随机性可调
7.4 Skip steps(少步数采样)
不必逐步 t→t−1,可以跳:选 sub-sequence τ0<τ1<⋯<τS=T,做:
xτi−1=αˉτi−1x^0+1−αˉτi−1−στi2ϵθ(xτi,τi)+στiz
经典 baseline:S=50 步 DDIM 在 ImageNet 256 上 FID 接近 DDPM 1000 步。
✅ DDIM = probability flow ODE 的离散化 — 当 η=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+σϵ,ϵ∼N(0,I),σ 直接当 noise level(没有 α)。
Denoiser 参数化:
Dθ(x;σ)=cskip(σ)x+cout(σ)Fθ(cin(σ)x,cnoise(σ))
其中 Fθ 是底层网络,四个 c 函数是 手工 schedule。Karras 推导:
推导:unit-variance 论证
目标:让 Fθ 的输入和训练 target 在所有 σ 上方差都是 O(1)。
输入侧:网络看到的输入 cinx。已知 Var[x]=σdata2+σ2(数据方差 + 噪声方差),所以:
cin(σ)=σdata2+σ21⇒Var[cinx]=1
输出侧:理想 denoiser D∗(x;σ)=E[x0∣x](Tweedie)。我们让网络学残差而非全量:定义 effective target
F∗(x;σ)=cout(σ)1[D∗(x;σ)−cskip(σ)x]
希望 Var[coutF∗+cskipx−D∗]=0 且 Var[F∗]=1(让网络的 target 单位方差)。
求最小 effective error 的 cskip 与 cout(最小化 E∥F∗−Fθ∥2 在 Var[F∗]=1 约束下)。Karras 取 D∗=x0(理想情形),代入并展开:
cskip(σ)=σ2+σdata2σdata2,cout(σ)=σ2+σdata2σ⋅σdata
直觉:
- σ→0(低噪声):cskip→1,cout→0 —— 输出基本是 input identity(denoiser 啥都不用做)
- σ→∞(高噪声):cskip→0,cout→σdata —— 输出由 network 完全决定(input 全是噪声)
时间编码:cnoise(σ)=41lnσ(log-scale,覆盖 σ∈[σmin,σmax] 的宽动态范围)。
8.3 训练 loss
EDM 用 weighted L2:
LEDM(θ)=Eσ,x0,ϵ[λ(σ)Dθ(x0+σϵ;σ)−x02]
权重 λ(σ)=(σ2+σdata2)/(σ⋅σdata)2=1/cout2,等价 训练 Fθ 用 unweighted L2(每个 σ 上 target 单位方差,loss 数量级一致)。
σ 训练采样:lnσ∼N(Pmean,Pstd2),默认 Pmean=−1.2, Pstd=1.2(让 σ 集中在 0.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/dt 下:
dσdx=−σ∇xlogpσ(x)=σx−Dθ(x;σ)
(用 Tweedie:∇logpσ=(D−x)/σ2,代入 dx/dσ=−σ∇logp)
Heun 第 i 步(σi→σi+1,Δσ=σi+1−σ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 临时提高到 σ^i=(1+γi)σi(γi 是当前步的小幅 churn),需注入额外噪声:x^i=xi+σ^i2−σi2z,其中 σ^i2−σi2=σi2γi+γi2;从 σ^i 降回 σ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 下用 ϵ-pred 改写:
dtdx=f(t)x+g(t)ϵθ(x,t)
其中 f(t)=−21β(t),g(t)=+21β(t)/1−αˉt(来自 −21gSDE2⋅s=+21β⋅ϵ/1−αˉt,因为 s=−ϵ/1−αˉt)。
把线性部分精确积分(exponential integrator),剩余部分用 Taylor 展开。
9.2 DPM-Solver-2 / 3(核心思想)
设 λt=log(αˉt/1−αˉt)(log-SNR),用 λ 当时间变量。ODE 重写:
xt=αˉsαˉtxs−αˉt∫λsλte−λϵ^θ(xτ,τ)dλ
把 ϵ^θ 在 λ 上做 k 阶 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 下不稳(ϵθ 经 CFG amplify 后超出训练域,Taylor 展开误差大)。DPM-Solver++ 改用 x0-prediction:
xt=σsσtxs+σt∫λsλteλx^θ0(xτ,τ)dλ
(用 x0-pred 而非 ϵ-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):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ϕ(c∣xt)(在 noisy data 上),用 Bayes:
∇xtlogp(xt∣c)=∇xtlogp(xt)+∇xtlogpϕ(c∣xt)
实践中给 classifier gradient 加 scale w(控制 guidance 强度):
ϵ~=ϵθ(xt,t)−w1−αˉt∇xtlogpϕ(c∣xt)
⚠️ Classifier guidance 的缺点 —— (a) 必须额外训 noisy classifier,工程负担;(b) classifier gradient 易"对抗",在远离训练分布时退化;(c) 对 text-to-image 这种连续 condition 不友好。CFG 完全替代了它。
10.2 Classifier-Free Guidance (Ho-Salimans 2022)
训练:以概率 pdrop(一般 0.1)把 c 替换为 ∅(null embedding),同一个 net 学 conditional 和 unconditional:
LCFG(θ)=E[∥ϵ−ϵθ(xt,t,c or ∅)∥2]
推理:把 w 称为 guidance scale:
ϵ~=ϵθ(xt,t,∅)+(1+w)[ϵθ(xt,t,c)−ϵθ(xt,t,∅)]
等价形式(Imagen / SD 实现常用):
ϵ~=(1+w)ϵθ(xt,t,c)−wϵθ(xt,t,∅)
⚠️ CFG w 的两种 convention — 论文 Ho-Salimans 2022 原文 ϵ~=ϵuncond+(1+w)(ϵcond−ϵuncond),即 w=0 是 unguided、w>0 增强。但 HuggingFace / SD UI 常用 w′=w+1,即 w′=1 是 unguided、w′=7.5 是常用强度。面试代码记得标明 convention。
10.3 CFG 的几何意义
CFG 等价于把采样轨迹拉向"条件梯度"方向:
∇xtlogp(xt∣c)≈∇xtlogp(xt)+w∇xtlogp(xt)p(xt∣c)
第二项是"条件性 score 差",把样本推向 conditional likelihood 高、unconditional likelihood 相对低的区域——直觉上"放大文本对齐"。
✅ CFG 是 SD/SDXL/FLUX 文图对齐的核心 —— w∈[3,7.5] 是 Stable Diffusion 的实验 sweet spot;w>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。
- 训一个 VAE E,D:z=E(x),x^=D(z),z 比 x 小 ~8×(如 5122×3→642×4)
- 在 z 上训 diffusion model(参数量、显存、计算全部降一个数量级)
- 生成时:从 zT 采样到 z0,再用 D(z0) decode 回 pixel
Stable Diffusion (SD) = LDM + CLIP text encoder + UNet on 64×64×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×2)成 token sequence
- 标准 Transformer block(self-attn + MLP)
- conditioning 通过 adaptive LayerNorm (adaLN) 注入:LN(x)⋅γ(c,t)+β(c,t),γ,β 来自 c,t 的 MLP
DiT 实验:scale law 比 UNet 好,FID 随参数量稳定下降。SD3 / FLUX / Sora 都基于 DiT 系。
11.4 SD3 (Esser 2024 ICML) —— diffusion 换成 Rectified Flow
SD3 的两个关键改动:
- Rectified Flow 替代 DDPM:训练目标变成 ∥vθ−(x1−x0)∥2(FM 框架)
- MM-DiT:多模态 DiT,text token 和 image token 在同一 Transformer 里互相 attend(不是 cross-attn)
为什么换 RF?Esser 2024 ablation:linear path 的 trajectory 比 cosine path 更直 → 少步采样更好;logit-normal t 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 一步 ≈ teacher 两步,蒸馏 log2N 轮把 N 步压到 1 步。关键:每次只压一半,分布漂移可控。
12.2 Consistency Models (Song 2023 ICML)
思路:直接学一个网络 fθ(xt,t),使得对所有 t 都满足:
fθ(xt,t)≈x0
即网络是 probability flow ODE 的 consistency function——任意 xt 映到对应的 x0。一步采样:x0=fθ(xT,T)。
训练目标(Consistency Distillation, CD):
LCD(θ)=E[d(fθ(xtn+1,tn+1),fθ−(x^tn,tn))]
其中:
- θ− 是 EMA target
- x^tn 由 teacher ODE solver 从 xtn+1 走一步得到(xtn=ODE-step(xtn+1))
- d 是 metric(L2 / LPIPS)
Boundary condition:要求 fθ(xσmin,σmin)=xσmin(在最低噪声处自洽)——用 EDM-style preconditioning 强制:
fθ(x,σ)=cskip(σ)x+cout(σ)Fθ(x,σ)
cskip,cout 设计让 σ=σmin 时 fθ≡x。
⚠️ CT (Consistency Training) vs CD (Consistency Distillation) — CT 完全 from scratch(不用 teacher,直接对 x0+σnϵ 与 x0+σn+1ϵ 做 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)
- Ladv:ADD(SDXL-Turbo) 用 pretrained vision model(DINOv2)当判别器;LADD(SD3-Turbo) 改用 teacher diffusion-transformer 自身的 latent 特征当判别器(不再用 DINOv2,判别发生在 latent 空间)
- Ldistill: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 学 v 和 score-based 学 s 是 同信息的两种参数化:
vθ(t,x)=f(x,t)−21g2(t)sθ(t,x)
具体到 VP(DDPM)path,写成 αt=αˉt,σt=1−αˉt,则 conditional vector field(Salimans-Ho 2022 v-prediction 同形式):
vθVP(t,xt)=αt′x0+σt′ϵ
代入 x0=(xt−σtϵ)/αt,整理得到 vθ 同时是 xt 与 ϵ(或 score)的线性组合——具体表达式与 αt,σt 的时间导数有关。
实际上:对应任意 α(t),σ(t) 的 Gaussian path,三个量 {ϵθ,sθ,vθ} 完全等价。所以训 DDPM、训 score-based、训 FM 在 VP/VE path 上是同一件事。
13.2 为什么 SD3 / FLUX 改用 Rectified Flow?
Rectified Flow 的 path:xt=(1−t)x0+tx1(噪声→数据线性插值),vt=x1−x0。
| 优势 | RF (linear) | VP/VE (curved) |
|---|
| ODE trajectory | 直线 | 曲线(需更高阶 solver) |
| Target vt | 不依赖 t | 依赖 t(VP cosine 路径) |
| 少步数采样 | Euler 4-8 步可用 | Euler 需 30+ 步 |
| Reflow 可压到 1-2 步 | ✓(InstaFlow / SD3-Turbo) | ✗ |
| 训练稳定性 | logit-normal t + RF 稳 | 需精心调 noise schedule |
💡 一句话 SD3 ablation 结论 — "在同样的 DiT backbone 下,RF + logit-normal t 比 VP + uniform t 在 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(xt∣x0)=N(αˉtx0,(1−αˉt)I),αˉt=∏s=1t(1−βs)
-
Reverse 参数化:pθ(xt−1∣xt)=N(μθ(xt,t),Σθ)
-
μθ=αt1(xt−1−αˉtβtϵθ(xt,t))(ϵ-prediction)
写错符号(如 αt 与 αˉt 混淆);忘 αˉ 是累积乘积。
Q2.DDPM 的 ELBO 怎么化简成 $L_\text{simple}$?
-
ELBO 拆成 LT+∑Lt−1+L0,LT 是常数(prior 匹配)
-
Lt−1=KL(q(xt−1∣xt,x0)∥pθ),两者都是 Gaussian,KL 闭式
-
把 x0=(xt−1−αˉtϵ)/αˉt 代入 μ~ 和 μθ,得 Lt−1=const⋅E∥ϵ−ϵθ∥2
-
Ho 2020 扔掉系数得 Lsimple=E∥ϵ−ϵθ∥2
只说"L_simple 是预测 noise" 不会推;或不知道扔系数等价 SNR-weighting。
Q3.为什么 $L_\text{simple}$ 扔掉系数还 work?
-
ELBO 的系数 βt2/[2σt2αt(1−αˉt)] 在小 t(高 SNR)大、在大 t(低 SNR)小
-
扔系数等价对低 SNR(大 t)权重相对提升——这些是"决定语义结构"的步骤
-
经验:unweighted FID 显著优于 ELBO weighted
-
代价:不再是 logp 的下界(FID ≠ likelihood)
不知道扔系数的代价是 likelihood vs sample quality 的 trade-off。
Q4.linear vs cosine schedule?
-
Linear: βt∈[10−4,0.02] 线性插值,DDPM 原文
-
问题:末端 SNR 不够低 (αˉT≈4×10−5);中间区域加噪太快
-
Cosine: αˉt=cos2(π(t/T+s)/(2(1+s))), s=0.008,末端 SNR ≈ 0
-
经验:cosine 在 ImageNet 64 FID 提升约 20%(Nichol-Dhariwal 2021)
只说"cosine 更好"不会写公式;忘记 s=0.008 offset 是为了 β1 不接近 0。
Q5.$\epsilon$-pred / $x_0$-pred / $v$-pred / score 怎么互转?
-
已知 xt=αˉtx0+1−αˉtϵ,所有量线性可逆
-
x^0=(xt−1−αˉtϵθ)/αˉt
-
v=αˉtϵ−1−αˉtx0 (Salimans-Ho 2022)
-
s=−ϵ/1−αˉt (由 Tweedie 或 ∇xtlogq(xt∣x0))
不知道四种 prediction 是同一信息的不同参数化;混淆 v 和 velocity。
Q6.DDIM vs DDPM 区别?
-
DDPM ancestral 是 stochastic Markov chain,每步加噪 σtz,必须走全 T 步
-
DDIM 用 non-Markovian forward,与 DDPM 共享同一 q(xt∣x0)——可直接用 DDPM 训练权重
-
η=0 deterministic、可 interpolation;η=1 + 走完整 T 步退化为 DDPM ancestral(skip 步时只是方差匹配)
-
DDIM 可 skip steps:50 步 ≈ DDPM 1000 步质量
只说"DDIM 是 DDPM 的少步版",不知道 marginal 等价;或不知道 η 控制随机性。
Q7.CFG (Classifier-Free Guidance) 怎么训怎么用?
只写公式不知 w/s convention;不知道 drop c 的训练 trick;说 CFG 需要单独训 classifier(那是 classifier guidance)。
Q8.什么是 Tweedie 公式?为什么重要?
-
E[x0∣xt]=xt+σt2∇xtlogpt(xt)(VE 视角;VP 类似有 α 因子)
-
推导:对 pt(xt)=∫p0(x0)N(xt;x0,σt2I)dx0 求 ∇xt log
-
意义:denoiser 最优输出 = 输入 + score 缩放——所有参数化(ϵ,x0,v,s)之间转换的"罗塞塔石碑"
只背公式不会推;不知道它把 score 和 denoiser 连起来。
Q9.VP-SDE vs VE-SDE?
-
VP (variance preserving): dx=−21β(t)xdt+β(t)dW,对应 DDPM;Var[xt]≤1
-
VE (variance exploding): dx=dσ2/dtdW,对应 SMLD/EDM;Var[xt] 增到 σmax2
-
VP 的 xT≈N(0,I);VE 的 xT≈N(x0,σmax2I),prior 是 N(0,σmax2I)
-
EDM 选 VE 因为 preconditioning 推导更干净;DDPM 选 VP 因为 prior N(0,I) 自然
只说"variance preserving / exploding" 不会写 SDE;不知 EDM 是 VE。
Q10.Probability flow ODE 是什么?
-
对任意 forward SDE dx=fdt+gdW,存在 deterministic ODE dx/dt=f−21g2∇logpt,共享所有时刻边缘 pt
-
注意 reverse SDE 的 drift 是 f−g2∇logpt(整个 score correction),PF-ODE 只用 21g2;不是简单地"reverse SDE 去掉随机项"
-
实际意义:可用 ODE solver(DDIM, Heun, RK4, DPM-Solver)少步数采样
-
是 score-based 与 Flow Matching 之间的桥:vt=f−21g2s
只知道公式不知道 PF-ODE 与 reverse SDE 的 drift 系数差一半;不知道它让 deterministic 采样成为可能。
L2 进阶题(research-oriented · 需熟悉 diffusion 细节)
Q11.EDM preconditioning 的 unit-variance argument 是什么?
-
让网络 Fθ 输入 cinx 方差为 1:cin=1/σdata2+σ2
-
让 effective target F∗=(D∗−cskipx)/cout 方差为 1:cskip=σdata2/(σ2+σdata2), cout=σσdata/σ2+σdata2
-
直觉:σ→0 时 cskip→1(identity),σ→∞ 时 cout→σdata(all from net)
-
作用:所有 σ 上 loss 数值范围一致,训练更稳
只背公式不知道为什么;不知道 σdata 是数据 std(约 0.5 for normalized images)。
Q12.Improved DDPM 学 $\Sigma_\theta$ 的好处?
-
DDPM 固定 Σθ=βtI 或 β~tI
-
Nichol-Dhariwal 2021 学 Σθ 在 [βt,β~t] 之间插值:Σθ=exp(vlogβt+(1−v)logβ~t)
-
好处:少步采样质量大幅提升(50 步达到 1000 步 fixed-Σ 水平)
-
Hybrid loss Lhybrid=Lsimple+0.001⋅Lvlb(Lvlb 提供 Σθ 学习信号)
-
λ=0.001 防 Lvlb 主导
不知道 hybrid loss;以为 Σθ 学习对训练 likelihood 影响最大(实际是少步采样涨点)。
Q13.DPM-Solver vs DDIM 的核心区别?
-
DDIM 是一阶 Euler,每步 1 NFE
-
DPM-Solver 利用 diffusion ODE 的半线性结构 dx/dt=f(t)x+g(t)ϵθ,对线性部分精确积分(exponential integrator)
-
把非线性部分(ϵθ)在 log-SNR λ 上做 k-阶 Taylor 展开
-
DPM-Solver-2 每步 2 NFE,二阶;DPM-Solver-3 每步 3 NFE,三阶
-
10-15 NFE 达到 DDIM 50 NFE 质量
-
DPM-Solver++ 改用 x0-pred,CFG 友好
不知道 exponential integrator;以为 DPM-Solver 是某种近似(实际是数学上更精的展开)。
Q14.Consistency Models 训练目标?怎么做到 1-step?
-
目标:fθ(xt,t)≈x0 对所有 t
-
Consistency loss:d(fθ(xtn+1,tn+1),fθ−(x^tn,tn)),x^tn 由 teacher ODE 一步得到
-
θ− 是 EMA,类似 BYOL;用 metric d = L2 + LPIPS
-
Boundary:fθ(x,σmin)≡x,用 EDM-style cskip,cout 强制
-
1-step 采样:x0=fθ(xT,T)
-
2-step 进阶:先 x0=fθ(xT,T),再加噪到中间 t、再 fθ
只说"学映射 xt→x0"不知道 consistency 约束怎么定义;不知道 EMA target / teacher ODE / boundary。
Q15.SD3 为什么从 DDPM 换成 Rectified Flow?
-
RF path xt=(1−t)x0+tx1 是直线 → ODE trajectory 直 → 少步采样误差小
-
vt=x1−x0 target 不依赖 t(给定 (x0,x1)),数值稳定
-
配合 logit-normal t sampling(集中在 t=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,t 经 MLP 输出 γ,β,α,out=α⋅block(LN(x)⋅γ+β)+x;初始化 α=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ˉ;每步注入新噪声,能修正早期错误
-
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: t∼U{1,…,T},离散均匀
-
EDM: lnσ∼N(Pmean,Pstd2),Pmean=−1.2,Pstd=1.2,集中在 σ≈0.3
-
SD3 / RF: t=sigmoid(τ),τ∼N(0,1),集中在 t=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:T∣x0)] —— Jensen 不等式给出变分下界
-
Step 2(LT 被当作常数忽略):ELBO 拆 L=LT+∑t=2TLt−1+L0。LT=KL(q(xT∣x0)∥p(xT)) —— 实际不严格为 0,但 αˉT≈0 时近似常数
-
Step 3(L0 被忽略 / 合并):L0=−E[logpθ(x0∣x1)] —— small contribution;常用 discretized Gaussian decoder 显式建模,训练时常被合并到 L1
-
Step 4(KL 闭式,Σθ 固定时常数 C 被忽略):Lt−1=Eq[KL(q(xt−1∣xt,x0)∥pθ(xt−1∣xt))]。两者都是 Gaussian → KL 闭式。如果 Σθ=σt2I 固定:
Lt−1=E[2σt21∥μ~t(xt,x0)−μθ(xt,t)∥2]+C
常数 C 来自 Σ 项的 log-determinant,Σ 固定时与 θ 无关,求梯度时消失。
- Step 5(精确重写为 ϵ-pred 形式):把 x0=(xt−1−αˉtϵ)/αˉt 代入 μ~t 和 μθ 都用 ϵ-pred parameterization:
Lt−1=E[2σt2αt(1−αˉt)βt2∥ϵ−ϵθ(xt,t)∥2]
精确,只要 μθ 用 Ho 2020 的 ϵ-pred 形式。
-
Step 6(扔掉 t 依赖系数):Lsimple 把系数 2σt2αt(1−αˉt)βt2 统一取 1。等价对不同 t 重新加权——在小 t(高 SNR)原系数大 → simple 相对降低权重;在大 t(低 SNR)原系数小 → simple 相对提升权重。
-
Step 7(t 改均匀采样):离散 t 改成 t∼U{1,…,T},均匀采样所有时间步,不是按 ELBO 各项的权重。
最终:
Lsimple=Et∼U{1,…,T},x0,ϵ[∥ϵ−ϵθ(αˉtx0+1−αˉtϵ,t)∥2]
代价:
- 不再是 logp 的下界(FID 涨但 likelihood 评估不再直接对应)
- LT 和 L0 被默认忽略
- Σθ 信息被丢(Improved DDPM 用 Lvlb 补回)
不知道哪些项被丢;以为 Lsimple 直接从 KL 推出来;忽略 LT,L0 的角色。
Q22.证明 DDIM ($\eta=0$) 与 DDPM 共享同一 marginal $q(x_t|x_0)$。
Statement:DDIM 定义 non-Markov forward qσ(x1:T∣x0),使得 qσ(xt∣x0)=N(αˉtx0,(1−αˉt)I) —— 与 DDPM 完全一致。
证明(归纳):
-
边界 qσ(xT∣x0)=N(αˉTx0,(1−αˉT)I) —— 由 DDIM 定义直接成立
-
假设 qσ(xt∣x0)=N(αˉtx0,(1−αˉt)I)。DDIM 定义:
qσ(xt−1∣xt,x0)=N(αˉt−1x0+1−αˉt−1−σt2⋅1−αˉtxt−αˉtx0,σt2I)
-
求 qσ(xt−1∣x0)=∫qσ(xt−1∣xt,x0)qσ(xt∣x0)dxt(两个 Gaussian 的边缘化)
-
用 Gaussian 边缘化定理:若 xt∣x0∼N(μt,Σt) 且 xt−1∣xt,x0∼N(Axt+b,Σt−1∣t),则:
xt−1∣x0∼N(Aμt+b,AΣtA⊤+Σt−1∣t)
-
这里 A=1−αˉt−1−σt2/1−αˉt,b=αˉt−1x0−Aαˉtx0。代入:
-
均值 = αˉt−1x0+Aαˉtx0−Aαˉtx0=αˉt−1x0
-
方差 = A2(1−αˉt)+σt2=(1−αˉt−1−σt2)+σt2=1−αˉt−1
-
所以 qσ(xt−1∣x0)=N(αˉt−1x0,(1−αˉt−1)I) —— 与 DDPM 完全一致 □
意义:DDIM 可以直接用 DDPM 训出来的 ϵθ,因为训练只看 marginal q(xt∣x0),而 marginal 一致;但采样路径不同(deterministic vs stochastic)。
不会写 Gaussian 边缘化定理;不知道证明的关键是 A2(1−αˉt)+σt2=1−αˉt−1。
Q23.推导 EDM preconditioning 中的 $c_\text{skip}$ 与 $c_\text{out}$。
Setup:VE 视角 x=x0+σϵ,ϵ∼N(0,I),Var[x0]=σdata2。Denoiser 参数化:
Dθ(x;σ)=cskip(σ)x+cout(σ)Fθ(cinx,cnoise)
Effective target for Fθ:
F∗(x0,σ,ϵ)=cout(σ)1[x0−cskip(σ)x]=cout1[(1−cskip)x0−cskipσϵ]
目标:找 cskip,cout 让 Var[F∗](对 x0,ϵ 取期望)= 1。
Var[F∗]=cout21[(1−cskip)2σdata2+cskip2σ2]=1
但单纯归一化有多解。第二准则(Karras 2022):让 Fθ 学的"残差"最小(让 cout 最小,因为 cout 越大 F 越被 amplify、误差也被 amplify)。等价求:
mincskipcout2(cskip)=(1−cskip)2σdata2+cskip2σ2
对 cskip 求导 = 0:
−2(1−cskip)σdata2+2cskipσ2=0⇒cskip=σdata2+σ2σdata2
代回 Var[F∗]=1 约束:
cout2=(1−cskip)2σdata2+cskip2σ2=(σ2+σdata2)2σ4σdata2+(σ2+σdata2)2σdata4σ2=σ2+σdata2σ2σdata2
cout(σ)=σ2+σdata2σ⋅σdata
输入归一化:cin(σ)=1/σdata2+σ2 让 Var[cinx]=1。
结论:四个 c 函数完全由 σdata 决定,无可调参数(实际工程上 σdata 由数据计算,对 normalized images 约 0.5)。
只背公式不会推导;不知道 cskip 是最小化 cout 推出来的;以为 c 函数有 free parameter。
Q24.Consistency Distillation 的训练流程?为什么需要 EMA target $\theta^-$?
流程:
-
取 pretrained teacher diffusion ϵϕ + 其 PF-ODE solver(如 EDM Heun)
-
取 noise schedule σ1>σ2>⋯>σN=σmin(典型 N=18)
-
训练 student fθ(xσ,σ)→x0,初始化 θ=ϕ (warm start)
-
每个 batch:
- 采样 x0,σn(uniformly n∈{1,…,N−1})
- 加噪:xσn+1=x0+σn+1ϵ
- Teacher ODE solve 一步:从 xσn+1 用 teacher ϵϕ 做 Heun step 到 x^σn
- Loss: d(fθ(xσn+1,σn+1),fθ−(x^σn,σn))
-
更新 θ,EMA 更新 θ−←μθ−+(1−μ)θ
为什么需要 EMA target?
-
直接用 θ=θ− 会有 trivial solution:fθ≡const 也满足 consistency
-
EMA θ− 比 θ 滞后,提供"stable" target,避免 student 跟着自己变
-
类似 BYOL / MoCo 的 self-supervised setup
-
μ=0.999∼0.99995(与训练步数相关)
最近改进 (iCT, Song-Dhariwal 2024):移除 EMA teacher(直接用同一 θ 算 target,不再保留 θ−),改用 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。逐步拆:
-
RF 让 trajectory 直 —— xt=(1−t)x0+tx1,ODE 解的"理想曲线"就是直线(线性插值),Euler 一阶在长 step 下误差小(与 cosine path 在 mid-t 处曲率大形成对比)
-
Reflow 让 trajectory 更直 —— 第一次训完拿 ODE 跑出 coupled (x0,x1),再训一次,trajectory 收敛到更接近直线。Liu 2022 证明 reflow 单调降低 transport cost
-
CFG-distillation —— 把 CFG 的 2× forward(cond + uncond)蒸馏成单 forward(FLUX 做了这步);NFE 折半
-
Adversarial distillation (ADD) —— SD3-Turbo / SDXL-Turbo 末段用 DINOv2 discriminator + distill loss,4-step 接近 30-step 质量
对比 DDPM 路线:DDPM trajectory 在 mid-t 曲率大(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(xt∣x0) + 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 η)
@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)