1240 lines
103 KiB
HTML
1240 lines
103 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Diffusion Foundations 面试 Cheat Sheet</title>
|
||
|
||
<meta name="generator" content="ARIS render-html (academic, v1)">
|
||
<meta name="aris:source-path" content="docs/tutorials/diffusion_foundations_tutorial.md">
|
||
<meta name="aris:source-sha256" content="95c1efc6f929678361bb3b54ef684da7def546002107412458fd14673ebbef67">
|
||
<meta name="aris:generated-at" content="2026-05-19 05:40 UTC">
|
||
|
||
<!-- MathJax 3 -->
|
||
<script>
|
||
window.MathJax = {
|
||
tex: { inlineMath: [['$', '$'], ['\\(', '\\)']], displayMath: [['$$', '$$'], ['\\[', '\\]']], processEscapes: true },
|
||
options: { skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] }
|
||
};
|
||
</script>
|
||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" async></script>
|
||
|
||
<!-- highlight.js -->
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/atom-one-light.min.css">
|
||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
|
||
<script>document.addEventListener('DOMContentLoaded', () => hljs.highlightAll());</script>
|
||
|
||
|
||
<style>
|
||
:root {
|
||
--bg: #fdfcf7;
|
||
--bg-soft: #f4f1ea;
|
||
--bg-code: #f8f5ec;
|
||
--ink: #1a1a1a;
|
||
--ink-soft: #4a4a4a;
|
||
--ink-muted: #6b6b6b;
|
||
--primary: #1a4a8c;
|
||
--primary-soft: #2d6cb8;
|
||
--accent: #b8390e;
|
||
--warn: #b45309;
|
||
--warn-bg: #fef3c7;
|
||
--info-bg: #dbeafe;
|
||
--good-bg: #d1fae5;
|
||
--good: #065f46;
|
||
--bad-bg: #fee2e2;
|
||
--bad: #991b1b;
|
||
--border: #d6d0c0;
|
||
--border-soft: #e8e3d5;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
html { scroll-behavior: smooth; }
|
||
|
||
body {
|
||
font-family: "Source Serif Pro", "Source Serif 4", "Crimson Pro", "Georgia", "Songti SC", "STSong", serif;
|
||
line-height: 1.65;
|
||
color: var(--ink);
|
||
background: var(--bg);
|
||
margin: 0;
|
||
padding: 0;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.layout {
|
||
max-width: 1280px;
|
||
margin: 0 auto;
|
||
display: grid;
|
||
grid-template-columns: 260px 1fr;
|
||
gap: 48px;
|
||
padding: 40px 32px;
|
||
}
|
||
|
||
nav.toc {
|
||
position: sticky;
|
||
top: 24px;
|
||
align-self: start;
|
||
font-size: 13px;
|
||
max-height: calc(100vh - 48px);
|
||
overflow-y: auto;
|
||
border-right: 1px solid var(--border-soft);
|
||
padding-right: 16px;
|
||
}
|
||
nav.toc h3 {
|
||
margin: 0 0 12px;
|
||
font-size: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--ink-muted);
|
||
font-weight: 600;
|
||
}
|
||
nav.toc ol { list-style: none; padding: 0; margin: 0; counter-reset: toc; }
|
||
nav.toc ol li { margin: 5px 0; counter-increment: toc; }
|
||
nav.toc ol li::before { content: counter(toc) ". "; color: var(--ink-muted); margin-right: 4px; }
|
||
nav.toc a {
|
||
color: var(--ink-soft);
|
||
text-decoration: none;
|
||
border-bottom: 1px dotted transparent;
|
||
}
|
||
nav.toc a:hover { color: var(--primary); border-bottom-color: var(--primary); }
|
||
nav.toc ul { list-style: none; padding-left: 14px; margin: 3px 0; font-size: 12px; }
|
||
nav.toc ul li::before { content: "→ "; color: var(--border); }
|
||
|
||
main { min-width: 0; }
|
||
|
||
header.hero {
|
||
border-bottom: 3px double var(--primary);
|
||
padding-bottom: 24px;
|
||
margin-bottom: 32px;
|
||
}
|
||
header.hero .eyebrow {
|
||
color: var(--accent);
|
||
font-size: 13px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.12em;
|
||
font-weight: 600;
|
||
margin-bottom: 8px;
|
||
}
|
||
header.hero h1 {
|
||
font-size: 32px;
|
||
line-height: 1.2;
|
||
margin: 0 0 12px;
|
||
color: var(--ink);
|
||
font-weight: 700;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
header.hero .subtitle {
|
||
font-size: 16px;
|
||
color: var(--ink-soft);
|
||
margin: 0 0 8px;
|
||
font-style: italic;
|
||
}
|
||
header.hero .byline {
|
||
font-size: 14px;
|
||
color: var(--ink-soft);
|
||
margin: 0 0 20px;
|
||
}
|
||
header.hero .byline strong {
|
||
color: var(--ink);
|
||
font-weight: 600;
|
||
}
|
||
header.hero .meta {
|
||
display: flex;
|
||
gap: 20px;
|
||
flex-wrap: wrap;
|
||
font-size: 12px;
|
||
color: var(--ink-muted);
|
||
border-top: 1px solid var(--border-soft);
|
||
padding-top: 14px;
|
||
}
|
||
header.hero .meta span strong { color: var(--ink-soft); }
|
||
header.hero .meta code {
|
||
font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
|
||
font-size: 11px;
|
||
background: var(--bg-soft);
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
border: 1px solid var(--border-soft);
|
||
}
|
||
|
||
h2 {
|
||
font-size: 24px;
|
||
margin: 44px 0 14px;
|
||
padding-bottom: 8px;
|
||
border-bottom: 1px solid var(--border);
|
||
color: var(--ink);
|
||
font-weight: 700;
|
||
}
|
||
h2 .num { color: var(--primary); font-weight: 600; margin-right: 8px; }
|
||
h3 { font-size: 19px; margin: 28px 0 10px; color: var(--primary); font-weight: 600; }
|
||
h4 { font-size: 16px; margin: 20px 0 8px; color: var(--ink); font-weight: 600; }
|
||
|
||
p { margin: 10px 0; }
|
||
ul, ol { padding-left: 22px; margin: 10px 0; }
|
||
ul li, ol li { margin: 4px 0; }
|
||
ul li::marker { color: var(--primary); }
|
||
|
||
strong { color: var(--accent); font-weight: 600; }
|
||
em { color: var(--ink-soft); }
|
||
|
||
a { color: var(--primary); }
|
||
a:hover { color: var(--accent); }
|
||
|
||
code:not(.hljs) {
|
||
font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
|
||
font-size: 0.86em;
|
||
background: var(--bg-code);
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
border: 1px solid var(--border-soft);
|
||
color: var(--accent);
|
||
}
|
||
|
||
pre {
|
||
background: #fafaf6;
|
||
border: 1px solid var(--border);
|
||
border-left: 4px solid var(--primary);
|
||
padding: 0;
|
||
overflow-x: auto;
|
||
border-radius: 4px;
|
||
margin: 14px 0;
|
||
}
|
||
pre code, pre code.hljs {
|
||
background: transparent !important;
|
||
display: block;
|
||
padding: 14px 18px !important;
|
||
font-size: 13px;
|
||
line-height: 1.55;
|
||
font-family: "JetBrains Mono", "SF Mono", "Menlo", monospace;
|
||
color: var(--ink);
|
||
}
|
||
pre.diagram {
|
||
background: #f9f6ed;
|
||
border-left: 4px solid var(--accent);
|
||
font-size: 12.5px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.callout {
|
||
margin: 16px 0;
|
||
padding: 12px 16px;
|
||
border-radius: 4px;
|
||
border-left: 4px solid;
|
||
font-size: 15px;
|
||
}
|
||
.callout-title {
|
||
font-weight: 600;
|
||
margin-bottom: 6px;
|
||
font-size: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
}
|
||
.callout-info { background: var(--info-bg); border-left-color: var(--primary); }
|
||
.callout-info .callout-title { color: var(--primary); }
|
||
.callout-warn { background: var(--warn-bg); border-left-color: var(--warn); }
|
||
.callout-warn .callout-title { color: var(--warn); }
|
||
.callout-good { background: var(--good-bg); border-left-color: var(--good); }
|
||
.callout-good .callout-title { color: var(--good); }
|
||
.callout-bad { background: var(--bad-bg); border-left-color: var(--bad); }
|
||
.callout-bad .callout-title { color: var(--bad); }
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 16px 0;
|
||
font-size: 14px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
thead { background: var(--primary); color: white; }
|
||
th, td {
|
||
text-align: left;
|
||
padding: 9px 12px;
|
||
border-bottom: 1px solid var(--border-soft);
|
||
vertical-align: top;
|
||
}
|
||
th { font-weight: 600; font-size: 13px; letter-spacing: 0.02em; }
|
||
tr:last-child td { border-bottom: none; }
|
||
tbody tr:nth-child(even) { background: var(--bg-soft); }
|
||
|
||
details.qa, details {
|
||
background: white;
|
||
border: 1px solid var(--border-soft);
|
||
border-radius: 6px;
|
||
margin: 10px 0;
|
||
padding: 0;
|
||
}
|
||
details summary {
|
||
cursor: pointer;
|
||
padding: 10px 14px;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
color: var(--primary);
|
||
list-style: none;
|
||
user-select: none;
|
||
}
|
||
details summary::-webkit-details-marker { display: none; }
|
||
details summary::before {
|
||
content: "▸ ";
|
||
margin-right: 4px;
|
||
display: inline-block;
|
||
transition: transform 0.15s;
|
||
}
|
||
details[open] summary::before { transform: rotate(90deg); }
|
||
details[open] summary { border-bottom: 1px solid var(--border-soft); }
|
||
details > :not(summary) { padding: 10px 14px; }
|
||
details p:first-of-type { margin-top: 8px; }
|
||
|
||
mjx-container[display="true"] { margin: 12px 0 !important; }
|
||
|
||
footer.aris-footer {
|
||
margin-top: 60px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid var(--border);
|
||
font-size: 12px;
|
||
color: var(--ink-muted);
|
||
}
|
||
footer.aris-footer a { color: var(--ink-muted); border-bottom: 1px dotted var(--border); }
|
||
|
||
@media (max-width: 900px) {
|
||
.layout { grid-template-columns: 1fr; gap: 20px; padding: 20px 16px; }
|
||
nav.toc {
|
||
position: static;
|
||
max-height: none;
|
||
border-right: none;
|
||
border-bottom: 1px solid var(--border-soft);
|
||
padding-right: 0;
|
||
padding-bottom: 14px;
|
||
}
|
||
header.hero h1 { font-size: 24px; }
|
||
h2 { font-size: 20px; }
|
||
}
|
||
@media print {
|
||
nav.toc { display: none; }
|
||
.layout { grid-template-columns: 1fr; padding: 0; }
|
||
body { background: white; }
|
||
header.hero { border-bottom-color: var(--ink); }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="layout">
|
||
|
||
<nav class="toc">
|
||
<h3>Contents</h3>
|
||
<ol>
|
||
<li><a href="#0-tldr">§0 TL;DR</a>
|
||
</li>
|
||
<li><a href="#1-直觉--三种视角">§1 直觉 & 三种视角</a>
|
||
<ul>
|
||
<li><a href="#11-一句话直觉">1.1 一句话直觉</a></li>
|
||
<li><a href="#12-三种视角对照">1.2 三种视角对照</a></li>
|
||
<li><a href="#13-convention全文统一">1.3 Convention(全文统一)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#2-ddpm-forward-process">§2 DDPM Forward Process</a>
|
||
<ul>
|
||
<li><a href="#21-单步与闭式表达">2.1 单步与闭式表达</a></li>
|
||
<li><a href="#22-闭式推导必考会反复出现">2.2 闭式推导(必考,会反复出现)</a></li>
|
||
<li><a href="#23-边界与极限">2.3 边界与极限</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#3-ddpm-reverse-process--训练">§3 DDPM Reverse Process & 训练</a>
|
||
<ul>
|
||
<li><a href="#31-reverse-是-gaussian-的前提">3.1 Reverse 是 Gaussian 的前提</a></li>
|
||
<li><a href="#32-elbo-推导">3.2 ELBO 推导</a></li>
|
||
<li><a href="#33-化简到-ltextsimple必考推导">3.3 化简到 $L_\text{simple}$(必考推导)</a></li>
|
||
<li><a href="#34-预测目标的等价转换必背">3.4 预测目标的等价转换(必背)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#4-schedulelinear--cosine--edm">§4 Schedule:linear / cosine / EDM</a>
|
||
<ul>
|
||
<li><a href="#41-linear-ho-2020">4.1 Linear (Ho 2020)</a></li>
|
||
<li><a href="#42-cosine-nichol-dhariwal-2021">4.2 Cosine (Nichol-Dhariwal 2021)</a></li>
|
||
<li><a href="#43-edm--schedule-karras-2022">4.3 EDM σ-schedule (Karras 2022)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#5-score-based-视角">§5 Score-based 视角</a>
|
||
<ul>
|
||
<li><a href="#51-score-与-score-matching-hyvrinen-2005">5.1 Score 与 score matching (Hyvärinen 2005)</a></li>
|
||
<li><a href="#52-denoising-score-matching-vincent-2011">5.2 Denoising Score Matching (Vincent 2011)</a></li>
|
||
<li><a href="#53-tweedie-公式必考推导">5.3 Tweedie 公式(必考推导)</a></li>
|
||
<li><a href="#54-ncsn--smld-song-ermon-2019">5.4 NCSN / SMLD (Song-Ermon 2019)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#6-score-sde统一框架--probability-flow-ode">§6 Score SDE:统一框架 + Probability Flow ODE</a>
|
||
<ul>
|
||
<li><a href="#61-forward-sde">6.1 Forward SDE</a></li>
|
||
<li><a href="#62-reverse-sde-anderson-1982">6.2 Reverse SDE (Anderson 1982)</a></li>
|
||
<li><a href="#63-probability-flow-ode与-fm-的桥梁">6.3 Probability Flow ODE(与 FM 的桥梁)</a></li>
|
||
<li><a href="#64-ode-视角的优势">6.4 ODE 视角的优势</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#7-ddimnon-markovian-forward--deterministic-sampler">§7 DDIM:Non-Markovian Forward → Deterministic Sampler</a>
|
||
<ul>
|
||
<li><a href="#71-motivation">7.1 Motivation</a></li>
|
||
<li><a href="#72-non-markovian-forward">7.2 Non-Markovian Forward</a></li>
|
||
<li><a href="#73-ddim-采样公式">7.3 DDIM 采样公式</a></li>
|
||
<li><a href="#74-skip-steps少步数采样">7.4 Skip steps(少步数采样)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#8-edmkarras-2022-设计空间">§8 EDM:Karras 2022 设计空间</a>
|
||
<ul>
|
||
<li><a href="#81-motivation">8.1 Motivation</a></li>
|
||
<li><a href="#82-preconditioning必考推导">8.2 Preconditioning(必考推导)</a></li>
|
||
<li><a href="#83-训练-loss">8.3 训练 loss</a></li>
|
||
<li><a href="#84-heun-2nd-order-sampler">8.4 Heun 2nd-order sampler</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#9-高阶采样器dpm-solver--dpm-solver">§9 高阶采样器:DPM-Solver / DPM-Solver++</a>
|
||
<ul>
|
||
<li><a href="#91-motivation">9.1 Motivation</a></li>
|
||
<li><a href="#92-dpm-solver-2--3核心思想">9.2 DPM-Solver-2 / 3(核心思想)</a></li>
|
||
<li><a href="#93-dpm-solvercfg-友好版lu-et-al-2023">9.3 DPM-Solver++(CFG 友好版,Lu et al. 2023)</a></li>
|
||
<li><a href="#94-采样器对比">9.4 采样器对比</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#10-conditioningclassifier-guidance--cfg">§10 Conditioning:Classifier Guidance & CFG</a>
|
||
<ul>
|
||
<li><a href="#101-classifier-guidance-dhariwal-nichol-2021">10.1 Classifier Guidance (Dhariwal-Nichol 2021)</a></li>
|
||
<li><a href="#102-classifier-free-guidance-ho-salimans-2022">10.2 Classifier-Free Guidance (Ho-Salimans 2022)</a></li>
|
||
<li><a href="#103-cfg-的几何意义">10.3 CFG 的几何意义</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#11-production从-ldm-到-flux">§11 Production:从 LDM 到 FLUX</a>
|
||
<ul>
|
||
<li><a href="#111-latent-diffusion-ldm-rombach-2022-cvpr">11.1 Latent Diffusion (LDM, Rombach 2022 CVPR)</a></li>
|
||
<li><a href="#112-sdxl-podell-et-al-2023-arxiv--iclr-2024-spotlight">11.2 SDXL (Podell et al. 2023 arXiv / ICLR 2024 spotlight)</a></li>
|
||
<li><a href="#113-dit-peebles-xie-2023-iccv">11.3 DiT (Peebles-Xie 2023 ICCV)</a></li>
|
||
<li><a href="#114-sd3-esser-2024-icml--diffusion-换成-rectified-flow">11.4 SD3 (Esser 2024 ICML) —— diffusion 换成 Rectified Flow</a></li>
|
||
<li><a href="#115-flux1-black-forest-labs-2024">11.5 FLUX.1 (Black Forest Labs 2024)</a></li>
|
||
<li><a href="#116-controlnet-zhang-2023-iccv">11.6 ControlNet (Zhang 2023 ICCV)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#12-distillation1-step--few-step-生成">§12 Distillation:1-step / Few-step 生成</a>
|
||
<ul>
|
||
<li><a href="#121-progressive-distillation-salimans-ho-2022">12.1 Progressive Distillation (Salimans-Ho 2022)</a></li>
|
||
<li><a href="#122-consistency-models-song-2023-icml">12.2 Consistency Models (Song 2023 ICML)</a></li>
|
||
<li><a href="#123-lcm--lcm-lora-luo-2023">12.3 LCM / LCM-LoRA (Luo 2023)</a></li>
|
||
<li><a href="#124-adversarial-diffusion-distillation-add--sdxl-turbo--sd3-turbo-sauer-20232024">12.4 Adversarial Diffusion Distillation (ADD) — SDXL-Turbo / SD3-Turbo (Sauer 2023/2024)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#13-与-flow-matching-的桥">§13 与 Flow Matching 的桥</a>
|
||
<ul>
|
||
<li><a href="#131-score-vs-vector-field--同信息不同参数化">13.1 Score vs Vector Field —— 同信息不同参数化</a></li>
|
||
<li><a href="#132-为什么-sd3--flux-改用-rectified-flow">13.2 为什么 SD3 / FLUX 改用 Rectified Flow?</a></li>
|
||
<li><a href="#133-ddpmddimedmrfcm-全图">13.3 DDPM/DDIM/EDM/RF/CM 全图</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#14-25-高频面试题l1-必会--l2-进阶--l3-顶级-lab">§14 25 高频面试题(L1 必会 · L2 进阶 · L3 顶级 lab)</a>
|
||
<ul>
|
||
<li><a href="#l1-必会题任何-ml-岗位-diffusion-题目都可能问">L1 必会题(任何 ML 岗位 diffusion 题目都可能问)</a></li>
|
||
<li><a href="#l2-进阶题research-oriented--需熟悉-diffusion-细节">L2 进阶题(research-oriented · 需熟悉 diffusion 细节)</a></li>
|
||
<li><a href="#l3-顶级-diffusion--视频生成方向深度推导--蒸馏--production-整合">L3 顶级 diffusion / 视频生成方向(深度推导 + 蒸馏 + Production 整合)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#a-附录核心-pytorch-代码from-scratch">§A 附录:核心 PyTorch 代码(from scratch)</a>
|
||
<ul>
|
||
<li><a href="#a1-ddpm-forward-qxt--x0--simplified-loss">A.1 DDPM forward $q(x_t | x_0)$ + simplified loss</a></li>
|
||
<li><a href="#a2-ddpm-ancestral-sampling">A.2 DDPM ancestral sampling</a></li>
|
||
<li><a href="#a3-ddim-sampling-with-eta">A.3 DDIM sampling (with $\eta$)</a></li>
|
||
<li><a href="#a4-classifier-free-guidance-训练--采样">A.4 Classifier-Free Guidance 训练 + 采样</a></li>
|
||
<li><a href="#a5-edm-preconditioning--heun-二阶-sampler">A.5 EDM preconditioning + Heun 二阶 sampler</a></li>
|
||
<li><a href="#a6-probability-flow-ode-简单-euler-求解">A.6 Probability Flow ODE 简单 Euler 求解</a></li>
|
||
<li><a href="#a7-sanity-check-输出教学版">A.7 Sanity-check 输出(教学版)</a></li>
|
||
</ul>
|
||
</li>
|
||
</ol>
|
||
</nav>
|
||
|
||
<main>
|
||
<header class="hero">
|
||
<div class="eyebrow">Interview Prep · Diffusion Generative Modeling</div>
|
||
<h1>Diffusion Foundations 面试 Cheat Sheet</h1>
|
||
<p class="subtitle">DDPM / Score / DDIM / EDM / CFG / Consistency Models · 公式推导 + From-Scratch 代码 + 25 高频题(L1 必会 · L2 进阶 · L3 顶级 lab)</p>
|
||
<p class="byline">By <strong>Ruofeng Yang (杨若峰), Shanghai Jiao Tong University</strong></p>
|
||
<div class="meta">
|
||
<span><strong>Source:</strong> <code>docs/tutorials/diffusion_foundations_tutorial.md</code></span>
|
||
<span><strong>SHA256:</strong> <code>95c1efc6f929</code></span>
|
||
<span><strong>Rendered:</strong> 2026-05-19 05:40 UTC</span>
|
||
|
||
</div>
|
||
</header>
|
||
|
||
<h2 id="0-tldr">§0 TL;DR</h2>
|
||
<div class="callout callout-info"><div class="callout-title">9 句话搞定 Diffusion 基础</div><p>一页拿下面试核心要点(详见 §1–§13 推导)。</p></div>
|
||
<ol><li><strong>DDPM (Ho 2020)</strong>:forward $q(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I)$ 闭式可采样;reverse $p_\theta(x_{t-1}|x_t) = \mathcal{N}(\mu_\theta, \Sigma_\theta)$ 学反向 Gaussian;ELBO 化简到 $L_\text{simple} = \mathbb{E}\|\epsilon - \epsilon_\theta(x_t, t)\|^2$($\epsilon$-prediction)。</li><li><strong>三种视角等价</strong>:DDPM 的 $\epsilon$、score-based 的 $s = \nabla \log p_t$、flow matching 的 $v$ 在 Gaussian path 下线性可逆 —— $s_\theta = -\epsilon_\theta / \sigma_t$,$v = \alpha'x_0 + \sigma'\epsilon$。</li><li><strong>Tweedie 公式</strong>:$\mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2 \nabla_{x_t} \log p_t(x_t)$ —— 一行式连接 denoiser 与 score。</li><li><strong>Score SDE (Song 2021)</strong>:VP-SDE / VE-SDE 统一框架;<strong>reverse-time SDE</strong> 与 <strong>probability flow ODE</strong> 共享同一族边缘分布,ODE 形式直接给出 FM 的 vector field。</li><li><strong>DDIM (Song 2020 / ICLR 2021)</strong>:non-Markovian forward 推出 deterministic sampler,<strong>marginal 与 DDPM 相同</strong>但采样路径可控($\eta=0$ 确定性;$\eta=1$ + 走完整 $T$ 步退化为 DDPM ancestral,skip 步下则只是匹配 DDPM 方差,不严格等价)。</li><li><strong>EDM (Karras 2022)</strong>:preconditioning 让网络输出方差恒为 1:$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,<strong>FID SOTA 同时 NFE 降到 18-35</strong>。</li><li><strong>CFG (Ho-Salimans 2022)</strong>:训练时以概率 $p_\text{drop}$ drop 条件 → 同一 net 学 conditional/unconditional;推理 $\tilde\epsilon = (1+w)\epsilon_\theta(x,c) - w\epsilon_\theta(x,\emptyset)$,$w \in [3, 7]$ 是 text-to-image 主力。</li><li><strong>Production</strong>:SD/SDXL 用 VAE latent + UNet;SD3 / FLUX.1 改用 <strong>Rectified Flow + MM-DiT</strong>;ControlNet 给 frozen UNet 加可训练 side branch;DiT 把 UNet 全换 Transformer。</li><li><strong>加速</strong>:DPM-Solver++ 把 NFE 压到 10-20;Consistency Models 学 $f_\theta(x_t, t) \mapsto x_0$ 做到 1-4 步;LCM / LCM-LoRA / SDXL-Turbo / SD3-Turbo (ADD) 让蒸馏在 Stable Diffusion 全家桶可用。</li></ol>
|
||
<h2 id="1-直觉--三种视角">§1 直觉 & 三种视角</h2>
|
||
<h3 id="11-一句话直觉">1.1 一句话直觉</h3>
|
||
<p><strong>Diffusion = 学会"去噪"</strong>:把数据从干净逐渐加噪到纯 Gaussian(forward),然后学会反过来从噪声一步步还原数据(reverse)。所有 diffusion 论文的差异都在三件事:</p>
|
||
<ul><li><strong>forward 怎么加噪</strong>(schedule、SDE 类型 VP/VE)</li><li><strong>网络预测什么</strong>($\epsilon$ / $x_0$ / $v$ / score / $D$)</li><li><strong>reverse 怎么采样</strong>(Markov ancestral / DDIM / DPM-Solver / EDM Heun / Consistency one-step)</li></ul>
|
||
<h3 id="12-三种视角对照">1.2 三种视角对照</h3>
|
||
<pre class="diagram"><code> 统一框架(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</code></pre>
|
||
<div class="callout callout-info"><div class="callout-title">面试一句话答</div><p>"DDPM 是离散时间下 VP-SDE 的特例;score-based 是连续时间下的等价参数化;Flow Matching 在 VP/VE path 下与 score matching 同信息,只是参数化成 $v$ 不是 $s$。Rectified Flow 跳出 SDE 框架,用线性 path 直接学 ODE 的 vector field。"</p></div>
|
||
<h3 id="13-convention全文统一">1.3 Convention(全文统一)</h3>
|
||
<table><thead><tr><th>符号</th><th>含义</th></tr></thead><tbody><tr><td>$x_0$</td><td>干净数据样本</td></tr><tr><td>$x_t$, $t \in \{1,\dots,T\}$ 或 $t \in [0,T]$</td><td>加噪后的样本</td></tr><tr><td>$\epsilon \sim \mathcal{N}(0, I)$</td><td>标准 Gaussian 噪声</td></tr><tr><td>$\alpha_t, \beta_t = 1 - \alpha_t$</td><td>DDPM 单步 forward 系数</td></tr><tr><td>$\bar\alpha_t = \prod_{s=1}^t \alpha_s$</td><td>DDPM cumulative 系数</td></tr><tr><td>$\sigma_t$</td><td>标准差(NCSN / EDM 视角的"噪声水平")</td></tr><tr><td>$s_\theta(x_t, t) \approx \nabla_{x_t}\log p_t(x_t)$</td><td>score</td></tr><tr><td>$\epsilon_\theta(x_t, t) \approx \epsilon$</td><td>DDPM 中预测的噪声</td></tr><tr><td>$D_\theta(x; \sigma) \approx x_0$</td><td>EDM 的 denoiser 输出</td></tr></tbody></table>
|
||
<div class="callout callout-warn"><div class="callout-title">时间方向陷阱</div><p>DDPM 论文 forward 是 $t = 0 \to T$(数据加噪到纯噪声),reverse 是 $T \to 0$;FM 论文常用 $t = 0$ 噪声、$t = 1$ 数据。<strong>面试写代码前一定要先 disambiguate 时间方向</strong>——否则 sampler 容易跑反。</p></div>
|
||
<h2 id="2-ddpm-forward-process">§2 DDPM Forward Process</h2>
|
||
<h3 id="21-单步与闭式表达">2.1 单步与闭式表达</h3>
|
||
<p>DDPM forward 是一条 <strong>Markov chain</strong>:</p>
|
||
<p>$$q(x_t | x_{t-1}) = \mathcal{N}(x_t;\; \sqrt{1-\beta_t}\, x_{t-1},\; \beta_t I), \quad t = 1, \dots, T$$</p>
|
||
<p>定义 $\alpha_t = 1 - \beta_t$,$\bar\alpha_t = \prod_{s=1}^t \alpha_s$。<strong>关键性质</strong>:$q(x_t | x_0)$ 是 <strong>闭式 Gaussian</strong>——可以一步从 $x_0$ 跳到任意 $t$(训练效率的核心):</p>
|
||
<p>$$\boxed{\; q(x_t | x_0) = \mathcal{N}\!\left(x_t;\; \sqrt{\bar\alpha_t}\, x_0,\; (1-\bar\alpha_t) I\right) \;}$$</p>
|
||
<p>等价 reparameterization:</p>
|
||
<p>$$x_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1-\bar\alpha_t}\, \epsilon, \quad \epsilon \sim \mathcal{N}(0, I)$$</p>
|
||
<h3 id="22-闭式推导必考会反复出现">2.2 闭式推导(必考,会反复出现)</h3>
|
||
<p>由 reparameterization $x_t = \sqrt{\alpha_t} x_{t-1} + \sqrt{\beta_t} z_t$,$z_t \sim \mathcal{N}(0, I)$ 独立。递推:</p>
|
||
<p>$$ \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} $$</p>
|
||
<p>两个独立 Gaussian 之和的方差:$\alpha_t \beta_{t-1} + \beta_t = \alpha_t(1 - \alpha_{t-1}) + (1 - \alpha_t) = 1 - \alpha_t \alpha_{t-1}$。所以可以合并成单个 Gaussian $\sqrt{1 - \alpha_t \alpha_{t-1}}\, \bar z$。归纳到 $t$ 步:</p>
|
||
<p>$$x_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1 - \bar\alpha_t}\, \epsilon$$</p>
|
||
<div class="callout callout-info"><div class="callout-title">变分 trick 直觉</div><p>Markov chain 的好处是每一步都是 Gaussian,所以累积仍是 Gaussian;这让 forward 不用网络就能采样、训练时不用模拟整条链。</p></div>
|
||
<h3 id="23-边界与极限">2.3 边界与极限</h3>
|
||
<ul><li>$t = 0$:$\bar\alpha_0 = 1$,$x_0$ 自身 —— forward 起点</li><li>$t = T$(DDPM 取 1000):要求 $\bar\alpha_T \approx 0$,则 $x_T \approx \epsilon \sim \mathcal{N}(0, I)$ —— forward 终点接近 Gaussian prior</li></ul>
|
||
<div class="callout callout-warn"><div class="callout-title">Schedule 末端的 SNR (Signal-to-Noise Ratio)</div><p>SNR$(t) = \bar\alpha_t / (1-\bar\alpha_t)$;linear schedule 在 $t=T$ 时 $\bar\alpha_T \approx 4\times 10^{-5}$ 对应 SNR $\approx 4\times 10^{-5}$——虽然很小但严格意义上未到 0,prior 仍非完全匹配 $\mathcal{N}(0,I)$;这是 cosine schedule 与 "v-prediction" 改进的动机之一。</p></div>
|
||
<h2 id="3-ddpm-reverse-process--训练">§3 DDPM Reverse Process & 训练</h2>
|
||
<h3 id="31-reverse-是-gaussian-的前提">3.1 Reverse 是 Gaussian 的前提</h3>
|
||
<p>理论上 $q(x_{t-1} | x_t)$ 不是 Gaussian(依赖整个数据分布)。但当 $\beta_t$ 足够小时,反向条件分布 <strong>近似</strong> 是 Gaussian(Feller 1949 / Sohl-Dickstein 2015),所以参数化为:</p>
|
||
<p>$$p_\theta(x_{t-1} | x_t) = \mathcal{N}\!\left(x_{t-1};\; \mu_\theta(x_t, t),\; \Sigma_\theta(x_t, t)\right)$$</p>
|
||
<h3 id="32-elbo-推导">3.2 ELBO 推导</h3>
|
||
<p>DDPM 优化 evidence lower bound(与 VAE 类似):</p>
|
||
<p>$$ \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} $$</p>
|
||
<p><strong>核心</strong>:$q(x_{t-1} | x_t, x_0)$ 是闭式 Gaussian(由 Bayes 推得):</p>
|
||
<p>$$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)$$</p>
|
||
<p>其中:</p>
|
||
<p>$$\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$$</p>
|
||
<h3 id="33-化简到-ltextsimple必考推导">3.3 化简到 $L_\text{simple}$(必考推导)</h3>
|
||
<p>把 $x_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon) / \sqrt{\bar\alpha_t}$ 代入 $\tilde\mu_t$:</p>
|
||
<p>$$\tilde\mu_t = \frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{\beta_t}{\sqrt{1-\bar\alpha_t}}\epsilon\right)$$</p>
|
||
<p>参数化 $\mu_\theta(x_t, t)$ 也采用同样形式(<strong>$\epsilon$-prediction</strong>):</p>
|
||
<p>$$\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)$$</p>
|
||
<p>固定 $\Sigma_\theta = \sigma_t^2 I$(取 $\sigma_t^2 = \beta_t$ 或 $\tilde\beta_t$)。两个 Gaussian 的 KL:</p>
|
||
<p>$$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]$$</p>
|
||
<p><strong>Ho 2020 的工程妙招</strong>:扔掉前面所有系数 + 常数项,直接用 unweighted 版本:</p>
|
||
<p>$$\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] \;}$$</p>
|
||
<div class="callout callout-good"><div class="callout-title">为什么扔系数也能 work?</div><p>Ho 2020 经验观察:unweighted 版本相当于<strong>给低 SNR(高 $t$)loss 更大权重</strong>,反而提升 sample 质量。但代价是 $\log$-likelihood 不再是 ELBO 下界——所以"FID 好"≠"likelihood 好"。后续 Improved DDPM (Nichol-Dhariwal 2021) 引入 hybrid loss $L_\text{hybrid} = L_\text{simple} + \lambda L_\text{vlb}$($\lambda = 0.001$),同时学 $\Sigma_\theta$。</p></div>
|
||
<h3 id="34-预测目标的等价转换必背">3.4 预测目标的等价转换(必背)</h3>
|
||
<p>给定 $x_t = \sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t}\epsilon$,三种主流参数化线性可逆:</p>
|
||
<p>$$ \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} $$</p>
|
||
<div class="callout callout-info"><div class="callout-title">v-prediction 为何更稳?</div><p>$\epsilon$-pred 在 $t \to 0$(小噪声)时退化(loss 系数爆炸);$x_0$-pred 在 $t \to T$(大噪声)时退化;$v$-pred 是两者插值,在所有 $t$ 上 loss 数值范围近似一致——是 Imagen Video / SD2.1-v / Karras EDM 选用的关键。</p></div>
|
||
<h2 id="4-schedulelinear--cosine--edm">§4 Schedule:linear / cosine / EDM</h2>
|
||
<h3 id="41-linear-ho-2020">4.1 Linear (Ho 2020)</h3>
|
||
<p>$$\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$$</p>
|
||
<p>$T = 1000$。简单、稳定,但末端 SNR 未严格到 0($\bar\alpha_T \approx 4 \times 10^{-5}$,对应 SNR $\approx 4 \times 10^{-5}$,理想 prior 要求更接近 0)。</p>
|
||
<h3 id="42-cosine-nichol-dhariwal-2021">4.2 Cosine (Nichol-Dhariwal 2021)</h3>
|
||
<p>$$\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$$</p>
|
||
<p>$\beta_t = 1 - \bar\alpha_t / \bar\alpha_{t-1}$(再裁剪到 $[0, 0.999]$ 防止数值问题)。$s = 0.008$ 是为了让 $\beta_1$ 不要太接近 0。</p>
|
||
<div class="callout callout-good"><div class="callout-title">Cosine schedule 为什么更好?</div><p>linear schedule 在低 $t$ 区域加噪太快,模型大部分时间"练"在已经全是噪声的区域(学不到东西)。cosine 在低 $t$ 加噪缓慢、中间快、末端 SNR 真的接近 0。Improved DDPM 实验:cosine 比 linear 在 ImageNet 64 上 FID 提升约 20%。</p></div>
|
||
<h3 id="43-edm--schedule-karras-2022">4.3 EDM σ-schedule (Karras 2022)</h3>
|
||
<p>EDM 把 $\beta$ schedule 重新参数化为 $\sigma$ schedule(直接用 $\sigma$ 当时间)。采样时:</p>
|
||
<p>$$\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$$</p>
|
||
<p>默认 $\sigma_\text{min} = 0.002$, $\sigma_\text{max} = 80$, $\rho = 7$。<strong>$\rho = 7$ 是 Karras 实验扫出来的</strong>——比线性 / 对数都好,因为它把更多步骤分配在小 $\sigma$(高 SNR)区域,那里步进误差更敏感。</p>
|
||
<div class="callout callout-info"><div class="callout-title">离散 vs 连续 schedule</div><p>DDPM 的 $\beta$ 数组等价于 VP-SDE 的 $\beta(t) = T \beta_{\lfloor tT \rfloor}$;EDM 的 $\sigma$-schedule 等价于 VE-SDE 的 $\sigma(t) = t$(线性时间);两者只差一个 $t$ 重参数化,<strong>信息上等价</strong>。EDM 的贡献是发现一组工程上更稳的 $\sigma_i$ 选取规则。</p></div>
|
||
<h2 id="5-score-based-视角">§5 Score-based 视角</h2>
|
||
<h3 id="51-score-与-score-matching-hyvrinen-2005">5.1 Score 与 score matching (Hyvärinen 2005)</h3>
|
||
<p>定义 $s(x) = \nabla_x \log p(x)$。如果学到 $s_\theta \approx s$,可以用 <strong>Langevin dynamics</strong> 采样:</p>
|
||
<p>$$x_{k+1} = x_k + \frac{\eta}{2} s_\theta(x_k) + \sqrt{\eta}\, z_k, \quad z_k \sim \mathcal{N}(0, I)$$</p>
|
||
<p>直接 score matching loss $\mathbb{E}_p\|s_\theta - \nabla\log p\|^2$ 不可计算(不知道 $\nabla \log p$)。Hyvärinen 2005 给出 <strong>implicit score matching</strong> 通过积分变换避开 $\nabla \log p$:</p>
|
||
<p>$$\mathbb{E}_p\left[\|s_\theta(x)\|^2 + 2 \operatorname{tr}(\nabla_x s_\theta(x))\right]$$</p>
|
||
<p>但 $\operatorname{tr}(\nabla_x s_\theta)$ 在高维下太贵(Hessian trace)。</p>
|
||
<h3 id="52-denoising-score-matching-vincent-2011">5.2 Denoising Score Matching (Vincent 2011)</h3>
|
||
<p>对每个数据点 $x_0$,加噪 $\tilde x = x_0 + \sigma \epsilon$,定义 perturbed distribution $p_\sigma(\tilde x) = \int p(x_0) \mathcal{N}(\tilde x; x_0, \sigma^2 I) dx_0$。Vincent 2011 证明:</p>
|
||
<p>$$\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}$$</p>
|
||
<p>而 $q(\tilde x | x_0) = \mathcal{N}(x_0, \sigma^2 I)$ 的 score <strong>闭式</strong>:</p>
|
||
<p>$$\nabla_{\tilde x} \log q(\tilde x | x_0) = -\frac{\tilde x - x_0}{\sigma^2} = -\frac{\epsilon}{\sigma}$$</p>
|
||
<p>所以训练 loss 简化为:</p>
|
||
<p>$$\boxed{\; L_\text{DSM}(\theta) = \mathbb{E}_{x_0, \sigma, \epsilon}\left\| \sigma\, s_\theta(\tilde x; \sigma) + \epsilon \right\|^2 \;}$$</p>
|
||
<p>这正是 NCSN / SMLD 的训练目标(差一个权重)。</p>
|
||
<h3 id="53-tweedie-公式必考推导">5.3 Tweedie 公式(必考推导)</h3>
|
||
<p><strong>陈述</strong>:对加性 Gaussian 噪声 $x_t = x_0 + \sigma_t \epsilon$(VE 视角,$\epsilon \sim \mathcal{N}(0,I)$):</p>
|
||
<p>$$\boxed{\; \mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2\, \nabla_{x_t} \log p_t(x_t) \;}$$</p>
|
||
<p><strong>推导</strong>:$p_t(x_t) = \int p_0(x_0) \mathcal{N}(x_t; x_0, \sigma_t^2 I)\, dx_0$。对 $x_t$ 求梯度:</p>
|
||
<p>$$\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$$</p>
|
||
<p>两边除以 $p_t(x_t)$:</p>
|
||
<p>$$\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]$$</p>
|
||
<p>即:</p>
|
||
<p>$$\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$$</p>
|
||
<div class="callout callout-good"><div class="callout-title">Tweedie 是连接所有 diffusion 参数化的"罗塞塔石碑"</div><p>denoiser 网络的最优输出(MMSE estimator)就是 score 加上恒等映射。所有 $\epsilon$-pred / score-pred / $x_0$-pred / $v$-pred 之间的转换都是 Tweedie 的一行式重排。</p></div>
|
||
<h3 id="54-ncsn--smld-song-ermon-2019">5.4 NCSN / SMLD (Song-Ermon 2019)</h3>
|
||
<p><strong>Noise-Conditional Score Network</strong>:训练一个共享网络 $s_\theta(x, \sigma)$,对一组噪声水平 $\sigma_1 > \sigma_2 > \dots > \sigma_L$ 同时做 DSM。采样时做 <strong>annealed Langevin dynamics</strong>:先在大 $\sigma_1$ 上 Langevin(探索全空间),逐步降到 $\sigma_L$(精修细节)。</p>
|
||
<p>$$x \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$$</p>
|
||
<p>每个 $\sigma_i$ 跑 $T$ 步 Langevin,然后切到下一个 $\sigma_{i+1}$。</p>
|
||
<div class="callout callout-warn"><div class="callout-title">为什么单一 $\sigma$ 不行?</div><p>小 $\sigma$ 训出来的 score 在远离数据流形的地方完全错(mode 之间的"空地带" $p(x) \approx 0$,score 给不出方向)。多 noise level 的核心是用大 $\sigma$ 把空间"填满",给小 $\sigma$ 提供初始位置。</p></div>
|
||
<h2 id="6-score-sde统一框架--probability-flow-ode">§6 Score SDE:统一框架 + Probability Flow ODE</h2>
|
||
<h3 id="61-forward-sde">6.1 Forward SDE</h3>
|
||
<p>Song et al. 2021 (ICLR) 把所有 diffusion 写成 <strong>forward SDE</strong>:</p>
|
||
<p>$$dx = f(x, t)\, dt + g(t)\, dW$$</p>
|
||
<table><thead><tr><th>Type</th><th>$f(x, t)$</th><th>$g(t)$</th><th>对应离散版</th></tr></thead><tbody><tr><td><strong>VP-SDE</strong> (variance preserving)</td><td>$-\frac{1}{2}\beta(t) x$</td><td>$\sqrt{\beta(t)}$</td><td>DDPM</td></tr><tr><td><strong>VE-SDE</strong> (variance exploding)</td><td>$0$</td><td>$\sqrt{d[\sigma^2(t)]/dt}$</td><td>SMLD / EDM</td></tr><tr><td><strong>sub-VP</strong></td><td>$-\frac{1}{2}\beta(t) x$</td><td>$\sqrt{\beta(t)(1-e^{-2\int_0^t \beta(s)ds})}$</td><td>介于 VP/VE,likelihood 更好</td></tr></tbody></table>
|
||
<p>VP-SDE 满足 $\text{Var}[x_t] \le 1$(variance preserving),VE-SDE 让方差无界增长(variance exploding)。</p>
|
||
<h3 id="62-reverse-sde-anderson-1982">6.2 Reverse SDE (Anderson 1982)</h3>
|
||
<p>对任意 forward SDE,存在 <strong>reverse-time SDE</strong>:</p>
|
||
<p>$$\boxed{\; dx = \left[f(x, t) - g^2(t)\, \nabla_x \log p_t(x)\right] dt + g(t)\, d\bar W \;}$$</p>
|
||
<p>$d\bar W$ 是 reverse-time Wiener process。<strong>采样</strong>:从 $x_T \sim p_T$(接近 prior)开始,用 SDE solver(Euler-Maruyama / predictor-corrector)积分到 $t = 0$。</p>
|
||
<h3 id="63-probability-flow-ode与-fm-的桥梁">6.3 Probability Flow ODE(与 FM 的桥梁)</h3>
|
||
<p><strong>关键定理</strong>(Song et al. 2021, "Score-Based Generative Modeling through SDEs"):以下确定性 ODE 与 reverse SDE 共享所有时刻的边缘 $p_t$:</p>
|
||
<p>$$\boxed{\; \frac{dx}{dt} = f(x, t) - \frac{1}{2} g^2(t)\, \nabla_x \log p_t(x) \;}$$</p>
|
||
<p>这就是 <strong>probability flow ODE</strong>。等价于 Flow Matching 的 vector field:</p>
|
||
<p>$$u_t(x) = f(x, t) - \tfrac{1}{2} g^2(t)\, s_\theta(x, t)$$</p>
|
||
<div class="callout callout-good"><div class="callout-title">三种采样器的关系图</div><pre class="diagram"><code> forward SDE (训练: score matching)
|
||
↓
|
||
┌──────────────────────┐
|
||
↓ ↓
|
||
reverse SDE probability flow ODE
|
||
(stochastic) (deterministic, ⇔ FM)
|
||
↓ ↓
|
||
DDPM ancestral sampler DDIM (η=0) / EDM / DPM-Solver</code></pre></div>
|
||
<p><strong>证明草图</strong>:写 forward SDE 的 Fokker-Planck(连续性方程):</p>
|
||
<p>$$\frac{\partial p_t}{\partial t} = -\nabla \cdot (f p_t) + \frac{1}{2} g^2 \Delta p_t$$</p>
|
||
<p>利用 $\Delta p_t = \nabla \cdot (p_t \nabla \log p_t)$ 把扩散项写成 transport 形式:</p>
|
||
<p>$$\frac{\partial p_t}{\partial t} = -\nabla \cdot \left[\left(f - \tfrac{1}{2} g^2 \nabla \log p_t\right) p_t\right]$$</p>
|
||
<p>这正是 ODE $dx/dt = f - \frac{1}{2} g^2 \nabla \log p_t$ 的连续性方程——所以它们的 $p_t$ 一致。</p>
|
||
<h3 id="64-ode-视角的优势">6.4 ODE 视角的优势</h3>
|
||
<table><thead><tr><th>优势</th><th>说明</th></tr></thead><tbody><tr><td><strong>Deterministic</strong></td><td>同一 noise → 同一 sample,可做 image editing / interpolation</td></tr><tr><td><strong>NFE 友好</strong></td><td>高阶 ODE solver(Heun / RK4 / DPM-Solver)少步数即可</td></tr><tr><td><strong>Likelihood 可计算</strong></td><td>$\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</td></tr><tr><td><strong>桥到 FM</strong></td><td>RF / SD3 / FLUX 走这条线</td></tr></tbody></table>
|
||
<div class="callout callout-warn"><div class="callout-title">SDE vs ODE 的 trade-off</div><p>SDE 采样的随机扰动可以"修正"早期错误,<strong>通常 sample 质量更高</strong>但 NFE 大;ODE deterministic 但易受 solver 误差累积,需更高阶 solver。EDM 提出折中:基础 ODE + 小幅 stochastic churn("$S_\text{churn}$"),FID 更好。</p></div>
|
||
<h2 id="7-ddimnon-markovian-forward--deterministic-sampler">§7 DDIM:Non-Markovian Forward → Deterministic Sampler</h2>
|
||
<h3 id="71-motivation">7.1 Motivation</h3>
|
||
<p>DDPM ancestral sampling 必须走 $T = 1000$ 步(Markov 链)。能否<strong>少步数采样</strong>且不重训?DDIM (Song et al. 2020 arXiv / ICLR 2021) 给出"yes"——核心是把 forward 改成 non-Markovian,但保持<strong>与 DDPM 一样的 marginal $q(x_t | x_0)$</strong>。</p>
|
||
<h3 id="72-non-markovian-forward">7.2 Non-Markovian Forward</h3>
|
||
<p>DDIM 定义一族 forward distribution,由参数 $\eta \in [0, 1]$ 控制:</p>
|
||
<p>$$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)$$</p>
|
||
<p>其中 $\sigma_t^2 = \eta^2 \cdot \tilde\beta_t = \eta^2 \cdot \frac{1-\bar\alpha_{t-1}}{1-\bar\alpha_t} \beta_t$。</p>
|
||
<p><strong>关键性质</strong>(DDIM Theorem 1):在此 forward 下,<strong>$q(x_t | x_0)$ 仍是 $\mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I)$</strong>——与 DDPM 完全一致!所以可以<strong>直接用 DDPM 训练的 $\epsilon_\theta$</strong> 做 DDIM 采样。</p>
|
||
<h3 id="73-ddim-采样公式">7.3 DDIM 采样公式</h3>
|
||
<p>把 $x_0 \to \hat x_0 = (x_t - \sqrt{1-\bar\alpha_t}\, \epsilon_\theta(x_t, t)) / \sqrt{\bar\alpha_t}$ 代入:</p>
|
||
<p>$$\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) \;}$$</p>
|
||
<ul><li><strong>$\eta = 0$(DDIM)</strong>:$\sigma_t = 0$,<strong>确定性</strong>——同 $x_T$ 同 $\hat x_0$(latent space interpolation 友好)</li><li><strong>$\eta = 1$(走完整 $T$ 步)</strong>:$\sigma_t = \sqrt{\tilde\beta_t}$,退化为标准 DDPM ancestral sampler;skip 步数 $S < T$ 时只是匹配方差量级,不严格等价 1000 步 DDPM</li><li>中间 $\eta \in (0, 1)$:随机性可调</li></ul>
|
||
<h3 id="74-skip-steps少步数采样">7.4 Skip steps(少步数采样)</h3>
|
||
<p>不必逐步 $t \to t-1$,可以跳:选 sub-sequence $\tau_0 < \tau_1 < \dots < \tau_S = T$,做:</p>
|
||
<p>$$x_{\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$$</p>
|
||
<p>经典 baseline:$S = 50$ 步 DDIM 在 ImageNet 256 上 FID 接近 DDPM 1000 步。</p>
|
||
<div class="callout callout-good"><div class="callout-title">DDIM = probability flow ODE 的离散化</div><p>当 $\eta = 0$ 且时间网格连续化时,DDIM 退化为 VP-SDE 对应的 probability flow ODE 的一阶 Euler 离散——这是为什么 deterministic DDIM 与 ODE-based 采样(DPM-Solver、EDM Heun)连成一线。</p></div>
|
||
<h2 id="8-edmkarras-2022-设计空间">§8 EDM:Karras 2022 设计空间</h2>
|
||
<h3 id="81-motivation">8.1 Motivation</h3>
|
||
<p>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。</p>
|
||
<h3 id="82-preconditioning必考推导">8.2 Preconditioning(必考推导)</h3>
|
||
<p>EDM 用 <strong>VE 视角</strong>:$x = x_0 + \sigma \epsilon$,$\epsilon \sim \mathcal{N}(0, I)$,$\sigma$ 直接当 noise level(没有 $\alpha$)。</p>
|
||
<p><strong>Denoiser</strong> 参数化:</p>
|
||
<p>$$\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) \;}$$</p>
|
||
<p>其中 $F_\theta$ 是底层网络,四个 $c$ 函数是 <strong>手工 schedule</strong>。Karras 推导:</p>
|
||
<h4 id="推导unit-variance-论证">推导:unit-variance 论证</h4>
|
||
<p><strong>目标</strong>:让 $F_\theta$ 的输入和训练 target 在所有 $\sigma$ 上方差都是 $\mathcal{O}(1)$。</p>
|
||
<p><strong>输入侧</strong>:网络看到的输入 $c_\text{in} x$。已知 $\text{Var}[x] = \sigma_\text{data}^2 + \sigma^2$(数据方差 + 噪声方差),所以:</p>
|
||
<p>$$c_\text{in}(\sigma) = \frac{1}{\sqrt{\sigma_\text{data}^2 + \sigma^2}} \quad \Rightarrow \quad \text{Var}[c_\text{in} x] = 1$$</p>
|
||
<p><strong>输出侧</strong>:理想 denoiser $D^*(x; \sigma) = \mathbb{E}[x_0 | x]$(Tweedie)。我们让网络学<strong>残差</strong>而非全量:定义 effective target</p>
|
||
<p>$$F^*(x; \sigma) = \frac{1}{c_\text{out}(\sigma)}\left[D^*(x;\sigma) - c_\text{skip}(\sigma)\, x\right]$$</p>
|
||
<p>希望 $\text{Var}[c_\text{out} F^* + c_\text{skip} x - D^*] = 0$ 且 $\text{Var}[F^*] = 1$(让网络的 target 单位方差)。</p>
|
||
<p>求最小 effective error 的 $c_\text{skip}$ 与 $c_\text{out}$(最小化 $\mathbb{E}\|F^* - F_\theta\|^2$ 在 $\text{Var}[F^*]=1$ 约束下)。Karras 取 $D^* = x_0$(理想情形),代入并展开:</p>
|
||
<p>$$c_\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}}$$</p>
|
||
<p><strong>直觉</strong>:</p>
|
||
<ul><li>$\sigma \to 0$(低噪声):$c_\text{skip} \to 1, c_\text{out} \to 0$ —— 输出基本是 input identity(denoiser 啥都不用做)</li><li>$\sigma \to \infty$(高噪声):$c_\text{skip} \to 0, c_\text{out} \to \sigma_\text{data}$ —— 输出由 network 完全决定(input 全是噪声)</li></ul>
|
||
<p><strong>时间编码</strong>:$c_\text{noise}(\sigma) = \frac{1}{4} \ln \sigma$(log-scale,覆盖 $\sigma \in [\sigma_\text{min}, \sigma_\text{max}]$ 的宽动态范围)。</p>
|
||
<h3 id="83-训练-loss">8.3 训练 loss</h3>
|
||
<p>EDM 用 weighted L2:</p>
|
||
<p>$$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]$$</p>
|
||
<p>权重 $\lambda(\sigma) = (\sigma^2 + \sigma_\text{data}^2) / (\sigma \cdot \sigma_\text{data})^2 = 1/c_\text{out}^2$,等价 <strong>训练 $F_\theta$ 用 unweighted L2</strong>(每个 $\sigma$ 上 target 单位方差,loss 数量级一致)。</p>
|
||
<p><strong>$\sigma$ 训练采样</strong>:$\ln \sigma \sim \mathcal{N}(P_\text{mean}, P_\text{std}^2)$,默认 $P_\text{mean} = -1.2$, $P_\text{std} = 1.2$(让 $\sigma$ 集中在 $0.3$ 附近——这是"最难学"的 SNR 区域,Karras 实验扫出来的)。</p>
|
||
<h3 id="84-heun-2nd-order-sampler">8.4 Heun 2nd-order sampler</h3>
|
||
<p>EDM 采样默认用 <strong>Heun 二阶 ODE</strong> + 可选 stochastic churn。VE-SDE 的 probability flow ODE 在 $f = 0, g(t) = \sqrt{d\sigma^2/dt}$ 下:</p>
|
||
<p>$$\frac{dx}{d\sigma} = -\sigma\, \nabla_x \log p_\sigma(x) = \frac{x - D_\theta(x; \sigma)}{\sigma}$$</p>
|
||
<p>(用 Tweedie:$\nabla \log p_\sigma = (D - x)/\sigma^2$,代入 $dx/d\sigma = -\sigma \nabla \log p$)</p>
|
||
<p>Heun 第 $i$ 步($\sigma_i \to \sigma_{i+1}$,$\Delta\sigma = \sigma_{i+1} - \sigma_i$):</p>
|
||
<pre><code>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_*</code></pre>
|
||
<p><strong>每步 2 NFE</strong>,但二阶精度——比 Euler 一阶 NFE 多但更准。CIFAR-10 EDM 配 35 NFE = 18 steps Heun + 一阶末端,FID 1.79。</p>
|
||
<div class="callout callout-info"><div class="callout-title">Stochastic churn(可选)</div><p>在每步开始时把 $\sigma_i$ 临时提高到 $\hat\sigma_i = (1+\gamma_i)\sigma_i$($\gamma_i$ 是当前步的小幅 churn),需注入额外噪声:$\hat x_i = x_i + \sqrt{\hat\sigma_i^2 - \sigma_i^2}\, z$,其中 $\sqrt{\hat\sigma_i^2 - \sigma_i^2} = \sigma_i\sqrt{2\gamma_i + \gamma_i^2}$;从 $\hat\sigma_i$ 降回 $\sigma_{i+1}$ 等价小幅 SDE。EDM 实验:少量 churn 在 ImageNet 上略涨 FID(约 0.1-0.3)。</p></div>
|
||
<h2 id="9-高阶采样器dpm-solver--dpm-solver">§9 高阶采样器:DPM-Solver / DPM-Solver++</h2>
|
||
<h3 id="91-motivation">9.1 Motivation</h3>
|
||
<p>DDIM 是一阶 ODE Euler。<strong>DPM-Solver</strong> (Lu et al. 2022 NeurIPS) 利用 diffusion ODE 的<strong>半线性结构</strong>做高阶展开。<strong>Probability flow ODE</strong> 在 VP-SDE 下用 $\epsilon$-pred 改写:</p>
|
||
<p>$$\frac{dx}{dt} = f(t)\, x + g(t)\, \epsilon_\theta(x, t)$$</p>
|
||
<p>其中 $f(t) = -\frac{1}{2}\beta(t)$,$g(t) = +\frac{1}{2}\beta(t)/\sqrt{1-\bar\alpha_t}$(来自 $-\frac{1}{2}g_\text{SDE}^2 \cdot s = +\frac{1}{2}\beta\cdot \epsilon/\sqrt{1-\bar\alpha_t}$,因为 $s = -\epsilon/\sqrt{1-\bar\alpha_t}$)。</p>
|
||
<p>把线性部分<strong>精确积分</strong>(exponential integrator),剩余部分用 Taylor 展开。</p>
|
||
<h3 id="92-dpm-solver-2--3核心思想">9.2 DPM-Solver-2 / 3(核心思想)</h3>
|
||
<p>设 $\lambda_t = \log(\sqrt{\bar\alpha_t} / \sqrt{1-\bar\alpha_t})$(log-SNR),用 $\lambda$ 当时间变量。ODE 重写:</p>
|
||
<p>$$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$$</p>
|
||
<p>把 $\hat\epsilon_\theta$ 在 $\lambda$ 上做 $k$ 阶 Taylor 展开,<strong>线性部分精确</strong>(exponential weight),剩余按阶数取近似:</p>
|
||
<ul><li><strong>DPM-Solver-1</strong> = DDIM(一阶)</li><li><strong>DPM-Solver-2</strong>:每步 2 NFE,二阶</li><li><strong>DPM-Solver-3</strong>:每步 3 NFE,三阶</li></ul>
|
||
<p>10-15 NFE 即可达到 50 NFE DDIM 同质量。</p>
|
||
<h3 id="93-dpm-solvercfg-友好版lu-et-al-2023">9.3 DPM-Solver++(CFG 友好版,Lu et al. 2023)</h3>
|
||
<p>原 DPM-Solver 在 CFG 下不稳($\epsilon_\theta$ 经 CFG amplify 后超出训练域,Taylor 展开误差大)。DPM-Solver++ 改用 <strong>$x_0$-prediction</strong>:</p>
|
||
<p>$$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$$</p>
|
||
<p>(用 $x_0$-pred 而非 $\epsilon$-pred 让 CFG amplification 落在更稳的区域)</p>
|
||
<p>15-20 NFE 在 CFG=7 下质量 close to 100-NFE DDIM。SDXL / SD3 默认 sampler 之一。</p>
|
||
<h3 id="94-采样器对比">9.4 采样器对比</h3>
|
||
<div class="callout callout-info"><div class="callout-title">常见 sampler 选择 cheat sheet</div><p>按 NFE/质量/适配排序如下(图像生成)。</p></div>
|
||
<ul><li><strong>DDPM ancestral</strong>:T=1000 步,作 baseline;现代少用</li><li><strong>DDIM ($\eta = 0$)</strong>:50-100 NFE,简单稳定,可做 interpolation</li><li><strong>PLMS / PNDM</strong>:50 NFE,linear-multistep,AUTOMATIC1111 老 default</li><li><strong>EDM Heun</strong>:18-35 NFE,确定性 ODE 二阶,文献 SOTA baseline</li><li><strong>DPM-Solver / DPM-Solver++</strong>:10-20 NFE,HuggingFace diffusers 推荐</li><li><strong>UniPC</strong> (Zhao 2023):predictor-corrector framework,可超过 DPM-Solver</li><li><strong>Consistency Models (one-step / two-step)</strong>:1-4 NFE,需蒸馏</li></ul>
|
||
<h2 id="10-conditioningclassifier-guidance--cfg">§10 Conditioning:Classifier Guidance & CFG</h2>
|
||
<h3 id="101-classifier-guidance-dhariwal-nichol-2021">10.1 Classifier Guidance (Dhariwal-Nichol 2021)</h3>
|
||
<p>训练一个独立 classifier $p_\phi(c | x_t)$(在 noisy data 上),用 Bayes:</p>
|
||
<p>$$\nabla_{x_t} \log p(x_t | c) = \nabla_{x_t} \log p(x_t) + \nabla_{x_t} \log p_\phi(c | x_t)$$</p>
|
||
<p>实践中给 classifier gradient 加 scale $w$(控制 guidance 强度):</p>
|
||
<p>$$\tilde\epsilon = \epsilon_\theta(x_t, t) - w \sqrt{1-\bar\alpha_t}\, \nabla_{x_t} \log p_\phi(c | x_t)$$</p>
|
||
<div class="callout callout-warn"><div class="callout-title">Classifier guidance 的缺点</div><p>(a) 必须额外训 noisy classifier,工程负担;(b) classifier gradient 易"对抗",在远离训练分布时退化;(c) 对 text-to-image 这种连续 condition 不友好。CFG 完全替代了它。</p></div>
|
||
<h3 id="102-classifier-free-guidance-ho-salimans-2022">10.2 Classifier-Free Guidance (Ho-Salimans 2022)</h3>
|
||
<p><strong>训练</strong>:以概率 $p_\text{drop}$(一般 0.1)把 $c$ 替换为 $\emptyset$(null embedding),同一个 net 学 conditional 和 unconditional:</p>
|
||
<p>$$L_\text{CFG}(\theta) = \mathbb{E}\big[\|\epsilon - \epsilon_\theta(x_t, t, c \text{ or } \emptyset)\|^2\big]$$</p>
|
||
<p><strong>推理</strong>:把 $w$ 称为 <strong>guidance scale</strong>:</p>
|
||
<p>$$\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] \;}$$</p>
|
||
<p>等价形式(Imagen / SD 实现常用):</p>
|
||
<p>$$\tilde\epsilon = (1 + w)\, \epsilon_\theta(x_t, t, c) - w\, \epsilon_\theta(x_t, t, \emptyset)$$</p>
|
||
<div class="callout callout-warn"><div class="callout-title">CFG $w$ 的两种 convention</div><p>论文 Ho-Salimans 2022 原文 $\tilde\epsilon = \epsilon_\text{uncond} + (1+w)(\epsilon_\text{cond} - \epsilon_\text{uncond})$,即 $w = 0$ 是 unguided、$w > 0$ 增强。但 HuggingFace / SD UI 常用 $w' = w + 1$,即 $w' = 1$ 是 unguided、$w' = 7.5$ 是常用强度。<strong>面试代码记得标明 convention</strong>。</p></div>
|
||
<h3 id="103-cfg-的几何意义">10.3 CFG 的几何意义</h3>
|
||
<p>CFG 等价于把采样轨迹拉向"条件梯度"方向:</p>
|
||
<p>$$\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)}$$</p>
|
||
<p>第二项是"条件性 score 差",把样本推向 conditional likelihood 高、unconditional likelihood 相对低的区域——直觉上"放大文本对齐"。</p>
|
||
<div class="callout callout-good"><div class="callout-title">CFG 是 SD/SDXL/FLUX 文图对齐的核心</div><p>$w \in [3, 7.5]$ 是 Stable Diffusion 的实验 sweet spot;$w > 10$ 容易 over-saturated(颜色饱和、artifact)。FLUX 把 CFG 内化进 distillation("guidance-distilled"),单 forward 就实现 CFG 效果——这是它推理速度的关键之一。</p></div>
|
||
<h2 id="11-production从-ldm-到-flux">§11 Production:从 LDM 到 FLUX</h2>
|
||
<h3 id="111-latent-diffusion-ldm-rombach-2022-cvpr">11.1 Latent Diffusion (LDM, Rombach 2022 CVPR)</h3>
|
||
<p><strong>核心 idea</strong>:在 VAE latent space 而非 pixel space 跑 diffusion。</p>
|
||
<ol><li>训一个 VAE $E, D$:$z = E(x), \hat x = D(z)$,$z$ 比 $x$ 小 ~8×(如 $512^2 \times 3 \to 64^2 \times 4$)</li><li>在 $z$ 上训 diffusion model(参数量、显存、计算全部降一个数量级)</li><li>生成时:从 $z_T$ 采样到 $z_0$,再用 $D(z_0)$ decode 回 pixel</li></ol>
|
||
<p><strong>Stable Diffusion (SD)</strong> = LDM + CLIP text encoder + UNet on $64 \times 64 \times 4$ latent,是当时最实用的开源 T2I 模型。</p>
|
||
<h3 id="112-sdxl-podell-et-al-2023-arxiv--iclr-2024-spotlight">11.2 SDXL (Podell et al. 2023 arXiv / ICLR 2024 spotlight)</h3>
|
||
<p>SD 1.5 → SDXL 的主要改进:</p>
|
||
<ul><li><strong>更大 UNet</strong>:参数从 ~860M 升到 ~2.6B,cross-attn 层更多</li><li><strong>二阶段架构</strong>:base + refiner(refiner 在低噪声段补细节)</li><li><strong>更好的 text encoder</strong>:OpenCLIP ViT-bigG/14 + CLIP-L/14 拼接</li><li><strong>多尺度 / 多 aspect-ratio 训练</strong>:原生支持 1024×1024 + 不同长宽比</li><li><strong>MicroConditioning</strong>:把原始分辨率、crop offset、aspect ratio 当条件喂给 UNet</li></ul>
|
||
<h3 id="113-dit-peebles-xie-2023-iccv">11.3 DiT (Peebles-Xie 2023 ICCV)</h3>
|
||
<p><strong>把 UNet 换成纯 Transformer</strong>:</p>
|
||
<ul><li>把 latent 切 patch(如 $2 \times 2$)成 token sequence</li><li>标准 Transformer block(self-attn + MLP)</li><li>conditioning 通过 <strong>adaptive LayerNorm (adaLN)</strong> 注入:$\text{LN}(x) \cdot \gamma(c, t) + \beta(c, t)$,$\gamma, \beta$ 来自 $c, t$ 的 MLP</li></ul>
|
||
<p>DiT 实验:scale law 比 UNet 好,FID 随参数量稳定下降。SD3 / FLUX / Sora 都基于 DiT 系。</p>
|
||
<h3 id="114-sd3-esser-2024-icml--diffusion-换成-rectified-flow">11.4 SD3 (Esser 2024 ICML) —— diffusion 换成 Rectified Flow</h3>
|
||
<p>SD3 的两个关键改动:</p>
|
||
<ol><li><strong>Rectified Flow 替代 DDPM</strong>:训练目标变成 $\|v_\theta - (x_1 - x_0)\|^2$(FM 框架)</li><li><strong>MM-DiT</strong>:多模态 DiT,text token 和 image token 在同一 Transformer 里互相 attend(不是 cross-attn)</li></ol>
|
||
<p>为什么换 RF?Esser 2024 ablation:<strong>linear path 的 trajectory 比 cosine path 更直</strong> → 少步采样更好;logit-normal $t$ sampling 让 mid-noise 更被重视,质量提升。</p>
|
||
<h3 id="115-flux1-black-forest-labs-2024">11.5 FLUX.1 (Black Forest Labs 2024)</h3>
|
||
<p>继承 SD3 + MM-DiT,主要更新:</p>
|
||
<ul><li>12B 参数(开源 dev 版)</li><li><strong>Guidance-distilled</strong>:把 CFG 蒸馏进单 forward,推理无需 2× CFG forward</li><li><strong>Adversarial training</strong> 末段微调(类似 SD3-Turbo / ADD),4-step 即出图</li></ul>
|
||
<h3 id="116-controlnet-zhang-2023-iccv">11.6 ControlNet (Zhang 2023 ICCV)</h3>
|
||
<p>给 frozen SD UNet 加 <strong>trainable copy</strong> + <strong>zero-conv</strong> 连接:</p>
|
||
<pre class="diagram"><code>原 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</code></pre>
|
||
<p><strong>Zero-conv = 初始权重为 0 的 1×1 卷积</strong> → 训练初始 ControlNet 不改变原 UNet 输出(保留 SD 能力),随训练逐渐学到 condition 控制。</p>
|
||
<div class="callout callout-good"><div class="callout-title">ControlNet 的训练效率</div><p>frozen 原 UNet(大部分参数),只训 trainable copy(~一半参数),单卡可训,是开源生态的关键。</p></div>
|
||
<h2 id="12-distillation1-step--few-step-生成">§12 Distillation:1-step / Few-step 生成</h2>
|
||
<h3 id="121-progressive-distillation-salimans-ho-2022">12.1 Progressive Distillation (Salimans-Ho 2022)</h3>
|
||
<p>迭代蒸馏:student 一步 $\approx$ teacher 两步,蒸馏 $\log_2 N$ 轮把 $N$ 步压到 1 步。<strong>关键</strong>:每次只压一半,分布漂移可控。</p>
|
||
<h3 id="122-consistency-models-song-2023-icml">12.2 Consistency Models (Song 2023 ICML)</h3>
|
||
<p><strong>思路</strong>:直接学一个网络 $f_\theta(x_t, t)$,使得对<strong>所有 $t$</strong> 都满足:</p>
|
||
<p>$$f_\theta(x_t, t) \approx x_0$$</p>
|
||
<p>即网络是 probability flow ODE 的 <strong>consistency function</strong>——任意 $x_t$ 映到对应的 $x_0$。一步采样:$x_0 = f_\theta(x_T, T)$。</p>
|
||
<p><strong>训练目标</strong>(Consistency Distillation, CD):</p>
|
||
<p>$$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]$$</p>
|
||
<p>其中:</p>
|
||
<ul><li>$\theta^-$ 是 EMA target</li><li>$\hat x_{t_n}$ 由 teacher ODE solver 从 $x_{t_{n+1}}$ 走一步得到($x_{t_n} = \text{ODE-step}(x_{t_{n+1}})$)</li><li>$d$ 是 metric(L2 / LPIPS)</li></ul>
|
||
<p><strong>Boundary condition</strong>:要求 $f_\theta(x_{\sigma_\text{min}}, \sigma_\text{min}) = x_{\sigma_\text{min}}$(在最低噪声处自洽)——用 EDM-style preconditioning 强制:</p>
|
||
<p>$$f_\theta(x, \sigma) = c_\text{skip}(\sigma) x + c_\text{out}(\sigma) F_\theta(x, \sigma)$$</p>
|
||
<p>$c_\text{skip}, c_\text{out}$ 设计让 $\sigma = \sigma_\text{min}$ 时 $f_\theta \equiv x$。</p>
|
||
<div class="callout callout-warn"><div class="callout-title">CT (Consistency Training) vs CD (Consistency Distillation)</div><p>CT 完全 from scratch(不用 teacher,直接对 $x_0 + \sigma_n \epsilon$ 与 $x_0 + \sigma_{n+1} \epsilon$ 做 consistency loss);CD 用 pretrained teacher 蒸馏。质量上 CD > CT;近期 ICT (Song 2024) 让 CT 接近 CD。</p></div>
|
||
<h3 id="123-lcm--lcm-lora-luo-2023">12.3 LCM / LCM-LoRA (Luo 2023)</h3>
|
||
<p><strong>Latent Consistency Model</strong>:把 Consistency Models 套到 latent diffusion(SD 1.5 / SDXL):</p>
|
||
<ul><li>Teacher = pretrained SD(用 DDIM 当 ODE solver)</li><li>Student = LCM,4-8 step 出图</li></ul>
|
||
<p><strong>LCM-LoRA</strong>:把 LCM 训练写成 LoRA adapter——单 LoRA 文件即可让任意 SD 1.5 / SDXL fine-tune 用 4 step 出图。<strong>生态价值巨大</strong>:用户不需要换 base model。</p>
|
||
<h3 id="124-adversarial-diffusion-distillation-add--sdxl-turbo--sd3-turbo-sauer-20232024">12.4 Adversarial Diffusion Distillation (ADD) — SDXL-Turbo / SD3-Turbo (Sauer 2023/2024)</h3>
|
||
<p><strong>ADD 训练目标</strong>:</p>
|
||
<p>$$L_\text{ADD} = L_\text{adv}(\text{student}) + \lambda L_\text{distill}(\text{student}, \text{teacher})$$</p>
|
||
<ul><li>$L_\text{adv}$:用 pretrained vision model(DINOv2)当 discriminator backbone</li><li>$L_\text{distill}$:student 多步 ODE 应该匹配 teacher 多步 ODE</li></ul>
|
||
<p><strong>结果</strong>:SDXL-Turbo 1-step 1024 px、SD3-Turbo 4-step 1024 px。质量略低于 multi-step 但实时(~100ms / image)。</p>
|
||
<h2 id="13-与-flow-matching-的桥">§13 与 Flow Matching 的桥</h2>
|
||
<h3 id="131-score-vs-vector-field--同信息不同参数化">13.1 Score vs Vector Field —— 同信息不同参数化</h3>
|
||
<p>在 VP-SDE / VE-SDE 框架内,FM 学 $v$ 和 score-based 学 $s$ 是 <strong>同信息的两种参数化</strong>:</p>
|
||
<p>$$v_\theta(t, x) = f(x, t) - \tfrac{1}{2} g^2(t)\, s_\theta(t, x)$$</p>
|
||
<p>具体到 VP(DDPM)path,写成 $\alpha_t = \sqrt{\bar\alpha_t}, \sigma_t = \sqrt{1-\bar\alpha_t}$,则 conditional vector field(Salimans-Ho 2022 $v$-prediction 同形式):</p>
|
||
<p>$$v_\theta^\text{VP}(t, x_t) = \alpha_t'\, x_0 + \sigma_t'\, \epsilon$$</p>
|
||
<p>代入 $x_0 = (x_t - \sigma_t \epsilon)/\alpha_t$,整理得到 $v_\theta$ 同时是 $x_t$ 与 $\epsilon$(或 score)的线性组合——具体表达式与 $\alpha_t, \sigma_t$ 的时间导数有关。</p>
|
||
<p><strong>实际上</strong>:对应任意 $\alpha(t), \sigma(t)$ 的 Gaussian path,三个量 $\{\epsilon_\theta, s_\theta, v_\theta\}$ 完全等价。所以训 DDPM、训 score-based、训 FM 在 VP/VE path 上是同一件事。</p>
|
||
<h3 id="132-为什么-sd3--flux-改用-rectified-flow">13.2 为什么 SD3 / FLUX 改用 Rectified Flow?</h3>
|
||
<p><strong>Rectified Flow 的 path</strong>:$x_t = (1-t) x_0 + t x_1$(噪声→数据线性插值),$v_t = x_1 - x_0$。</p>
|
||
<table><thead><tr><th>优势</th><th>RF (linear)</th><th>VP/VE (curved)</th></tr></thead><tbody><tr><td>ODE trajectory</td><td>直线</td><td>曲线(需更高阶 solver)</td></tr><tr><td>Target $v_t$</td><td>不依赖 $t$</td><td>依赖 $t$(VP cosine 路径)</td></tr><tr><td>少步数采样</td><td>Euler 4-8 步可用</td><td>Euler 需 30+ 步</td></tr><tr><td>Reflow 可压到 1-2 步</td><td>✓(InstaFlow / SD3-Turbo)</td><td>✗</td></tr><tr><td>训练稳定性</td><td>logit-normal $t$ + RF 稳</td><td>需精心调 noise schedule</td></tr></tbody></table>
|
||
<div class="callout callout-info"><div class="callout-title">一句话 SD3 ablation 结论</div><p>"在同样的 DiT backbone 下,RF + logit-normal $t$ 比 VP + uniform $t$ 在 ImageNet 256 FID 提升约 0.5-1.0;在 T2I 任务 GenEval 上文本对齐显著更好。"</p></div>
|
||
<h3 id="133-ddpmddimedmrfcm-全图">13.3 DDPM/DDIM/EDM/RF/CM 全图</h3>
|
||
<pre class="diagram"><code> 训练目标 采样方式 典型 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</code></pre>
|
||
<h2 id="14-25-高频面试题l1-必会--l2-进阶--l3-顶级-lab">§14 25 高频面试题(L1 必会 · L2 进阶 · L3 顶级 lab)</h2>
|
||
<h3 id="l1-必会题任何-ml-岗位-diffusion-题目都可能问">L1 必会题(任何 ML 岗位 diffusion 题目都可能问)</h3>
|
||
<details>
|
||
<summary>Q1.写出 DDPM 的 forward $q(x_t | x_0)$ 和 reverse $p_\theta(x_{t-1}|x_t)$。</summary>
|
||
<ul><li>Forward 闭式:$q(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t)I)$,$\bar\alpha_t = \prod_{s=1}^t (1-\beta_s)$</li><li>Reverse 参数化:$p_\theta(x_{t-1}|x_t) = \mathcal{N}(\mu_\theta(x_t, t), \Sigma_\theta)$</li><li>$\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)</li></ul>
|
||
<p>写错符号(如 $\sqrt{\alpha_t}$ 与 $\sqrt{\bar\alpha_t}$ 混淆);忘 $\bar\alpha$ 是累积乘积。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q2.DDPM 的 ELBO 怎么化简成 $L_\text{simple}$?</summary>
|
||
<ul><li>ELBO 拆成 $L_T + \sum L_{t-1} + L_0$,$L_T$ 是常数(prior 匹配)</li><li>$L_{t-1} = \text{KL}(q(x_{t-1}|x_t, x_0) \,\Vert\, p_\theta)$,两者都是 Gaussian,KL 闭式</li><li>把 $x_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon)/\sqrt{\bar\alpha_t}$ 代入 $\tilde\mu$ 和 $\mu_\theta$,得 $L_{t-1} = \text{const} \cdot \mathbb{E}\|\epsilon - \epsilon_\theta\|^2$</li><li>Ho 2020 扔掉系数得 $L_\text{simple} = \mathbb{E}\|\epsilon - \epsilon_\theta\|^2$</li></ul>
|
||
<p>只说"L_simple 是预测 noise" 不会推;或不知道扔系数等价 SNR-weighting。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q3.为什么 $L_\text{simple}$ 扔掉系数还 work?</summary>
|
||
<ul><li>ELBO 的系数 $\beta_t^2 / [2\sigma_t^2 \alpha_t (1-\bar\alpha_t)]$ 在小 $t$(高 SNR)大、在大 $t$(低 SNR)小</li><li>扔系数等价<strong>对低 SNR(大 $t$)权重相对提升</strong>——这些是"决定语义结构"的步骤</li><li>经验:unweighted FID 显著优于 ELBO weighted</li><li>代价:不再是 $\log p$ 的下界(FID ≠ likelihood)</li></ul>
|
||
<p>不知道扔系数的代价是 likelihood vs sample quality 的 trade-off。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q4.linear vs cosine schedule?</summary>
|
||
<ul><li>Linear: $\beta_t \in [10^{-4}, 0.02]$ 线性插值,DDPM 原文</li><li>问题:末端 SNR 不够低 ($\bar\alpha_T \approx 4\times 10^{-5}$);中间区域加噪太快</li><li>Cosine: $\bar\alpha_t = \cos^2(\pi(t/T + s)/(2(1+s)))$, $s=0.008$,末端 SNR ≈ 0</li><li>经验:cosine 在 ImageNet 64 FID 提升约 20%(Nichol-Dhariwal 2021)</li></ul>
|
||
<p>只说"cosine 更好"不会写公式;忘记 $s=0.008$ offset 是为了 $\beta_1$ 不接近 0。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q5.$\epsilon$-pred / $x_0$-pred / $v$-pred / score 怎么互转?</summary>
|
||
<ul><li>已知 $x_t = \sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t} \epsilon$,所有量线性可逆</li><li>$\hat x_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon_\theta) / \sqrt{\bar\alpha_t}$</li><li>$v = \sqrt{\bar\alpha_t}\epsilon - \sqrt{1-\bar\alpha_t} x_0$ (Salimans-Ho 2022)</li><li>$s = -\epsilon / \sqrt{1-\bar\alpha_t}$ (由 Tweedie 或 $\nabla_{x_t} \log q(x_t|x_0)$)</li></ul>
|
||
<p>不知道四种 prediction 是同一信息的不同参数化;混淆 $v$ 和 velocity。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q6.DDIM vs DDPM 区别?</summary>
|
||
<ul><li>DDPM ancestral 是 stochastic Markov chain,每步加噪 $\sigma_t z$,必须走全 $T$ 步</li><li>DDIM 用 non-Markovian forward,<strong>与 DDPM 共享同一 $q(x_t|x_0)$</strong>——可直接用 DDPM 训练权重</li><li>$\eta = 0$ deterministic、可 interpolation;$\eta = 1$ + 走完整 $T$ 步退化为 DDPM ancestral(skip 步时只是方差匹配)</li><li>DDIM 可 skip steps:50 步 ≈ DDPM 1000 步质量</li></ul>
|
||
<p>只说"DDIM 是 DDPM 的少步版",不知道 marginal 等价;或不知道 $\eta$ 控制随机性。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q7.CFG (Classifier-Free Guidance) 怎么训怎么用?</summary>
|
||
<ul><li>训练:以 $p_\text{drop}=0.1$ 概率把 $c$ 替换为 $\emptyset$(null embedding),同一 net 学 conditional/uncond</li><li><p>推理两种 convention(务必区分):</p>
|
||
<ul><li><strong>HF / SD 风格</strong>(记为 $s$):$\tilde\epsilon = \epsilon_\theta(x,\emptyset) + s\,[\epsilon_\theta(x,c) - \epsilon_\theta(x,\emptyset)]$,$s=1$ 即无 guidance,$s\in[3, 7.5]$ 为 SD 常用强度</li><li><strong>Ho-Salimans 2022 原文</strong>(记为 $w$):$\tilde\epsilon = (1+w)\,\epsilon_\theta(x,c) - w\,\epsilon_\theta(x,\emptyset)$,$w=0$ 即无 guidance,等价 $s = w + 1$</li></ul></li><li>$s$ 大 → 文本对齐强 但多样性下降;$s>10$ → 色彩过饱和</li></ul>
|
||
<p>只写公式不知 $w$/$s$ convention;不知道 drop $c$ 的训练 trick;说 CFG 需要单独训 classifier(那是 classifier guidance)。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q8.什么是 Tweedie 公式?为什么重要?</summary>
|
||
<ul><li>$\mathbb{E}[x_0 | x_t] = x_t + \sigma_t^2 \nabla_{x_t} \log p_t(x_t)$(VE 视角;VP 类似有 $\alpha$ 因子)</li><li>推导:对 $p_t(x_t) = \int p_0(x_0) \mathcal{N}(x_t; x_0, \sigma_t^2 I) dx_0$ 求 $\nabla_{x_t}$ log</li><li>意义:<strong>denoiser 最优输出 = 输入 + score 缩放</strong>——所有参数化($\epsilon, x_0, v, s$)之间转换的"罗塞塔石碑"</li></ul>
|
||
<p>只背公式不会推;不知道它把 score 和 denoiser 连起来。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q9.VP-SDE vs VE-SDE?</summary>
|
||
<ul><li><strong>VP</strong> (variance preserving): $dx = -\frac{1}{2}\beta(t) x\, dt + \sqrt{\beta(t)}\, dW$,对应 DDPM;$\text{Var}[x_t] \le 1$</li><li><strong>VE</strong> (variance exploding): $dx = \sqrt{d\sigma^2/dt}\, dW$,对应 SMLD/EDM;$\text{Var}[x_t]$ 增到 $\sigma_\text{max}^2$</li><li>VP 的 $x_T \approx \mathcal{N}(0, I)$;VE 的 $x_T \approx \mathcal{N}(x_0, \sigma_\text{max}^2 I)$,prior 是 $\mathcal{N}(0, \sigma_\text{max}^2 I)$</li><li>EDM 选 VE 因为 preconditioning 推导更干净;DDPM 选 VP 因为 prior $\mathcal{N}(0,I)$ 自然</li></ul>
|
||
<p>只说"variance preserving / exploding" 不会写 SDE;不知 EDM 是 VE。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q10.Probability flow ODE 是什么?</summary>
|
||
<ul><li>对任意 forward SDE $dx = f dt + g dW$,存在 deterministic ODE $dx/dt = f - \frac{1}{2}g^2 \nabla \log p_t$,<strong>共享所有时刻边缘 $p_t$</strong></li><li>注意 reverse SDE 的 drift 是 $f - g^2 \nabla \log p_t$(<strong>整个</strong> score correction),PF-ODE 只用 $\frac{1}{2} g^2$;<strong>不是简单地"reverse SDE 去掉随机项"</strong></li><li>实际意义:<strong>可用 ODE solver(DDIM, Heun, RK4, DPM-Solver)少步数采样</strong></li><li>是 score-based 与 Flow Matching 之间的桥:$v_t = f - \frac{1}{2}g^2 s$</li></ul>
|
||
<p>只知道公式不知道 PF-ODE 与 reverse SDE 的 drift 系数差一半;不知道它让 deterministic 采样成为可能。</p>
|
||
</details>
|
||
<h3 id="l2-进阶题research-oriented--需熟悉-diffusion-细节">L2 进阶题(research-oriented · 需熟悉 diffusion 细节)</h3>
|
||
<details>
|
||
<summary>Q11.EDM preconditioning 的 unit-variance argument 是什么?</summary>
|
||
<ul><li>让网络 $F_\theta$ 输入 $c_\text{in} x$ 方差为 1:$c_\text{in} = 1/\sqrt{\sigma_\text{data}^2 + \sigma^2}$</li><li>让 effective target $F^* = (D^* - c_\text{skip} x)/c_\text{out}$ 方差为 1:$c_\text{skip} = \sigma_\text{data}^2/(\sigma^2+\sigma_\text{data}^2)$, $c_\text{out} = \sigma \sigma_\text{data} / \sqrt{\sigma^2 + \sigma_\text{data}^2}$</li><li>直觉:$\sigma \to 0$ 时 $c_\text{skip} \to 1$(identity),$\sigma \to \infty$ 时 $c_\text{out} \to \sigma_\text{data}$(all from net)</li><li>作用:所有 $\sigma$ 上 loss 数值范围一致,训练更稳</li></ul>
|
||
<p>只背公式不知道为什么;不知道 $\sigma_\text{data}$ 是数据 std(约 0.5 for normalized images)。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q12.Improved DDPM 学 $\Sigma_\theta$ 的好处?</summary>
|
||
<ul><li>DDPM 固定 $\Sigma_\theta = \beta_t I$ 或 $\tilde\beta_t I$</li><li>Nichol-Dhariwal 2021 学 $\Sigma_\theta$ 在 $[\beta_t, \tilde\beta_t]$ 之间插值:$\Sigma_\theta = \exp(v \log\beta_t + (1-v) \log\tilde\beta_t)$</li><li>好处:<strong>少步采样质量大幅提升</strong>(50 步达到 1000 步 fixed-$\Sigma$ 水平)</li><li>Hybrid loss $L_\text{hybrid} = L_\text{simple} + 0.001 \cdot L_\text{vlb}$($L_\text{vlb}$ 提供 $\Sigma_\theta$ 学习信号)</li><li>$\lambda = 0.001$ 防 $L_\text{vlb}$ 主导</li></ul>
|
||
<p>不知道 hybrid loss;以为 $\Sigma_\theta$ 学习对训练 likelihood 影响最大(实际是少步采样涨点)。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q13.DPM-Solver vs DDIM 的核心区别?</summary>
|
||
<ul><li>DDIM 是一阶 Euler,每步 1 NFE</li><li>DPM-Solver 利用 diffusion ODE 的<strong>半线性</strong>结构 $dx/dt = f(t) x + g(t) \epsilon_\theta$,对线性部分<strong>精确积分</strong>(exponential integrator)</li><li>把非线性部分($\epsilon_\theta$)在 log-SNR $\lambda$ 上做 $k$-阶 Taylor 展开</li><li>DPM-Solver-2 每步 2 NFE,二阶;DPM-Solver-3 每步 3 NFE,三阶</li><li>10-15 NFE 达到 DDIM 50 NFE 质量</li><li>DPM-Solver++ 改用 $x_0$-pred,CFG 友好</li></ul>
|
||
<p>不知道 exponential integrator;以为 DPM-Solver 是某种近似(实际是数学上更精的展开)。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q14.Consistency Models 训练目标?怎么做到 1-step?</summary>
|
||
<ul><li>目标:$f_\theta(x_t, t) \approx x_0$ 对所有 $t$</li><li>Consistency loss:$d(f_\theta(x_{t_{n+1}}, t_{n+1}), f_{\theta^-}(\hat x_{t_n}, t_n))$,$\hat x_{t_n}$ 由 teacher ODE 一步得到</li><li>$\theta^-$ 是 EMA,类似 BYOL;用 metric $d$ = L2 + LPIPS</li><li>Boundary:$f_\theta(x, \sigma_\text{min}) \equiv x$,用 EDM-style $c_\text{skip}, c_\text{out}$ 强制</li><li>1-step 采样:$x_0 = f_\theta(x_T, T)$</li><li>2-step 进阶:先 $x_0 = f_\theta(x_T, T)$,再加噪到中间 $t$、再 $f_\theta$</li></ul>
|
||
<p>只说"学映射 $x_t \to x_0$"不知道 consistency 约束怎么定义;不知道 EMA target / teacher ODE / boundary。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q15.SD3 为什么从 DDPM 换成 Rectified Flow?</summary>
|
||
<ul><li>RF path $x_t = (1-t)x_0 + tx_1$ 是直线 → ODE trajectory 直 → 少步采样误差小</li><li>$v_t = x_1 - x_0$ target 不依赖 $t$(给定 $(x_0, x_1)$),数值稳定</li><li>配合 <strong>logit-normal $t$ sampling</strong>(集中在 $t=0.5$)涨点</li><li>Esser 2024 ablation:同 backbone 下 RF + LogitNorm vs VP-cosine + Uniform,GenEval 文本对齐显著好</li><li>进一步可 reflow 压到 4-step(FLUX-Schnell / SD3-Turbo)</li></ul>
|
||
<p>只说"RF 更稳"不知道是因为 path 直;不知道 logit-normal 是额外 trick。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q16.DiT 怎么注入 condition?adaLN vs cross-attn?</summary>
|
||
<ul><li><strong>adaLN-Zero</strong>(DiT 默认):把 $c, t$ 经 MLP 输出 $\gamma, \beta, \alpha$,$\text{out} = \alpha \cdot \text{block}(\text{LN}(x) \cdot \gamma + \beta) + x$;初始化 $\alpha=0$(zero-init),train 初始 DiT block 不改变输入</li><li><strong>Cross-attn</strong>:image tokens 作 Q,text/condition 作 K/V</li><li><strong>Token-concat (MM-DiT, SD3)</strong>:text tokens 和 image tokens 拼成单一序列,所有 token 互相 attend</li><li>经验:adaLN-Zero scale 性最好(DiT 论文);cross-attn 文本控制力强(SD UNet);MM-DiT 综合最佳(SD3 / FLUX)</li></ul>
|
||
<p>只知道 cross-attn;不知 adaLN-Zero 的"zero-init" 是关键 trick。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q17.ControlNet 的 zero-conv 是什么?为什么必要?</summary>
|
||
<ul><li>1×1 conv,<strong>weight 初始化为 0</strong>,bias 也为 0</li><li>训练初始时 trainable copy 的输出经 zero-conv → 0,原 UNet 输出不变 → <strong>保留 SD pretrained 能力</strong></li><li>随训练 zero-conv 学到非零权重,逐渐注入 condition 控制</li><li>为什么不能直接 random init:random init 会扰动 frozen UNet 的中间特征,破坏 pretrained representation</li></ul>
|
||
<p>只说"加 controlnet 模块"不知道 zero-conv;以为 zero-conv 是 1×1 卷积的特殊变体(其实只是初始化)。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q18.SDE vs ODE 采样的 trade-off?</summary>
|
||
<ul><li><strong>SDE</strong>:reverse SDE 含随机项 $g(t) d\bar W$;每步注入新噪声,<strong>能修正早期错误</strong></li><li><strong>ODE</strong> (probability flow):deterministic;solver 误差累积无回头路</li><li>SDE 通常 FID 更好;ODE NFE 少 + deterministic(可 interpolation)</li><li>EDM 折中:基础 ODE Heun + 少量 stochastic churn(每步前小幅加噪),FID 比 pure ODE 好 0.1-0.3</li></ul>
|
||
<p>只说"SDE 是 stochastic, ODE 是 deterministic" 不知道 trade-off;不知 EDM churn。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q19.LCM vs SDXL-Turbo 的区别?</summary>
|
||
<ul><li><strong>LCM</strong>:Consistency Distillation 套到 latent diffusion,4-8 step;纯 distillation loss</li><li><strong>LCM-LoRA</strong>:把 LCM 训练写成 LoRA adapter,适配任意 SD 1.5 / SDXL fine-tune</li><li><strong>SDXL-Turbo (ADD)</strong>:adversarial loss + distill loss,1-4 step;用 DINOv2 当 discriminator</li><li>LCM 偏稳,ADD 偏锐利(adversarial 让纹理更清晰)</li><li>LCM 开源更早,生态更全;Turbo 需要 BFL/SAI 自家训练</li></ul>
|
||
<p>不知道 LCM-LoRA 的"LoRA 适配性"是杀手锏;以为 Turbo = LCM。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q20.训练 noise level $\sigma$ 怎么采样?</summary>
|
||
<ul><li>DDPM: $t \sim \mathcal{U}\{1, \dots, T\}$,离散均匀</li><li>EDM: $\ln \sigma \sim \mathcal{N}(P_\text{mean}, P_\text{std}^2)$,$P_\text{mean}=-1.2, P_\text{std}=1.2$,集中在 $\sigma \approx 0.3$</li><li>SD3 / RF: $t = \text{sigmoid}(\tau), \tau \sim \mathcal{N}(0, 1)$,集中在 $t = 0.5$</li><li>共同 idea:<strong>mid-noise 最难学</strong>,多采样 mid 区域涨点</li></ul>
|
||
<p>只说"uniform 采样"不知道 EDM/SD3 都改成 normal/logitnormal;不知道为什么集中在 mid。</p>
|
||
</details>
|
||
<h3 id="l3-顶级-diffusion--视频生成方向深度推导--蒸馏--production-整合">L3 顶级 diffusion / 视频生成方向(深度推导 + 蒸馏 + Production 整合)</h3>
|
||
<details>
|
||
<summary>Q21.从 ELBO 推 $L_\text{simple} = \|\epsilon - \epsilon_\theta\|^2$,列出所有中间近似与"扔掉"的项。</summary>
|
||
<p><strong>推导链 + 近似清单</strong>:</p>
|
||
<ul><li><strong>Step 1</strong>(<strong>精确</strong>,无近似):$\log p_\theta(x_0) \ge \mathbb{E}_q[\log p_\theta(x_{0:T})/q(x_{1:T}|x_0)]$ —— Jensen 不等式给出变分下界</li><li><strong>Step 2</strong>($L_T$ <strong>被当作常数忽略</strong>):ELBO 拆 $L = L_T + \sum_{t=2}^T L_{t-1} + L_0$。$L_T = \text{KL}(q(x_T|x_0)\,\lVert\, p(x_T))$ —— 实际不严格为 0,但 $\bar\alpha_T \approx 0$ 时近似常数</li><li><strong>Step 3</strong>($L_0$ <strong>被忽略 / 合并</strong>):$L_0 = -\mathbb{E}[\log p_\theta(x_0 | x_1)]$ —— small contribution;常用 discretized Gaussian decoder 显式建模,训练时常被合并到 $L_1$</li><li><strong>Step 4</strong>(<strong>KL 闭式,$\Sigma_\theta$ 固定时常数 $C$ 被忽略</strong>):$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 闭式。如果 $\Sigma_\theta = \sigma_t^2 I$ 固定:</li></ul>
|
||
<p>$$L_{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$$</p>
|
||
<p>常数 $C$ 来自 $\Sigma$ 项的 log-determinant,<strong>$\Sigma$ 固定时与 $\theta$ 无关,求梯度时消失</strong>。</p>
|
||
<ul><li><strong>Step 5</strong>(<strong>精确</strong>重写为 $\epsilon$-pred 形式):把 $x_0 = (x_t - \sqrt{1-\bar\alpha_t}\epsilon)/\sqrt{\bar\alpha_t}$ 代入 $\tilde\mu_t$ 和 $\mu_\theta$ 都用 $\epsilon$-pred parameterization:</li></ul>
|
||
<p>$$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]$$</p>
|
||
<p>精确,只要 $\mu_\theta$ 用 Ho 2020 的 $\epsilon$-pred 形式。</p>
|
||
<ul><li><strong>Step 6</strong>(<strong>扔掉 $t$ 依赖系数</strong>):$L_\text{simple}$ 把系数 $\frac{\beta_t^2}{2\sigma_t^2 \alpha_t (1-\bar\alpha_t)}$ 统一取 1。等价<strong>对不同 $t$ 重新加权</strong>——在小 $t$(高 SNR)原系数大 → simple 相对降低权重;在大 $t$(低 SNR)原系数小 → simple 相对提升权重。</li><li><strong>Step 7</strong>(<strong>$t$ 改均匀采样</strong>):离散 $t$ 改成 $t \sim \mathcal{U}\{1,\dots,T\}$,均匀采样所有时间步,不是按 ELBO 各项的权重。</li></ul>
|
||
<p><strong>最终</strong>:</p>
|
||
<p>$$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]$$</p>
|
||
<p><strong>代价</strong>:</p>
|
||
<ul><li>不再是 $\log p$ 的下界(FID 涨但 likelihood 评估不再直接对应)</li><li>$L_T$ 和 $L_0$ 被默认忽略</li><li>$\Sigma_\theta$ 信息被丢(Improved DDPM 用 $L_\text{vlb}$ 补回)</li></ul>
|
||
<p>不知道哪些项被丢;以为 $L_\text{simple}$ 直接从 KL 推出来;忽略 $L_T, L_0$ 的角色。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q22.证明 DDIM ($\eta=0$) 与 DDPM 共享同一 marginal $q(x_t|x_0)$。</summary>
|
||
<p><strong>Statement</strong>:DDIM 定义 non-Markov forward $q_\sigma(x_{1:T}|x_0)$,使得 $q_\sigma(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I)$ —— 与 DDPM 完全一致。</p>
|
||
<p><strong>证明</strong>(归纳):</p>
|
||
<ul><li>边界 $q_\sigma(x_T|x_0) = \mathcal{N}(\sqrt{\bar\alpha_T} x_0, (1-\bar\alpha_T) I)$ —— 由 DDIM 定义直接成立</li><li>假设 $q_\sigma(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t} x_0, (1-\bar\alpha_t) I)$。DDIM 定义:</li></ul>
|
||
<p>$$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)$$</p>
|
||
<ul><li>求 $q_\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 的边缘化)</li><li>用 Gaussian 边缘化定理:若 $x_t | x_0 \sim \mathcal{N}(\mu_t, \Sigma_t)$ 且 $x_{t-1}|x_t, x_0 \sim \mathcal{N}(A x_t + b, \Sigma_{t-1|t})$,则:</li></ul>
|
||
<p>$$x_{t-1}|x_0 \sim \mathcal{N}\!\left(A \mu_t + b,\; A \Sigma_t A^\top + \Sigma_{t-1|t}\right)$$</p>
|
||
<ul><li><p>这里 $A = \sqrt{1-\bar\alpha_{t-1}-\sigma_t^2}/\sqrt{1-\bar\alpha_t}$,$b = \sqrt{\bar\alpha_{t-1}} x_0 - A \sqrt{\bar\alpha_t} x_0$。代入:</p>
|
||
<ul><li>均值 = $\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$</li><li>方差 = $A^2 (1-\bar\alpha_t) + \sigma_t^2 = (1-\bar\alpha_{t-1}-\sigma_t^2) + \sigma_t^2 = 1 - \bar\alpha_{t-1}$</li></ul></li><li>所以 $q_\sigma(x_{t-1}|x_0) = \mathcal{N}(\sqrt{\bar\alpha_{t-1}} x_0, (1-\bar\alpha_{t-1}) I)$ —— <strong>与 DDPM 完全一致</strong> $\square$</li></ul>
|
||
<p><strong>意义</strong>:DDIM 可以<strong>直接用 DDPM 训出来的 $\epsilon_\theta$</strong>,因为训练只看 marginal $q(x_t|x_0)$,而 marginal 一致;但采样路径不同(deterministic vs stochastic)。</p>
|
||
<p>不会写 Gaussian 边缘化定理;不知道证明的关键是 $A^2(1-\bar\alpha_t) + \sigma_t^2 = 1-\bar\alpha_{t-1}$。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q23.推导 EDM preconditioning 中的 $c_\text{skip}$ 与 $c_\text{out}$。</summary>
|
||
<p><strong>Setup</strong>:VE 视角 $x = x_0 + \sigma \epsilon$,$\epsilon \sim \mathcal{N}(0, I)$,$\text{Var}[x_0] = \sigma_\text{data}^2$。Denoiser 参数化:</p>
|
||
<p>$$D_\theta(x; \sigma) = c_\text{skip}(\sigma) x + c_\text{out}(\sigma) F_\theta(c_\text{in} x, c_\text{noise})$$</p>
|
||
<p><strong>Effective target for $F_\theta$</strong>:</p>
|
||
<p>$$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]$$</p>
|
||
<p><strong>目标</strong>:找 $c_\text{skip}, c_\text{out}$ 让 $\text{Var}[F^*]$(对 $x_0, \epsilon$ 取期望)= 1。</p>
|
||
<p>$$\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$$</p>
|
||
<p>但单纯归一化有多解。<strong>第二准则</strong>(Karras 2022):让 $F_\theta$ 学的"残差"最小(让 $c_\text{out}$ 最小,因为 $c_\text{out}$ 越大 $F$ 越被 amplify、误差也被 amplify)。等价求:</p>
|
||
<p>$$\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$$</p>
|
||
<p>对 $c_\text{skip}$ 求导 = 0:</p>
|
||
<p>$$-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}$$</p>
|
||
<p>代回 $\text{Var}[F^*] = 1$ 约束:</p>
|
||
<p>$$c_\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}$$</p>
|
||
<p>$$\boxed{\; c_\text{out}(\sigma) = \frac{\sigma \cdot \sigma_\text{data}}{\sqrt{\sigma^2 + \sigma_\text{data}^2}} \;}$$</p>
|
||
<p><strong>输入归一化</strong>:$c_\text{in}(\sigma) = 1/\sqrt{\sigma_\text{data}^2 + \sigma^2}$ 让 $\text{Var}[c_\text{in} x] = 1$。</p>
|
||
<p><strong>结论</strong>:四个 $c$ 函数完全由 $\sigma_\text{data}$ 决定,无可调参数(实际工程上 $\sigma_\text{data}$ 由数据计算,对 normalized images 约 0.5)。</p>
|
||
<p>只背公式不会推导;不知道 $c_\text{skip}$ 是最小化 $c_\text{out}$ 推出来的;以为 $c$ 函数有 free parameter。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q24.Consistency Distillation 的训练流程?为什么需要 EMA target $\theta^-$?</summary>
|
||
<p><strong>流程</strong>:</p>
|
||
<ol><li>取 pretrained teacher diffusion $\epsilon_\phi$ + 其 PF-ODE solver(如 EDM Heun)</li><li>取 noise schedule $\sigma_1 > \sigma_2 > \dots > \sigma_N = \sigma_\text{min}$(典型 $N = 18$)</li><li>训练 student $f_\theta(x_\sigma, \sigma) \to x_0$,初始化 $\theta = \phi$ (warm start)</li><li><p>每个 batch:</p>
|
||
<ul><li>采样 $x_0$,$\sigma_n$(uniformly $n \in \{1, \dots, N-1\}$)</li><li>加噪:$x_{\sigma_{n+1}} = x_0 + \sigma_{n+1} \epsilon$</li><li><strong>Teacher ODE solve 一步</strong>:从 $x_{\sigma_{n+1}}$ 用 teacher $\epsilon_\phi$ 做 Heun step 到 $\hat x_{\sigma_n}$</li><li>Loss: $d(f_\theta(x_{\sigma_{n+1}}, \sigma_{n+1}), f_{\theta^-}(\hat x_{\sigma_n}, \sigma_n))$</li></ul></li><li>更新 $\theta$,EMA 更新 $\theta^- \leftarrow \mu \theta^- + (1-\mu)\theta$</li></ol>
|
||
<p><strong>为什么需要 EMA target?</strong></p>
|
||
<ul><li>直接用 $\theta = \theta^-$ 会有 trivial solution:$f_\theta \equiv \text{const}$ 也满足 consistency</li><li>EMA $\theta^-$ 比 $\theta$ 滞后,提供"stable" target,避免 student 跟着自己变</li><li>类似 BYOL / MoCo 的 self-supervised setup</li><li>$\mu = 0.999 \sim 0.99995$(与训练步数相关)</li></ul>
|
||
<p><strong>最近改进 (iCT, Song-Dhariwal 2024)</strong>:<strong>移除 EMA teacher</strong>(直接用同一 $\theta$ 算 target,不再保留 $\theta^-$),改用 pseudo-Huber loss,配合 lognormal noise schedule + curriculum 增加 discretization step 数,CT 接近 CD 质量。</p>
|
||
<p>不知道 trivial solution;以为 EMA 只是工程稳定 trick;不知道 teacher 是干啥的。</p>
|
||
</details>
|
||
<details>
|
||
<summary>Q25.SD3 / FLUX 这条线为什么能压到 4-step / 1-step 出图?</summary>
|
||
<p><strong>核心路径</strong>:<strong>RF (linear path) + Reflow + Distill</strong>。逐步拆:</p>
|
||
<ol><li><strong>RF 让 trajectory 直</strong> —— $x_t = (1-t)x_0 + tx_1$,ODE 解的"理想曲线"就是直线(线性插值),Euler 一阶在长 step 下误差小(与 cosine path 在 mid-$t$ 处曲率大形成对比)</li><li><strong>Reflow 让 trajectory 更直</strong> —— 第一次训完拿 ODE 跑出 coupled $(x_0, x_1)$,再训一次,trajectory 收敛到更接近直线。Liu 2022 证明 reflow 单调降低 transport cost</li><li><strong>CFG-distillation</strong> —— 把 CFG 的 2× forward(cond + uncond)蒸馏成单 forward(FLUX 做了这步);NFE 折半</li><li><strong>Adversarial distillation (ADD)</strong> —— SD3-Turbo / SDXL-Turbo 末段用 DINOv2 discriminator + distill loss,4-step 接近 30-step 质量</li></ol>
|
||
<p><strong>对比 DDPM 路线</strong>:DDPM trajectory 在 mid-$t$ 曲率大(cosine path),Euler 一阶在 5 步以下不可用;要 DPM-Solver-2 二阶 + consistency distillation 才能压到 4 step。<strong>RF 是工程友好的多</strong>——一阶 sampler 就够。</p>
|
||
<p><strong>FLUX-Schnell 的 1-step</strong>:RF + reflow + heavy distillation;1024px 单 forward 就出图,~100ms/image。代价:可控性 / 多样性略降;prompt 跟随精度略低于 multi-step。</p>
|
||
<p>只说"RF 比 DDPM 快"不知道为什么;不知道 reflow + distill 是双管齐下;以为 FLUX 1-step 只是因为 RF(实际还有 distillation)。</p>
|
||
</details>
|
||
<h2 id="a-附录核心-pytorch-代码from-scratch">§A 附录:核心 PyTorch 代码(from scratch)</h2>
|
||
<div class="callout callout-warn"><div class="callout-title">教学版</div><p>重点演示数学;生产用 <code>diffusers</code> / <code>EDM</code> 官方实现,含 mixed precision / EMA / DDP / VAE / xformers / fused kernels。</p></div>
|
||
<h3 id="a1-ddpm-forward-qxt--x0--simplified-loss">A.1 DDPM forward $q(x_t | x_0)$ + simplified loss</h3>
|
||
<pre><code class="language-python">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)</code></pre>
|
||
<h3 id="a2-ddpm-ancestral-sampling">A.2 DDPM ancestral sampling</h3>
|
||
<pre><code class="language-python">@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</code></pre>
|
||
<h3 id="a3-ddim-sampling-with-eta">A.3 DDIM sampling (with $\eta$)</h3>
|
||
<pre><code class="language-python">@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</code></pre>
|
||
<h3 id="a4-classifier-free-guidance-训练--采样">A.4 Classifier-Free Guidance 训练 + 采样</h3>
|
||
<pre><code class="language-python">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</code></pre>
|
||
<h3 id="a5-edm-preconditioning--heun-二阶-sampler">A.5 EDM preconditioning + Heun 二阶 sampler</h3>
|
||
<pre><code class="language-python">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</code></pre>
|
||
<h3 id="a6-probability-flow-ode-简单-euler-求解">A.6 Probability Flow ODE 简单 Euler 求解</h3>
|
||
<pre><code class="language-python">@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</code></pre>
|
||
<h3 id="a7-sanity-check-输出教学版">A.7 Sanity-check 输出(教学版)</h3>
|
||
<p>跑 64×64 ImageNet subset toy 设置,2 层 UNet baseline,sched=cosine, T=1000:</p>
|
||
<pre><code>[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 一致 ✓</code></pre>
|
||
<p>主要参考: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).</p>
|
||
<p><strong>Diffusion Foundations Cheat Sheet</strong> · 公式 + From-Scratch 代码 + 25 高频题(L1 必会 · L2 进阶 · L3 顶级 lab)</p>
|
||
|
||
<footer class="aris-footer">
|
||
Generated by <a href="https://github.com/wanshuiyin/Auto-claude-code-research-in-sleep/blob/main/skills/render-html/SKILL.md">ARIS <code>/render-html</code></a> ·
|
||
source path <code>docs/tutorials/diffusion_foundations_tutorial.md</code> ·
|
||
SHA256 <code>95c1efc6f929</code> ·
|
||
generated at 2026-05-19 05:40 UTC.
|
||
This is a generated view — edit the source Markdown, then re-render.
|
||
</footer>
|
||
</main>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|