扩散模型CFG全解析:从原理到ComfyUI参数调优

扩散模型CFG全解析:从原理到ComfyUI参数调优 我第一次认真看CFG这个参数是在ComfyUI的KSampler节点里。那时候我还在用外挂分类器给扩散模型做条件对齐生成一张图要同时维护一个UNet和一个分类器稍微换个数据集就得重新训练配套模块折腾得够呛。后来才把Classifier-Free GuidanceCFG这个看起来只有一行加权相减的操作彻底搞清楚——它直接干掉了外挂分类器成了现在扩散模型条件生成的事实标准。这篇文章我就把CFG的来龙去脉、数学直觉和实操参数一次讲透适合正在啃扩散模型源码、或者天天在ComfyUI里调cfg滑块但不太清楚它在干什么的朋友。1. CFG是怎么来的为什么外挂分类器方案会被淘汰1.1 分类器引导的两大痛点在CFG出现之前想让扩散模型生成符合某个条件的内容主流方案是Dhariwal和Nichol在《Diffusion Models Beat GANs on Image Synthesis》里提出的Classifier Guidance分类器引导。这个思路说起来很直白扩散模型在去噪过程中已经知道了图像的“大致长相”但不知道你想要什么那就额外训练一个分类器在每个去噪步骤里计算梯度把生成方向往目标类别那边推一把。写成公式就是score(z_t, y) score(z_t) ∇ log p(y | z_t)这个公式的第二项是分类器给出的梯度相当于一个“外力”。这方案当时效果确实好生成质量明显提升但它有两个很要命的问题。第一个问题分类器和扩散模型是两套系统训练、存储、推理都要分别维护。扩散模型动不动上亿参数分类器也得跟着训练等于所有成本直接翻倍。更麻烦的是分类器是额外加的换一个条件类别体系分类器就得重新训练扩散模型反而成了配角。第二个问题更隐蔽分类器是在干净图像上训练的但扩散模型在采样时输入的是带噪的z_t越往早期步骤噪声越大。这就好比让一个阅卷老师去读被墨水泼了一半的试卷——他练的是看干净卷子你递给他一张花的他自然给不出靠谱的判断。实际应用中梯度会变得很不稳定经常把生成带偏。有人试过把分类器也放到带噪图像上训练或者对输入做噪声增强但代价是分类器训练变得更复杂整体方案越来越臃肿。我当时就在想有没有一种办法不靠外挂分类器也能让模型自己学会“往条件方向走”CFG给出的答案很聪明与其让模型把条件和图像的关系拆开学不如在训练时直接让模型见过“有条件”和“无条件”两种状态采样时让这两个状态自己做差。1.2 CFG的核心思路随机丢条件CFG的论文作者Ho和Salimans做了一个看起来简单到有点“随意”的操作训练的时候以一定概率通常是10%到20%把条件y直接替换成空条件比如文本就替换成空字符串类别就替换成null。这样训练出来的模型同一个参数权重里同时容纳了条件分布 p(z | y) 和无条件分布 p(z) 两种能力。这个做法理解起来不难但它背后的想法极其关键。你想象一个厨师平时做菜都是按菜单来的但偶尔有一次你给他看一张白纸让他自由发挥。等他习惯了“有菜单”和“没菜单”两种模式之后你再让他做菜他就能告诉你“按菜单做是这样自由发挥是那样”两个答案一对比你就能明确知道“菜单到底改变了什么”。在采样阶段CFG做的事情就是把这两种预测同时算出来然后做线性外推。伪代码就长这样# training: 以概率 drop_prob 丢弃条件 if random.random() drop_prob: y None # 空条件 # sampling: 同时做有条件和无条件两次预测 eps_cond model(z_t, t, y) # 按条件预测噪声 eps_uncond model(z_t, t, None) # 不按条件预测噪声 eps eps_uncond cfg_scale * (eps_cond - eps_uncond) z_next denoise_step(z_t, t, eps)那“一行代码”就体现在这里eps eps_uncond cfg_scale * (eps_cond - eps_uncond)。几乎所有的CFG实现核心都是这一行加权相减没有分类器没有额外的梯度网络训练和推理都在同一个模型里完成。2. CFG技术拆解一行代码背后的推导逻辑2.1 从分数估计到条件引导的等价变形要理解CFG为什么有效最好从score-based模型的角度看。DDPM在训练时学的是噪声ε实际上它和score函数是等价的score(z_t) 正比于 -ε_θ(z_t, t)。所以后面我用ε来写也不影响理解。Classifier Guidance的score公式可以重新整理一下。带条件的score是∇ log p(z_t | y) ∇ log p(z_t) ∇ log p(y | z_t)而CFG做的事是直接用两个网络输出之差来近似后一项∇ log p(y | z_t) ∝ ε_θ(z_t, y) - ε_θ(z_t, ∅)为什么这个近似成立因为模型在空条件下近似建模的是无条件分布在有条件下近似建模的是条件分布两者之差在score空间里正好对应了“条件提供了多少额外信息”的梯度方向。也就是说CFG并没有显式计算分类器的梯度而是通过训练时的随机丢弃让同一个网络隐式地学会了两套分布然后拿两套分布之差当引导方向。这等于把原来“外挂分类器”做的事变成了“模型内部自我对比”的事。你可以理解成原来你要请一个外部专家来告诉你“这一步该往哪偏”现在模型自己就能回答“条件版本和无条件版本差在哪”偏转方向直接从两个预测之差里提取出来。2.2 数学外推为什么w大于1会更好CFG的推理公式里cfg_scale通常记为w这个系数很有意思。当w1时公式就是eps eps_cond模型输出就是纯条件采样没有任何引导强化。但实际使用中w通常取大于1的值比如7。这时候公式变成eps eps_uncond 7 * (eps_cond - eps_uncond)这可以拆成eps_cond 6 * (eps_cond - eps_uncond)。也就是说在条件采样的基础上又额外加上了6倍的“条件与无条件之差”的方向。这不是插值是外推——它放大了“条件”这一侧的信号强度。生活化一点类比条件采样好比一个人知道目的地但走得小心翼翼CFG w7就是不仅知道目的地还每一步都拼命提醒自己“我要往这个方向走别偏”。模型会更有“主见”生成的样本和条件描述贴合得更紧。但外推是有代价的。你把一个方向放大了7倍同时也把噪声和高频细节放大了7倍过度的时候就会出现颜色过饱和、边缘发光的“塑料感”。这就是我们常说的“CFG过高图崩了”。2.3 无条件分支的必要性没有它就会失去基准线注意一个细节CFG的计算里无条件预测eps_uncond绝不是一个可有可无的点缀它是整个公式的基准线。如果没有这个基准只放大eps_cond的幅度模型会被条件信号捆住多样性急剧下降甚至直接坍缩到某几个固定模式。你可以做个实验把cfg_scale从1慢慢调到20观察同一组种子。你会发现在某个区间内图片和提示词的匹配度越来越高但过了某个点颜色开始溢出边缘开始出现奇怪的纹理细节变得生硬。这就是基准线被外推破坏的典型表现。不同的模型、不同的采样器这个最佳区间都不同所以实际使用中必须自己扫一遍。3. ComfyUI实操KSampler里那个cfg到底该怎么调3.1 参数位置和常见取值区间在ComfyUI里KSampler节点的cfg参数直接对应上面的cfg_scale。默认值通常是7.0这个数值来自Stable Diffusion 1.x时代的大量经验但对不同模型并不普适。我自己的经验值可以给个参考区间模型类型常用CFG区间备注SD 1.55 ~ 9经典默认7左右SDXL3 ~ 7推荐4~6过高容易过曝Flux系列1.5 ~ 4很多流程甚至用1~3OELCFG类节点有特殊处理LCM / Turbo等少步数模型1 ~ 3配合蒸馏模型时CFG通常很低拿到一个新模型我建议先按这个区间扫一遍不要上来就用默认7。尤其是SDXL很多人第一次用感觉“怎么颜色那么冲、对比度那么高”十有八九是CFG还停在7没降下来。3.2 不同采样器对CFG的敏感程度不一样这个点很多新手会忽略同样的CFG值配不同采样器效果可能天差地别。Euler、Heun这类一阶/高阶采样器对CFG的响应相对平稳DPM 2M Karras这一类在CFG7附近表现稳定但到了SDE类采样器比如DPM SDE Karras它对CFG特别敏感我实测下来CFG调到3~4反而更干净。原因也不难理解。SDE采样器在每一步会注入额外的随机噪声这个随机性和CFG的“外推放大”叠加在一起容易产生高频噪声。你调低CFG本质上是在给外推方向降权压制了随机性的过度放大。所以以后在ComfyUI里换采样器的时候别忘了重新检查CFG别一个参数走天下。3.3 步数、负面提示词和CFG的搭配经验步数和CFG的关系也很有意思。扩散模型用DDIM这类采样器时步数太少模型还没完全收敛CFG再高也救不回来反而会把中间状态的伪影放大。我遇到过不少“步数设置的12CFG拉到15结果画面全是噪点”的情况这不是模型不行而是参数组合太激进。常规做法是步数20以下CFG尽量控制在4~6步数20~30CFG可以放到7~9步数50以上CFG回到5左右就能有稳定的效果。很多人不知道一个反直觉的经验CFG不是越高越好步数足了以后反而是低CFG配合更多步数出图更细腻。负面提示词Negative Prompt在ComfyUI里也是通过CFG机制生效的。它的本质是把“空条件”替换成“你不想要的内容条件”让模型同时看到你想要的方向和不想要的方向CFG外推的时候会额外远离负面描述。所以在写负面词时你不需要写“低质量、模糊、混乱”这种大而全的词堆写两三个明确不想出现的东西就够了因为CFG的外推机制会自动放大这些词的“排斥效果”。4. 常见问题与排查技巧实录4.1 图片过饱和、塑料感严重怎么办这个现象太典型了脸是好看的但皮肤像涂了一层蜡高光部分直接过曝成白色整个画面饱和度溢出。排查思路很直接先看CFG。把CFG一步步往下降每次降0.5左右对比效果。我遇到过很多次CFG从7降到5画面立刻干净一大截。如果你降CFG之后提示词遵守度变差了那说明你卡在“高CFG组合”和“低CFG组合”的矛盾里。此时优先检查采样器。换一个更稳的采样器比如从SDE类换成非SDE类通常能让你在较低CFG下仍然保持良好的条件对齐。4.2 提示词明明写了但生成结果里没有出现这类问题先别急着加CFG分三步排查第一步确认模型本身能理解这个提示词换个简单同义词测试第二步看负面提示词是不是把相关概念压掉了负面词写太狠会形成“排斥力”第三步才考虑把CFG从6慢慢往上加加到8左右如果还没有反应那就不是CFG的问题而是模型能力边界问题。现实中我见过最多次的翻车原因其实是负面提示词写得太狠。比如你想生成“一只戴帽子的猫”负面词里写了“帽子”模型当然左右为难——CFG会同时让它靠近“猫”和远离“帽子”结果就是猫出来了但帽子消失。CFG不是魔法它只是在两个方向上做平衡你把矛盾塞给它它只能给你一个妥协结果。4.3 CFG在潜在空间中的表现怪异的特殊场景扩散模型里还有一类特殊场景img2img或者inpainting时输入图像本身有很大的结构约束。这时候CFG太高模型为了“贴合提示词”会把原图结构也强行改掉导致构图变得很奇怪。所以做图生图时我通常会比文生图低1~2个档位尤其是重绘幅度大的情况下。另一个容易忽略的点当你在节点的Latent类型上做多次采样比如两次KSampler串联每一步的CFG最好也考虑递减。有一次我做二次精修流程第一步CFG7第二步还保持CFG7结果画面越修越硬边缘出现明显伪影。后来把第二步CFG降到4输出才自然起来。这其实就是“动态CFG”的朴素版本越到后期噪声越少需要的引导越弱CFG也该跟着递减。4.4 CFG调成1和调成0到底有什么区别这个问题我问过不少人实测结果是这样的CFG1时公式里eps_cond和eps_uncond的权重完全落在eps_cond上模型退化为纯条件采样。CFG0时公式变成eps_uncond - (eps_cond-eps_uncond) 2eps_uncond - eps_cond这个操作不是“无引导”而是反向引导实际生成会往“远离条件”的方向走画面会变得不可控制。所以正常情况下CFG最低就设在1附近不要去碰0。顺带说一句现在有些新采样器直接集成了CFG逻辑比如DPM-Solver系列在少步数场景下能自动适配CFG但大部分流程里CFG还是需要你手工控制的。搞清楚这个参数的本质远比死记“7最好”要有用。5. 从CFG出发理解扩散模型更多设计引导、蒸馏与动态化5.1 CFG与无分类器训练现在模型为什么要塞大量空条件样本如果你去翻现在主流潜在扩散模型LDM的训练代码会发现一个共同点训练过程中都预留了一定比例的空条件样本。Stable Diffusion训练时会把约10%的文本条件替换成空字符串目的就是为了给CFG提供那个“无条件基准线”。这个设计现在已经被当成基础设施写进了几乎所有扩散模型训练框架里。理解了这个逻辑你就能看懂很多模型行为。为什么你用空提示词生成时画面常常很放飞因为模型把这种情况当成训练时见过的“无条件模式”它会自由发挥。为什么有些模型对某个特定风格特别敏感因为训练数据里该风格的“条件差异”更大CFG外推时放大的信号更强。5.2 CFG不是终点蒸馏模型教你重新审视引导强度随着LCM、Turbo这类少步数蒸馏模型的流行CFG的经典用法也在改变。蒸馏模型训练时就刻意把CFG行为压低了你再用CFG7去跑很容易出现过曝。这也是为什么很多LCM工作流直接把CFG设为1到2。蒸馏模型不需要大CFG本质上是训练过程已经把“无条件与有条件之差”的引导信息压缩进了网络权重里推理时就不用再外推那么狠。这个现象反过来验证了CFG的本质它不是一个需要永远保持高值的参数而是一个用来补偿模型条件表达能力不足的工具。模型对条件理解得越好你需要的外推就越小。5.3 动态CFG与自适应引导我的下一步尝试最近我在玩动态CFG的流程思路是让cfg_scale随采样步数递减前一半步数用CFG8保证语义对齐后一半步数降到CFG3保住细节干爽度。实测下来在同样的总步数下动态CFG通常比固定CFG出图更稳定尤其是复杂提示词场景。ComfyUI里有专门的CFG调度节点也可以用自定义脚本来实现递减逻辑。核心代码就是把原来固定的cfg_scale变成随t变化的函数比如cfg_scale cfg_max - (cfg_max - cfg_min) * (t / total_steps)这个思路虽然不一定对每个模型都最优但它让我对CFG的“时变特性”有了更多直觉。扩散模型的去噪过程不是均匀的早期确定构图中期确定主体后期确定细节每个阶段对条件引导的需求本就不同一刀切用一个CFG走到黑多少有点粗糙。我自己的经验是在早期多给一点引导让语义坐实后期降低引导让纹理自然这个方向基本不会错。最后再分享一个我调图的固定流程拿到一个新模型先把采样器固定在DPM 2M Karras步数固定在25步然后把CFG从3到12每隔1个档出一张图摆成九宫格对比。整个过程五分钟但能让你一眼看穿这个模型的性格——它是在哪个区间提示词对齐最好、哪个区间开始发糊、哪个区间开始过曝全都在几张图里摆得明明白白。这个习惯帮我解决了很多“模板提示词没用”的困惑。CFG从来不是一个越大越好的参数找到这个模型自己的“舒适区间”才是真正意义上的条件对齐自由。