扩散后验采样DPS:用生成先验求解成像逆问题的通用框架

发布时间:2026/10/26 4:31:02

扩散后验采样DPS:用生成先验求解成像逆问题的通用框架 今天这讲是计算成像系列里我很期待的一节聊的是扩散后验采样DPS一种用生成先验来求解成像逆问题的通用框架。成像逆问题说白了就是从一张缺信息、带噪声的观测图里把原始干净图像x给猜出来。去模糊、超分辨率、图像修复、相位恢复、压缩感知重建全都是同一个数学壳子y A x n。这问题天生欠定直接做最小二乘基本是给噪声做放大器。早些年我们靠手调正则项后来有人尝试用生成模型当先验直到扩散模型成熟之后才有了DPS这类把“数据流形”和“测量一致性”都兼顾的解法。这讲我打算从贝叶斯视角把DPS的每一步拆开再给出一套可以直接上手的代码骨架和排坑清单适合正在做计算成像实验、或者准备把生成先验用到自己重建任务里的同学参考。1. 成像逆问题为什么难为什么需要生成先验1.1 从一个最小二乘问题说起所有成像系统都在干一件事把真实场景x经过某个测量过程变成观测y。普通相机是卷积加噪声手机超分是模糊加下采样医学MRI是欠采样傅里叶变换无透镜成像可能是光强级联的非线性算子。把这些统一写成 y A x nn通常假设为高斯白噪声 N(0, σ²I)逆问题就是反解x。如果不加任何约束最自然的方法是最小化 ||y - A x||²。但这个优化问题的解在A病态时会非常不稳定。拿去模糊来举例卷积核在频域里往往有一堆接近零的频率逆滤波要把这些位置的观测值除以接近零的数结果就是把噪声放大了无数倍。伪逆解看起来像一张被高频噪声吞噬的图像毫无实用价值。因此工程上必须引入先验把问题改写成最大后验估计x̂ argmin ||y - A x||² λ R(x)这里的R(x)就是我们塞进去的先验知识。早期最常用总变差假设干净图像分段平滑惩罚梯度幅值后来有各种稀疏变换、低秩约束、非局部自相似约束。这些先验有一个共同特点它们是人类基于对自然图像的局部统计观察手写出来的规则。手写规则的问题在于表达力有限总变差能保住边缘但会抹平纹理稀疏先验假设自然图像在某组基下稀疏但这种假设对复杂场景常常不成立。它们本质上都是“局部规则”像阅卷标准里的“卷面分”只看你每段写没写清楚不管你的论证逻辑是不是真的像一篇好文章。1.2 传统先验与生成先验的差异生成先验换了一个思路与其手写规则不如让模型从海量自然图像里学“什么是自然图像”。GAN时代有人用生成器G把重建结果限制在隐空间x G(z)再优化z让A(G(z))≈y。这条路证明有效但隐空间优化不稳定生成器覆盖的分布往往有空洞遇到复杂测量算子容易陷入局部最优恢复出来的图像也可能被限制在生成器见过的模式里。扩散模型的到来改变了这一点。它不是在低维隐空间做生成而是在像素空间直接建模整个数据分布p(x)。它的先验体现为一个分数函数∇ log p_t(x_t)告诉你当前图像离自然图像流形还有多远、应该往哪个方向修正。这个先验是非线性的、全局的能捕捉纹理、结构和语义层面的约束。把这种先验接到逆问题求解上就有了DPS这一类方法。理解这个背景很重要因为后面DPS的每一个设计选择本质上都是在回答同一个问题如何既让结果“像自然图像”又让结果“和观测吻合”。注意生成先验的一个隐藏前提是“数据域匹配”。如果预训练扩散模型在自然图像上训练而你的观测来自医学影像或工业无损检测先验会把输出往自然图像分布上拽可能掩盖真实缺陷。选底座时要先想清楚目标域和训练域是不是同一类数据。2. 扩散模型基础DPS的底座2.1 前向过程与反向过程DPS不是要单独训练一个条件扩散模型它利用的是无条件扩散模型。先简单回顾扩散模型在做什么。给一张干净图像x0前向过程逐步加噪x_t √ᾱ_t · x0 √(1−ᾱ_t) · εε ~ N(0, I)这里ᾱ_t是一个随t递减的常数t越大ᾱ_t越接近0x_t就越接近纯噪声。一个很好的性质是从x0到任意时刻t的加噪结果都能一步算出来不用真的迭代T次。训练时神经网络ε_θ(x_t, t)的任务是预测混进去的噪声ε损失就是简单的 ||ε − ε_θ(x_t,t)||²。这个看似简单的目标有一个深层含义网络实际在估计条件期望并隐式学会了当前噪声图像的分数。具体关系是∇ log p_t(x_t) ≈ −ε_θ(x_t,t) / √(1−ᾱ_t)也就是说只要有一个噪声预测器就等于拿到了图像分布的分数。生成时从x_T ~ N(0,I)出发按反向过程一步步去噪每步相当于“先猜一个x0再往更可信的状态挪一步”。DDPM采样通常要几百到上千步DDIM通过确定性的轨迹可以压缩到几十步代价是生成多样性略降。对DPS来说这个底座是预先训练好的不需要为每个逆问题重新训练这是它能成为通用求解器的前提。2.2 从无条件生成到条件采样无条件扩散模型只能采样p(x)也就是生成一张看起来自然的图像但完全不受观测y约束。要让采样结果满足y最朴素的想法是给每个任务都训练一个条件扩散模型p(x|y)。可逆问题的退化方式五花八门卷积核不同、超分倍率不同、采样mask不同都算一个新任务。每个任务都重新训练成本完全不可接受。DPS的思路是在采样阶段做零训练引导。它把后验分布写成p(x_t|y) ∝ p(x_t) · p(y|x_t)对应到反向采样的每一步我们可以先按无条件扩散模型走一步再根据“当前状态和观测y的匹配程度”去修正这一步的方向。这个修正量来自似然项的梯度也就是DPS名字里的“后验采样”采样过程不再只是沿着先验分数走而是沿着条件分数走。这样同一个预训练扩散模型可以通用于一批逆问题只需要换测量算子A和观测y接入一条梯度即可。3. DPS核心推导测量一致性如何注入扩散采样3.1 目标分布条件分数分解如果能在每一步都计算出条件分数的解析式问题就解决了。条件分数可以分解为∇ log p_t(x_t|y) ∇ log p_t(x_t) ∇ log p_t(y|x_t)第一项是扩散模型的先验分数可以直接用噪声预测网络代入。第二项是“给定x_t时观测y的似然”它是DPS整个方法的核心难点。难点在于p_t(y|x_t)不是一个可以直接写出来的分布原因是x_t是加了大量噪声的中间状态它和观测y之间隔着未知的x0。严格写的话p_t(y|x_t) ∫ p(y|x0) p(x0|x_t) dx0这里p(y|x0)很简单就是一个以A x0为均值的高斯分布但p(x0|x_t)是“从噪声图像反推干净图像”的后验没有解析形式。这个积分把测量一致性和先验耦合在一起必须想办法近似。3.2 关键近似用x̂0替换不可解积分一个自然的想法是用期望值或众数来近似这个积分。扩散模型领域有一个Tweedie公式给定x_t对干净图像x0的最优估计是x̂0 (x_t − √(1−ᾱ_t) · ε_θ(x_t,t)) / √ᾱ_t这个x̂0其实就是去噪网络做一次去噪得到的结果它在采样过程中本来就会反复出现。DPS的关键近似是直接把这个x̂0当成x0的代表把不可解积分替换成一个点估计p_t(y|x_t) ≈ p(y|x̂0(x_t)) exp( −||y − A x̂0(x_t)||² / (2σ²) )这样一来测量一致性的量度完全落在一个平方误差上。我们需要修正x_t使得当前去噪估计再经过测量算子之后尽可能接近观测y。注意这里x̂0是x_t的函数所以对x_t求梯度时必须把ε_θ的参数也纳进来靠自动微分完成。这个近似看起来简单但把“先验推断”和“数据拟合”之间最难解的积分绕过去了这是DPS能在工程里落地的关键一步。3.3 更新公式与实现细节把上面几步组合起来DPS的采样循环可以写成x_T ~ N(0, I) for t T, T−1, ..., 1: 1) 先执行一步无条件反向采样得到 x_{t−1}^{uncond} 2) 计算 x̂0 (x_t − √(1−ᾱ_t) ε_θ(x_t,t)) / √ᾱ_t 3) 计算引导梯度 g ∇_{x_t} ||y − A(x̂0)||² 4) 令 x_{t−1} x_{t−1}^{uncond} − ζ_t · g / (||g|| ε)这里ζ_t是引导步长ε是防止除零的小常数。在实践中最稳的做法是对梯度做归一化后再乘步长这样可以避免不同t时刻梯度范数差异太大导致的震荡。有些人会问为什么不直接用带权重的梯度1/(2σ²)·g可以但权重会和σ耦合在一起调参时比较痛苦归一化之后ζ的语义更单纯就是一个“测量约束强度”的旋钮。还有两个值得注意的细节。第一在自动微分框架里计算g时x_t必须是requires_grad的叶子节点损失用的是A(x̂0)与y的平方误差这样g会自然包含x̂0对x_t的依赖。第二在t很大的时候x_t几乎全是噪声x̂0非常模糊引导梯度的方向并不精确所以有些实现会给ζ乘一个随t变化的系数早期弱引导、后期强引导。我个人复现时全程固定ζ1.0在很多线性问题上也能跑通但要做精细实验还是建议加一个动态系数。4. 实操指南从代码到实验设置的完整流程4.1 一个能跑起来的DPS代码骨架纸上谈兵没用直接看代码。假设你已经有一个训练好的无条件扩散模型denoiser测量算子A是一个可微的映射alpha_bar[i]存放第i步的噪声调度值。下面是单步引导梯度的实现思路import torch def dps_guidance(x_t, t, y, A, denoiser, alpha_bar, zeta1.0): # 关键让 x_t 成为计算图的叶子节点 x_t x_t.clone().requires_grad_(True) eps_pred denoiser(x_t, t) x0_hat (x_t - torch.sqrt(1 - alpha_bar[t]) * eps_pred) / torch.sqrt(alpha_bar[t]) loss torch.sum((y - A(x0_hat)) ** 2) grad torch.autograd.grad(loss, x_t)[0] # 梯度归一化后乘步长更稳 grad grad / (grad.norm() 1e-6) * zeta return grad在采样循环里每一轮的顺序是先用DDPM或DDIM的无条件更新规则得到x_next_uncond然后调用上面的函数拿到引导梯度最后让x_next x_next_uncond - guidance。注意整个引导计算单独立出来避免污染无条件更新里的梯度状态。我实际跑下来有几个容易踩的坑输入尺度必须和训练扩散模型时一致。如果扩散模型是在[-1,1]范围内训练的y和A的输出也要统一到这个尺度否则引导梯度的量级会偏移。A(x0_hat)的输出和y的shape要保持一致。超分辨里A往往包含下采样如果你的下采样算子不是可微的要换成“模糊可微采样”的近似。不要对x_t本身做detach后再计算引导否则你拿不到∂x̂0/∂x_t这一项引导会退化成一种粗糙的“测量匹配”效果明显变差。4.2 关键超参数选择DPS的超参数不多但每个都值得花时间调。引导步长ζ是最敏感的一个。太小测量一致性不足结果模糊太大图像结构被强行扭曲出现彩色噪点或振铃。一个比较合理的起始点是1.0然后观察结果如果数据保真项下降但感知质量变差就往小调如果重建结果和观测始终差得远就往大调。在DDPM采样下0.5到1.5是一个常见区间改成DDIM加速时步数减少会让每一步的“信息密度”变大我认为把ζ比经典设置略微下调更安全。噪声水平σ也不能乱设。DPS的似然项里1/(2σ²)承担着“我该多信任观测”的权重。如果你直接把σ设成0就是把观测当成无噪声在低质量观测下容易过拟合噪声设得过大则引导太软。如果退化噪声未知可以先做一个粗重建从观测残差里估计σ² ≈ ||y − A x_recon||² / m其中m是y的元素个数。把这个估计值放进DPS比拍脑袋定σ要靠谱得多。总采样步数T也需要平衡。理论上DPS遵循DDPM反向过程1000步是黄金标准实际工程里我用DDIM把步数压到50到100步效果依然可以只是需要重新调一下ζ。如果你发现步数减少后图像出现“半成品感”大概率是引导太强每一步都企图把当前状态强行拉向某个测量一致的点。4.3 不同逆问题怎么快速适配DPS最大的吸引力在于“通用”。换问题基本就是换一个A函数。下面这张表是我做过的几种常见适配逆问题测量算子A需要注意的点高斯去模糊卷积核要padding正确梯度在边界处收敛慢超分辨率模糊下采样下采样算子要可微用面积平均近似较多图像修复二进制掩膜掩膜区域直接置零即可引导梯度天然只在已知区域生效压缩感知随机投影矩阵大矩阵时用分块计算避免显存爆掉相位恢复傅里叶幅度非线性观测损失改成y与幅度之间的误差医学MRI重建欠采样傅里叶用FFT算子梯度计算方便重点调σ拿相位恢复举例观测模型变成y |F(x)|²不再写成y Ax。这时候DPS的推导几乎不用改只要把数据保真项从||y − A(x̂0)||²换成||y − |F(x̂0)||²并且对x_t重新求梯度就行。这恰恰是DPS比很多依赖线性观测模型的方法更通用的原因只要你的测量过程可微它就能作为损失函数接入。5. 实验结果解读DPS效果与局限5.1 定性定量的表现在常见基准上DPS的表现在中等噪声和复杂先验场景下通常明显优于传统TV正则化。去模糊和超分这类任务中TV解出来的图边缘是干净的但纹理和材质会被磨平DPS能恢复比较自然的高频细节尤其在人脸、毛发这类强语义结构上差距肉眼可见。定量指标上PSNR提升几dB的情况不少见在感知指标LPIPS上的提升通常更明显有时能差出0.1以上。这背后的原因是像素级指标PSNR更关注“平均误差”而DPS的先验约束在感知层面生成结果可能和真实图像不是逐像素一致的但视觉上非常真实。这也是所有生成先验方法的双刃剑指标好看、观感自然但你没法保证恢复出来的细节是“真实发生过”的细节。在做图像质量评价时不能只看单张最好看的图还要看多random seed下的稳定性和最终应用能不能接受这种不确定性。5.2 和其他生成先验路线的对比把DPS放进生成先验的谱系里看会更清楚方法路线先验形式主要限制DPS的相对优势GAN生成先验隐空间流形分布覆盖率有限、隐空间优化不稳像素空间采样更完整能量模型/归一化流显式密度高维图像建模难度大扩散模型训练成熟稳定需要SVD的扩散后验方法子空间分解需要测量算子可分解只需要可微算子DPS分数先验逐像素梯度每步一次反向传播通用、零训练如果你手里的测量算子结构很规整比如周期采样、对角化的模糊核那些需要SVD的方法可能更快更准但一旦算子是非线性、耦合复杂、没有漂亮分解的DPS往往是更省事的选择。这也是我在实际课题里选择DPS的主要原因。5.3 典型的失败模式和边界DPS不是万能的。第一个典型失败是高噪声场景。观测噪声很大时x̂0在采样早期非常不可靠引导梯度可能把一个合理的去噪方向带偏结果出现各种不存在的结构。第二个是测量信息严重不足时生成先验会“脑补”细节。这在医学图像里尤其需要警惕因为“看起来合理”不等于“诊断正确”。第三个是逐像素平方损失带来的过平滑有时对细节敏感的任务DPS不如专门设计的结构化损失版本。针对这些边界后续有不少改进思路。一类是在更新时限制x̂0不要偏离x_t的邻域太远相当于给引导梯度加一个信任区域另一类是改成在潜在扩散模型的潜空间里做DPS速度和显存都友好很多还有一类是把逐像素MSE换成特征域损失让测量一致性约束在感知空间里生效。这些都是在DPS框架上打补丁核心的后验采样思想没有变。6. 常见问题与排查技巧实录6.1 样本崩坏引导步长过大还是过小我在复现时遇到的第一个问题是结果出现大量彩色噪点和结构性伪影。第一反应是引导步长太大了把每次采样都推出合理流形。后来我把ζ从1.0降到0.5伪影立刻缓解但细节也少了一些。最终在0.8左右找到一个平衡点。反过来的症状是结果模糊、观测残差居高不下多半是ζ太小或引导启动太晚。建议做法固定其他参数对ζ做一组小扫描比如0.3、0.5、1.0、1.5。同时监控两个数值数据保真项||y − A(x̂0)||²和x̂0的感知质量。保真项下降但感知质量变差说明过拟合观测保真项降不下去说明引导不足。这种“保真和感知互搏”的现象在生成先验方法里几乎必然出现要学会接受并找到平衡点。6.2 噪声水平设置不匹配如果你发现DPS在低噪声数据上表现反而不如高噪声数据不用奇怪很可能是σ值设大了。σ在这个方法里直接控制似然项的锐度设太大引导就变成“蜻蜓点水”设太小引导变成“钻牛角尖”。比较稳妥的方式是直接用上一节说的经验估计法从残差里估σ。如果想让结果更稳一点可以略微把估出来的σ调大10%20%牺牲一点保真换稳定。6.3 梯度爆炸与数值不稳定虽然我对梯度做了归一化但不幸的是在某些病态严重的算子下A(x̂0)的梯度依然可能在某几步出现尖峰。原因通常是A本身条件数太差或者x̂0在t较小时出现了极值。我的应对办法有几个一是对引导梯度做clip限制每一步修正量不要超过图像像素值范围的一定比例二是给A(x̂0)先做一个很小的空间平滑再算损失相当于给梯度穿过一个低通滤波三是检查输入尺度如果图像像素值在[-1,1]但y是[0,255]梯度方向可能没问题但幅度会被尺度差带着跑。6.4 内存占用与采样加速DPS每一步都要通过A(x̂0)反传显存开销比普通扩散采样高出一截。在高分辨率任务里我一般这样优化优先用DDIM把总步数压到100以内对大图像按重叠patch切块分别计算引导梯度再拼回去注意重叠区域做加权平均对线性算子的矩阵直接用自定义的线性算子实现不要显式构造大矩阵最后如果你发现瓶颈在x̂0的反向上可以对x̂0做一次detach再算一部分解析梯度但这会损失精度我一般只在显存实在不够时用。6.5 怎么判断DPS该停了我个人的习惯是每20步打印一个数据保真项和x̂0的LPIPS值。DPS采样的过程中保真项一般是单调下降然后趋于稳定感知质量则可能在某个点之后开始反弹变差。当你看到这两个指标开始“互搏”的时候就是ζ该调小、或者采样总步数可以截断的信号。这个习惯帮我避过很多次“生成结果好看但不保真”的坑。另有一个小技巧如果某个任务的测量模型特别复杂先用一个低分辨率或降采样的版本跑一轮DPS观察引导梯度的量级和稳定性再放大到完整分辨率。这样调参的成本会低很多。计算成像里大部分DPS翻车都是直接上高分辨率结果发现噪声估计、步长、显存三个问题搅在一起很难定位。先降维排查再回全分辨率效率要高不少。
延伸阅读

更多相关文章

2026/10/26 4:31:02

DSH内核会话模块:JSONL追加写入与SessionEvent设计实践

1. 会话系统到底在解决什么问题很多人第一次听到“内核里的会话”这个词,脑子里浮现的可能是聊天窗口、对话框,或者某个长连接。但真到自己动手写一个内核的时候,你会发现会话的本质根本不是界面,而是状态的可持久化与可恢复。我当…

2026/10/26 4:26:02

Canvas动态星空背景:分层粒子、DPR适配与渲染性能优化

1. 需求拆解:先想清楚要画成什么样,再动手写代码1.1 星空背景到底要解决什么问题Canvas 绘画动态星空背景这件事,本质上是用浏览器的一块位图画布,在每一帧里不断擦除、重绘,模拟出肉眼在野外夜里抬头看到的那种星野效…

2026/10/26 4:26:02

智能体抢话问题排查:VAD、端点检测与停顿参数调优实战

做语音交互开发的都知道,智能体抢话这件事有多让人抓狂。你正在测试刚上线的语音助手,用户刚说了半句"我想问一下那个——",智能体就抢答"好的,我来帮你处理"。用户愣了,然后不得不重复一遍。等这…

2026/10/26 5:31:06

选择排序与堆排序:从线性找最值到堆结构优化

排序算法写到第二章,选择排序这个题目其实挺尴尬——几乎所有教材都会讲它,但真到工程里手写它的人少得可怜。我最早也把它当成"教科书里的过客",直到有一次碰到写操作代价特别高的场景,才回过头认真读了一遍它的交换次…

2026/10/26 5:31:06

WorkBuddy+微信云开发实战:OCR剧本快读小程序从零到上架

1. 从一句“能不能做个剧本快读”开始:项目缘起与整体设计去年年底,一个做短剧和网文的朋友找到我,说他们团队每天要处理几十上百个剧本文件,格式五花八门,有 PDF、有 Word、有扫描件,甚至还有拍照发过来的…

2026/10/26 5:31:06

YOLO口罩检测毕设全链路实践:从数据到树莓派部署

1. 这个毕设为什么值得花三个月认真做——不是为了交差,而是练出真本事“基于YOLO实现的口罩佩戴检测系统”——光看标题,很多同学第一反应是:“哦,又一个调库跑通Demo的毕业设计”。但我在带过十几届毕业设计、审过上百份图像识别…

2026/10/26 5:31:06

经典算法题实战解析与优化技巧

1. 为什么经典算法题值得反复练习作为一名在编程竞赛和工业界摸爬滚打多年的老码农,我见过太多程序员在算法面试中折戟沉沙的场景。去年帮团队面试37个候选人,有29个在基础算法题上栽了跟头——不是写不出完整解法,就是时间复杂度分析出错。这…

2026/10/26 5:31:05

用《计算机网络》第2章习题吃透物理层底层逻辑

1. 这不是“抄答案”,而是用课后题反向吃透《计算机网络》第2章的底层逻辑很多人拿到《计算机网络教程》(第六版)第2章课后习题,第一反应是翻答案、对结果、打个勾完事。我带过几届某高校网络课程助教,也辅导过不少备考…

2026/10/26 5:26:05

Apache Pulsar在分布式消息中间件中的创新实践

1. 活动背景与核心价值Pulsar Developer Day作为COSCon25的重要同场活动,聚焦消息中间件领域的技术创新与落地实践。消息中间件作为分布式系统的"中枢神经",在实时数据处理、微服务解耦、事件驱动架构等场景中扮演着关键角色。Apache Pulsar作…

2026/10/26 0:20:20

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/26 1:53:47

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/26 0:20:17

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/26 0:00:48

Nested Learning:解决AI灾难性遗忘的分层记忆架构

1. 为什么AI需要"记忆"能力?在传统深度学习模型中,我们常遇到一个根本性问题:模型在处理新任务时,往往会完全遗忘之前学到的知识。这种现象被业界称为"灾难性遗忘"(Catastrophic Forgetting)。想象一下&#…

2026/10/26 0:00:48

企业级大模型应用:OCR预处理、Dify编排与DeepSeek落地实战

1. 这不是“用大模型”,而是重构工作流的起点“大模型的应用和工具”——这八个字现在被贴在无数技术分享、招聘JD、内部立项书的标题栏里,但绝大多数人点进去后看到的,是零散的API调用示例、几个开源UI截图、或者一句“接入LLM就能降本增效”…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑