扩散模型CFG全解析:从原理到ComfyUI参数调优
2026/9/9 14:32:16 网站建设 项目流程

我第一次认真看CFG这个参数,是在ComfyUI的KSampler节点里。那时候我还在用外挂分类器给扩散模型做条件对齐,生成一张图要同时维护一个UNet和一个分类器,稍微换个数据集就得重新训练配套模块,折腾得够呛。后来才把Classifier-Free Guidance(CFG)这个看起来只有一行加权相减的操作彻底搞清楚——它直接干掉了外挂分类器,成了现在扩散模型条件生成的事实标准。这篇文章我就把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)这个系数很有意思。当w=1时,公式就是:

eps = eps_cond

模型输出就是纯条件采样,没有任何引导强化。但实际使用中,w通常取大于1的值,比如7。这时候公式变成:

eps = eps_uncond + 7 * (eps_cond - eps_uncond)

这可以拆成:eps_cond + 6 * (eps_cond - eps_uncond)。也就是说,在条件采样的基础上,又额外加上了6倍的“条件与无条件之差”的方向。这不是插值,是外推——它放大了“条件”这一侧的信号强度。

生活化一点类比:条件采样好比一个人知道目的地,但走得小心翼翼;CFG w=7就是不仅知道目的地,还每一步都拼命提醒自己“我要往这个方向走,别偏”。模型会更有“主见”,生成的样本和条件描述贴合得更紧。

但外推是有代价的。你把一个方向放大了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~3,OELCFG类节点有特殊处理
LCM / Turbo等少步数模型1 ~ 3配合蒸馏模型时CFG通常很低

拿到一个新模型,我建议先按这个区间扫一遍,不要上来就用默认7。尤其是SDXL,很多人第一次用感觉“怎么颜色那么冲、对比度那么高”,十有八九是CFG还停在7没降下来。

3.2 不同采样器对CFG的敏感程度不一样

这个点很多新手会忽略:同样的CFG值,配不同采样器,效果可能天差地别。Euler、Heun这类一阶/高阶采样器对CFG的响应相对平稳;DPM++ 2M Karras这一类在CFG=7附近表现稳定;但到了SDE类采样器,比如DPM++ SDE Karras,它对CFG特别敏感,我实测下来CFG调到3~4反而更干净。

原因也不难理解。SDE采样器在每一步会注入额外的随机噪声,这个随机性和CFG的“外推放大”叠加在一起,容易产生高频噪声。你调低CFG,本质上是在给外推方向降权,压制了随机性的过度放大。所以以后在ComfyUI里换采样器的时候,别忘了重新检查CFG,别一个参数走天下。

3.3 步数、负面提示词和CFG的搭配经验

步数和CFG的关系也很有意思。扩散模型用DDIM这类采样器时,步数太少,模型还没完全收敛,CFG再高也救不回来,反而会把中间状态的伪影放大。我遇到过不少“步数设置的12,CFG拉到15,结果画面全是噪点”的情况,这不是模型不行,而是参数组合太激进。

常规做法是:步数20以下,CFG尽量控制在4~6;步数20~30,CFG可以放到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最好也考虑递减。有一次我做二次精修流程,第一步CFG=7,第二步还保持CFG=7,结果画面越修越硬,边缘出现明显伪影。后来把第二步CFG降到4,输出才自然起来。这其实就是“动态CFG”的朴素版本:越到后期,噪声越少,需要的引导越弱,CFG也该跟着递减。

4.4 CFG调成1和调成0到底有什么区别

这个问题我问过不少人,实测结果是这样的:CFG=1时,公式里eps_cond和eps_uncond的权重完全落在eps_cond上,模型退化为纯条件采样。CFG=0时,公式变成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行为压低了,你再用CFG=7去跑,很容易出现过曝。这也是为什么很多LCM工作流直接把CFG设为1到2。

蒸馏模型不需要大CFG,本质上是训练过程已经把“无条件与有条件之差”的引导信息压缩进了网络权重里,推理时就不用再外推那么狠。这个现象反过来验证了CFG的本质:它不是一个需要永远保持高值的参数,而是一个用来补偿模型条件表达能力不足的工具。模型对条件理解得越好,你需要的外推就越小。

5.3 动态CFG与自适应引导:我的下一步尝试

最近我在玩动态CFG的流程,思路是让cfg_scale随采样步数递减:前一半步数用CFG=8保证语义对齐,后一半步数降到CFG=3保住细节干爽度。实测下来,在同样的总步数下,动态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从来不是一个越大越好的参数,找到这个模型自己的“舒适区间”,才是真正意义上的条件对齐自由。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询