
1. 扩散模型到底在解决什么问题老实说我刚接触扩散模型那会儿也一度被DDPM、DDIM、SDE、LDM这一堆缩写绕晕。后来搞明白了其实它们是在讲同一条主线怎么把一堆纯噪声一步步变成一张清晰的图、一段自然的声音或者一个合理的分子结构。生成模型的老问题本质上是从一个“好算的分布”映射到一个“复杂到没法直接写公式的数据分布”。以前GAN靠生成器和判别器打擂台VAE靠重参数化逼近隐空间而扩散模型的思路截然不同我不一下子从噪声变图像而是把“从数据变噪声”的过程设计成很多小步然后再学一个反向过程一步一个小步地“倒放”最终把噪声还原成数据。这个思路最大的好处是训练稳定不像GAN那样容易模式崩溃也不需要花大力气设计复杂的对抗损失。这一篇我打算沿着“DDPM → DDIM → Score-Based/SDE → LDM → Guidance → Rectified Flow”这条线把扩散模型家族的核心知识整体捋一遍。不管你是刚入门、想复现训练脚本的研究生还是想在工程里接API、改采样器、做产品应用的同学只要你认真跟下来应该都能建立起一套完整的认知框架后面再看各种新论文、新工具就会轻松很多。2. DDPM扩散模型的基石2.1 前向过程给数据“慢慢掺毒”DDPM的全称叫Denoising Diffusion Probabilistic Models中文常翻译成去噪扩散概率模型。它是2020年Ho等人提出的虽然之前也有扩散模型的想法但DDPM在图像生成质量上第一次做到了能和GAN掰手腕也让整个领域重新燃起来了。先看前向过程。假设我们有一张真实图像$x_0$它的分布是$q(x_0)$。前向过程做的事情是按照一个提前设计好的噪声调度表往图像里逐步加高斯噪声每一步都只加一点点。如果一共加$T$步每一步的方差由参数$\beta_t$控制那么前向过程可以写成$$q(x_t | x_{t-1}) \mathcal{N}(x_t; \sqrt{1-\beta_t}, x_{t-1}, \beta_t \mathbf{I})$$这里$\beta_t$通常从$10^{-4}$慢慢增加到$0.02$也就是说越到后面每一步加的噪声越大。当$T$足够大比如DDPM默认取$T1000$到最后一张图$x_T$就基本完全变成了各向同性的高斯噪声。这里有一个非常妙的性质因为每一步都是独立的高斯噪声所以我们可以不通过一步一步迭代直接算出任意第$t$步的$x_t$。把每一步累乘之后可以定义$\alpha_t 1 - \beta_t$$\bar{\alpha}t \prod{s1}^t \alpha_s$那么$$q(x_t | x_0) \mathcal{N}(x_t; \sqrt{\bar{\alpha}_t}, x_0, (1 - \bar{\alpha}_t) \mathbf{I})$$写作重参数化的形式就是$x_t \sqrt{\bar{\alpha}_t}, x_0 \sqrt{1 - \bar{\alpha}_t}, \epsilon$其中$\epsilon \sim \mathcal{N}(0, \mathbf{I})$。这个公式非常重要因为训练过程根本不需要真的模拟1000步只需要随便抽一个$t$按公式直接生成带噪样本就行。2.2 反向过程学一条“解毒”路径正向过程是在破坏信息反向过程就要学一条“解毒”路径。因为每步加的噪声都很小所以反向的每一步也可以用高斯分布来近似。我们想学一个参数化模型$p_\theta(x_{t-1} | x_t)$它同样是一个高斯分布均值和方差都由神经网络来预测。DDPM最常用的做法是让网络预测噪声$\epsilon_\theta(x_t, t)$然后根据$x_t$和预测的噪声反推出上一步的均值。在训练时DDPM优化的是变分下界但经过一系列推导后损失函数被简化成了非常直观的形式随机采样时间步$t$、采样真实噪声$\epsilon$然后让网络预测的噪声和真实噪声尽可能接近$$\mathcal{L} \mathbb{E}{t, x_0, \epsilon} \left[ |\epsilon - \epsilon\theta(x_t, t)|^2 \right]$$简化后的损失其实就是个简单的MSE。我第一次看到这个推导时也挺惊讶整个理论绕了半天最后落地竟然就是一个回归问题。但这正是扩散模型最大的优势目标函数非常简单稳定不涉及对抗训练几乎不会出现训练不收敛那种让人头疼的问题。网络结构方面经典DDPM用的是带时间嵌入的U-Net。U-Net的下采样和上采样结构天然适合图像这种局部相关数据而时间步$t$则会通过正弦位置编码变成向量再用FiLM、加性嵌入等方式注入到网络的不同层让网络知道现在“掺毒掺到了什么程度”。2.3 训练与采样的实操细节如果用一个现代深度学习框架来实现DDPM的训练循环核心代码会非常短。下面这段PyTorch风格的伪代码基本就是DDPM训练的骨架for step, (x0, _) in enumerate(train_loader): x0 x0.to(device) t torch.randint(0, T, (x0.size(0),), devicedevice).long() noise torch.randn_like(x0) x_t sqrt_alpha_bar[t].view(-1, 1, 1, 1) * x0 \ sqrt_one_minus_alpha_bar[t].view(-1, 1, 1, 1) * noise pred_noise model(x_t, t) loss F.mse_loss(pred_noise, noise) optimizer.zero_grad() loss.backward() optimizer.step()采样则是在正向过程“倒放”一遍。从纯噪声$x_T \sim \mathcal{N}(0, \mathbf{I})$开始根据模型预测的噪声反推$p_\theta(x_{t-1} | x_t)$再从中采样得到下一步。这个过程要循环$T$次也就是默认要跑1000步。代码大致是这样x torch.randn(batch_size, 3, H, W, devicedevice) for t in reversed(range(T)): t_tensor torch.full((batch_size,), t, devicedevice, dtypetorch.long) eps_pred model(x, t_tensor) x denoise_step(x, t, eps_pred)DDPM效果很好但“慢”这个问题立刻暴露出来。1000步迭代每步都要过一次U-Net在GPU上生成一张64x64的图都要好几秒更别提高分辨率或者视频了。后来几乎所有改进方向都绕不开“能不能少跑几步”。DDIM就是第一个让我印象深刻的加速方案。3. DDIM把采样步数打下来3.1 为什么DDPM慢DDPM慢的根本原因是它假设反向过程也是马尔可夫链每一步$p_\theta(x_{t-1} | x_t)$都只依赖上一步$x_t$没法跳过中间状态。因为每步只能去除一点点噪声如果跳的步子太大高斯近似的误差就会变大最后模型会崩。这就好比把一个房间里的灰尘一粒一粒清理正常情况下一粒粒捡确实干净但代价是慢得离谱。DDIM换了个思路别一粒粒捡了用吸尘器吸只要路径设计得合理跳着清也能达到差不多的效果。3.2 DDIM的非马尔可夫采样DDIM的全称是Denoising Diffusion Implicit Models它的核心贡献是在训练目标不变的前提下设计了一类非马尔可夫的前向过程使得采样时可以从任意一个更小的子序列${t_1, t_2, \dots, t_S}$上做反演而不是必须走满$T$步。DDIM的采样公式长这样$$x_{t-1} \sqrt{\bar{\alpha}{t-1}} \left( \frac{x_t - \sqrt{1-\bar{\alpha}t}, \epsilon\theta(x_t, t)}{\sqrt{\bar{\alpha}t}} \right) \sqrt{1-\bar{\alpha}{t-1} - \sigma_t^2}, \epsilon\theta(x_t, t) \sigma_t \epsilon$$这个式子看着复杂实际含义其实可以拆成三部分第一项是根据预测噪声“去噪”得到的$x_0$估计第二项是沿着噪声预测方向上没有噪完的部分第三项是额外的随机噪声项由$\sigma_t$控制。当$\sigma_t 0$时采样过程有随机性当$\sigma_t 0$时整个过程就变成一个确定性映射同一份初始噪声最终会生成同一样本。3.3 η参数与确定性采样DDIM论文里用$\eta$来统一控制随机性的强度。$\eta 1$时采样过程退化成类似DDPM的完整马尔可夫链$\eta 0$时就是完全确定性的采样。实际使用中$\eta 0$往往已经足够好有时候甚至比带随机性的版本更稳定因为少了一层采样噪声。我在实际测试里DDIM用50步生成的效果已经很接近DDPM用1000步的效果速度提高将近20倍。后来用20步也能出不错的结果但能明显感觉到细节层次下降一些。如果你的需求是快速出图、批量生成DDIM绝对是我第一个推荐试的方案。这里顺手整理一下DDPM和DDIM的核心差异技术选型时可以直接参考对比维度DDPMDDIM过程假设马尔可夫链每步只依赖前一步非马尔可夫采样可以跨步跳跃采样步数通常需要1000步通常50步甚至更少随机性采样必带随机噪声通过$\eta$控制支持确定性生成训练权重原始模型可直接复用DDPM权重主要用途理论基准、高质量生成快速出图、可控生成提示如果你只是想快速给模型配一个采样器建议先固定步数为50比较DDIM、DPM-Solver和Rectified Flow的输出差异而不是无脑往上加步数。固定步数下某些采样器在中等步数会出现过度平滑或伪影加重的问题这是不同离散化方式导致的正常现象。4. Score-Based与SDE统一视角4.1 Score函数与朗之万采样在DDPM这条线之外其实还有一条Score-Based Model路线。Song Yang等人的工作把这两条线统一了起来这也是后续很多加速采样、高质量生成算法的理论基础。Score-Based模型的核心概念是score函数也就是对数概率密度的梯度$\nabla_x \log p(x)$。简单说它告诉我们在某个点上为了让样本更“像数据”应该往哪个方向调整。如果我们能学到这个score函数就可以用朗之万动力学做采样从任意初始点出发沿着score方向不断更新加一点随机噪声最终收敛到数据分布。但问题在于真实数据的$p(x)$我们是不知道的也没有办法直接算$\nabla_x \log p(x)$。而且在高维空间里数据都集中在低维流形附近score估计很容易在数据稀疏的区域失效。为了解决这个问题就必须在整个数据周围都“铺上”不同尺度的噪声这样模型才有足够的梯度信号可学。这就是Score-SDE里“多尺度扰动”思想的来源。4.2 把扩散写成SDESong Yang他们发现无论是DDPM的噪声扰动还是Score-Based模型里的多尺度噪声其实都可以统一写成随机微分方程SDE的形式。前向过程可以写成$$dx f(x, t), dt g(t), dw$$其中$f(x,t)$是漂移项$g(t)$是扩散系数$w$是标准维纳过程。DDPM对应的是VP-SDE方差保持而Score-Based里的NCSN对应的是VE-SDE方差爆炸。这个统一看起来只是数学形式上的漂亮但它最大的实际价值在于有了前向SDE就可以推导出对应的反向SDE从噪声倒着生成数据也有了严格的理论保证。反向SDE的难点在于其中包含score函数。我们的神经网络学到的噪声预测器其实和score函数只差一个缩放系数。所以DDPM训练出来的模型同样可以被解释为score模型的近似进而用于求解反向SDE。这就是为什么这些不同名字的模型最终可以被放进同一个框架里互相借用。4.3 概率流ODE为什么重要除了反向SDE论文里还推导出一个非常重要的结论存在一个概率流ODEProbability Flow ODE它的轨迹在任何时刻都会和数据分布的变化保持对齐且边缘分布与SDE完全相同。也就是说我们可以用ODE来代替SDE做采样。ODE和SDE最大的区别是ODE没有随机噪声项是确定性的因此可以用更少的步数来数值求解。DDIM本质上就是求解概率流ODE的一种离散化方法。后续很多加速采样算法比如DPM-Solver、UniPC也都是在这个ODE视角下做的高阶数值解法。从SDE统一视角再回头看DDPM和DDIM会有一种“原来如此”的感觉它们不是两个孤立的算法而是同一个扩散过程在不同假设下的两个端点。就像坐标系的变换不会改变物理本质一样不同表达给出的只是观察扩散过程的不同角度。理解这一点对后面看LDM里为什么能加各种控制、为什么采样时可以自由切换scheduler都会有不小的帮助。5. LDM让扩散模型跑得动大图5.1 在潜在空间做扩散的理由DDPM和DDIM都可以在像素空间直接生成图但分辨率稍微提上来计算量就扛不住了。比如一张512x512的RGB图像要在这样的空间里跑1000步U-Net每一步都要处理百万级像素显存和时间都会崩溃。LDMLatent Diffusion Model的思路很简单也很聪明与其在高维像素空间折腾不如先用一个自编码器把图像压缩到低维潜在空间在潜在空间里做扩散最后再解码回像素空间。这就好比你要写一本很长的书与其一个字一个字手写不如先用压缩算法把稿子压缩一下改写草稿的时候只处理压缩后的版本最后再解压成书。5.2 整体结构VAE UNet Cross-AttentionLDM在结构上由两部分组成一是感知压缩用的自编码器二是核心的扩散U-Net再加上一个可选的条件注入模块。第一步是训练一个自编码器把图像$x$编码成潜在变量$z$再用解码器把它重建回图片。扩散过程就在潜在空间$z$上进行潜在空间通常只有原图像素规模的1/8甚至更少。在Stable Diffusion中潜在变量一般是4个通道、空间分辨率是图像的1/8左右相比像素空间计算量大幅下降。条件注入用的是Cross-Attention。文本提示先通过一个文本编码器变成向量序列然后通过cross-attention层注入到U-Net中。U-Net在去噪时每一层都会去关注“这段文本里哪些词和当前生成区域相关”。正是这个设计让“文生图”成为可能也让LDM能适配图像修复、超分、布局控制等各种条件生成任务。5.3 LDM带来的实际变化LDM在论文里还提出了一个重要的设计选择在不同训练阶段自编码器和扩散模型可以分开训练。你可以用任意一个高质量的自编码器只要潜在空间分布足够“亲民”扩散模型就可以在顶层继续训练。Stable Diffusion后续的几个版本其实都在不断调优自编码器、文本编码器和UNet之间的配合。实际使用LDM时有几个参数会直接影响出图质量。潜在空间的缩放因子scale factor要在训练自编码器时校准否则扩散模型在潜在空间里难以稳定生成。如果发现生成图像颜色发灰、对比度偏低大概率是潜在空间的统计量没有对齐。LDM也让个人显卡跑生成模型成为了可能。原来的像素空间扩散模型想在消费级显卡上生成一幅高质量作品很吃力而LDM把计算量降了一个量级再加上DDIM这类加速采样器普通人用一张消费级显卡就能跑Stable Diffusion。从研究到落地这一步跨越意义非常大。6. Guidance让生成结果“听指挥”6.1 Classifier Guidance梯度带着方向走扩散模型学会的是$p(x)$或$p(x|c)$但实际使用时我们往往想要满足特定条件的结果比如“生成一只戴红色帽子的猫”。如果只靠数据里的文本条件偶尔模型也会不听话于是就有了Guidance机制。最早的Classifier Guidance思路是在采样过程中额外训练一个分类器$p(y|x)$用它的梯度来“推”采样方向让生成结果更符合目标类别。具体实现时在反向采样的均值更新里加上一项分类器对数概率的梯度相当于靠分类器给去噪过程额外提供“往哪个方向走更像目标类别”的信号。这种方法的优点是不用改生成模型的训练过程缺点是必须额外训练分类器而且分类器在带噪数据上的表现很难保证。噪声越大分类准确率越低梯度的方向就越不准。如果分类器没训练好采样最后甚至会变得不稳定生成图片出现明显伪影。6.2 Classifier-Free Guidance不训练额外分类器Classifier-Free Guidance简称CFG是完全不同的思路也是目前扩散模型领域使用最广泛的引导方式。它的核心做法是在训练阶段以一定概率把条件信息比如文本随机置空让模型既能处理“给条件”的情况也能处理“无条件”的情况。采样时同时计算条件模型的预测和无条件模型的预测然后通过一个权重$w$把它们组合起来$$\hat{\epsilon}\theta \epsilon\theta(x_t, t, \varnothing) w \cdot \left( \epsilon_\theta(x_t, t, c) - \epsilon_\theta(x_t, t, \varnothing) \right)$$当$w0$时就等于不加引导模型只按无条件分布生成当$w0$时条件差异被放大生成结果和提示词的一致性更强。实际中Stable Diffusion的CFG scale默认在7到8左右想更贴近提示词就调高到10以上想保留更多多样性就调低到3到5。CFG不需要额外训练分类器训练流程和普通条件生成几乎一样却能达到甚至超过分类器引导的效果。代价是采样时要多跑一次无条件模型的推理计算量大约翻倍。在实践中这个代价通常可以接受而且因为引导方向来自同一个生成模型稳定性比分类器引导好不少。6.3 实践中的CFG配置经验用CFG时最常见的坑是引导权重设太高导致的过曝和颜色失真。我自己的测试经验是不同模型对CFG scale的敏感度差别很大有的模型在7就很稳有的模型到5就崩。建议每个新模型先用一组小图扫一遍scale比如3、5、7、9、11看看哪个区间最稳再固定下来。另外一些推理框架还支持“动态CFG”采样前期用较高的引导权重快速锁定构图后期把权重降下来避免过曝。这么做通常能兼顾文本一致性和画面自然度出图质量会比固定CFG好上一截。如果你在自己写采样器可以把这个逻辑实现出来试试收益挺明显。7. Rectified Flow把传输路径拉直7.1 为什么路径越直越好前面讲的扩散过程走的是一条从噪声到数据的弯曲路径。无论你用SDE还是ODE路径的弯曲程度直接决定了数值求解的误差和需要的步数。如果能把路径拉直成近似直线那么从起点到终点也许一步就能走完。Rectified Flow就是站在这个视角上做文章的。它用最优传输思想来构造从噪声分布到数据分布的一个“尽量直”的映射。数学上它不依赖逐步加噪的马氏链而是直接学习从初始分布$p_0$到目标分布$p_1$的速度场让样本沿直线$z_t (1-t) z_0 t z_1$从噪声移动到数据。训练时网络学习的就是连接成对样本的速度方向$z_1 - z_0$。这个思路看起来很简单但它大大改变了采样模式以前要跑几十步现在可以只用几步甚至一步。在直线上做数值积分误差本来就很小这比在弯曲路径上硬跑要可靠得多。近几年很多一步生成模型底层思路都和这类传输路径拉直有关。7.2 Reflow与蒸馏Rectified Flow还提出了“Reflow”机制来进一步把路径拉直。训练完第一轮速度场后沿着当前路径采样一批数据用这些采样结果重新配对源点和目标点再在“重新配对的直线”上训练第二轮速度场。每做一轮reflow路径就会被拉得更直。通常做两三轮后路径已经足够直数值积分用几步就够甚至可以做一步生成。在此基础上还可以配合蒸馏技术把多步采样学到的高质量结果“教”给一个单步模型。蒸馏的好处是推理成本降到最低坏处是会损失一定的多样性。所以实际选择时要看场景批量生成、实时交互一步模型更合适追求细节和多样性原始多步采样更稳。这一步也需要反复实验来权衡质量与速度的取舍不能指望照搬别人蒸馏参数就能一次到位。7.3 与其他方法的关系Rectified Flow和DDIM、SDE并不冲突反而互补。DDIM可以看作是概率流ODE在某个特定离散化下的解而Rectified Flow则是从传输角度重新设计了路径。你可以沿用DDIM的scheduler思路也可以直接在Rectified Flow框架里做多步采样。关键区别在于传统扩散模型的采样路径是固定的噪声衰减曲线而Rectified Flow的路径是通过成对样本“学”出来的因此更贴近数据分布本身。这套思路在实践中的直接收益就是可以用更少的步数达到近似的生成质量。如果你用的是Stable Diffusion或类似架构直接换成Rectified Flow一类的scheduler往往能在不损失太多画质的前提下把采样步数从25步降到个位数。对这类成熟架构来说这个加速收益已经相当可观了。8. 常见问题排查与经验速查8.1 训练阶段高频问题在训练扩散模型时我最先遇到的坑是loss下不去。如果你确认训练数据、学习率、batch size都没问题那多半是噪声调度器的尺度和数据分布不匹配。比如数据本身均值不是0、方差不是1而加噪公式却按标准正态分布设计模型就永远学不对。建议在训练前先把数据标准化到接近标准正态分布。另一个常见问题是EMA指数移动平均要不要开。我的经验是一定要开。扩散模型训练过程中权重震荡其实挺常见EMA能明显提升采样稳定性。衰减系数一般设0.999左右训练步数越多可以调高一点。不开EMA的话你可能会遇到“训练loss很漂亮但采样效果一塌糊涂”的情况。学习率方面扩散模型对学习率不像GAN那么敏感但也不建议太大。我用AdamW时初始学习率设1e-4起步比较稳偶尔遇到显存不够而被迫调小batch size时要注意同步降低学习率不然收敛曲线会变得很毛躁。8.2 采样阶段高频问题采样阶段最常见的抱怨就是“生成结果糊”。如果你用的不是几百步的高精度采样那大概率不是模型没训练好而是步数太少加上scheduler不合适。固定采样步数下可以依次试DDIM、DPM-Solver、Rectified Flow等不同采样器很多情况下画质差异非常明显。还有一个容易忽略的点输入噪声的尺度。很多新手直接用标准正态分布采样做初始$x_T$但如果模型的潜在空间和标准正态分布有明显偏移生成结果会整体偏灰或偏暗。LDM里会专门标定潜在空间的scale factor原因就在这里。如果换了自编码器这个标定一定要重新做。显存不够时优先考虑减小batch size而不是降低分辨率因为分辨率变换可能导致模型在训练时没见过这种尺度生成结构出现问题。也可以开混合精度训练和推理扩散模型对fp16的容忍度比很多人想象中要高尤其是推理阶段效果损失很小。8.3 工程落地经验把扩散模型部署到实际产品里我个人的习惯是先单独优化采样器。很多团队花大量时间精调Prompt、重训模型但其实只用DDIM换到更合适的scheduler采样步数减到一半还保持同等质量是完全可能的。先能用再能快最后才考虑上蒸馏、上一步模型。条件控制方面如果要在产品里支持风格控制、多条件组合等建议一开始就用CFG不要走Classifier Guidance的老路。分类器引导的维护成本太高而且分类器在带噪数据上的表现始终是个隐患。CFG条件dropout的方案简洁得多社区资料也丰富遇到问题容易排查。最后想提一句这些看似不同的模型和采样方法底层其实是互相打通的。你可以今天用DDIM跑一个经典模型明天换成Rectified Flow的scheduler后天再试试动态CFG并不需要每次都从头训练。掌握它们之间的关联后做实验会有一种“工具箱在手”的感觉。这也是我为什么建议大家先从基本原理入手而不是直接去抄某一段现成代码。