🏠 总目录📚 本教程 06 · 条件控制与 CFG ← →
📑 本页目录(点开跳转)

06 · 条件控制:文本与 CFG

⏱ 22 分钟 | ⭐ 你在任何 UI 上拖的那个 guidance scale,这一页讲清它在算什么


🎯 一句话

提示词不是一次性的「输入」,而是每一步去噪时都在旁边说话的那个人;而 guidance scale 更奇怪 —— 它让模型同一步跑两次,一次听提示词、一次装作没听见,再朝两者的差外推。 外推多远,就是那个滑块。

前面几章的去噪循环里没出现过一个字的提示词,跑完得到的是「训练分布里的某一张图」—— 哪一张,随缘。这一章把提示词接进去。


📝 一、文本先变成一串向量

第一步和扩散无关:把提示词交给一个已经训好、并且冻住的文本编码器(Stable Diffusion 1.x 用 CLIP 的文本塔)。

⭐ 它吐出来的不是一个向量,是一串。 CLIP ViT-L/14 固定输出 77 个位置 × 768 维,不足补齐、超了截断 —— 早期「提示词写太长后面不起作用」的传说,根就在这个 77 上。

为什么必须是一串?因为下一步要做对齐:「戴红帽子的猫」里「红」该影响帽子那片像素、不该影响爪子;压成一个向量这信息就没了。


🔀 二、交叉注意力:每个位置自己决定听哪个词

条件要在每一层、每一步都参与,靠的是交叉注意力。

每个图像位置自己决定听哪个词图像特征(Q)h × w 个位置文本(K / V)77 个 token 位打分矩阵(h·w) × 77每行一个图像位置,每列一个词⭐ 每一层都有、每一步都重算 —— 条件不是起点,是全程偏置
看矩阵的形状:行是图像位置、列是词。⭐ 所以「听哪个词」是每个位置各自算的,不是整张图共用一个答案 —— 这就是提示词能控制局部的机制。
从哪来 有多少
Q(我想找什么) 当前图像特征,每个空间位置一个 h × w 个
K / V(能提供什么) 文本编码器的输出 77 个

打分矩阵因此是 (h·w) × 77:每个图像位置对 77 个文本位置各打一个分,softmax 成权重后把 V 加权取回 —— 所以「帽子」那片特征会大量取走「红」,爪子那片不会。

⭐ 「提示词塞在哪一层」的答案是:哪一层都塞了,条件不是起点而是全程施加的偏置。⚠️ 时间步 t 不走这条路,它和主干结构一起放在下一章。


🎚️ 三、CFG:同一步跑两次

光靠交叉注意力,服从度往往不够。CFG(classifier-free guidance)在采样时动手:带提示词跑一次得 ε_cond,带空提示词再跑一次得 ε_uncond,然后合成:

CFG 不是在两者之间插值,是沿着方向外推ε_uncond不带条件ε_cond带条件s = 15过饱和、构图崩s = 7.5常用区间s 越大走得越远s = 1 停在这里实测:s=1 范数 15.81(训练尺度的 0.99 倍)→ s=15 涨到 91.65(5.73 倍)⚠️ s 太大就合成出了模型训练时从没见过的 ε
看 s=1 的位置:它就停在带条件那一点上,等于没引导。⭐ 再往右全是外推而不是插值 —— 所以调大 s 不是「更接近条件」,是越过它,越过太多就出了训练分布。

$$\tilde{\varepsilon} = \varepsilon_{\text{uncond}} + s \cdot (\varepsilon_{\text{cond}} - \varepsilon_{\text{uncond}})$$

💡 人话:括号里是「有提示词和没提示词,预测差在哪」,也就是条件把画面往哪拽;s 决定沿这个方向走多远。

整理成 (1−s)·ε_uncond + s·ε_cond 更能看出问题:⭐ s = 1 时它就等于 ε_cond,即完全不做引导;s > 1 时 ε_uncond 的系数变负 —— 这不是插值,是越过 ε_cond 往外推。UI 上默认的 7.5,就是沿条件方向走出那么远。

👀 名字里的 free 指什么(可跳过)

前身 classifier guidance 要另训一个看得懂带噪图的分类器;CFG 改成训练时以约 10% 的概率把条件丢掉,同一个网络就兼职学会了无条件预测,不需要额外分类器。

⭐ 另外:「空提示词」不是全零张量,是把空字符串真送进文本编码器得到的那串向量。把它换成负面提示词,那一支就成了「往反面推」—— 负面提示词的全部机制就在这里。


📉 四、s 调大之后坏在哪

用两个假的预测张量,看合成那一行把结果放大成什么样:

# CFG 合成出来的 ε,随 s 被放大成什么样
import torch

torch.manual_seed(0)
D = 256                                # 假装是展平的潜变量
eps_uncond = torch.randn(D)            # 空提示词那次前向的输出
eps_cond = eps_uncond + 0.35 * torch.randn(D)   # 带条件那次
base = D ** 0.5                        # 训练时 ε~N(0,I),范数期望 sqrt(256)=16

print(f"训练时见过的 ε:范数约 {base:.1f},几乎没有分量超过 3")
for s in (1.0, 3.0, 7.5, 15.0):
    eps = eps_uncond + s * (eps_cond - eps_uncond)   # ⭐ 就是 CFG 那一行
    n = eps.norm().item()
    print(f"s={s:4.1f}  范数 {n:6.2f}(训练尺度的 {n/base:.2f} 倍)"
          f"  越过 3 的分量 {int((eps.abs() > 3).sum()):3d} 个")
训练时见过的 ε:范数约 16.0,几乎没有分量超过 3
s= 1.0  范数  15.81(训练尺度的 0.99 倍)  越过 3 的分量   0 个
s= 3.0  范数  22.84(训练尺度的 1.43 倍)  越过 3 的分量   9 个
s= 7.5  范数  47.13(训练尺度的 2.95 倍)  越过 3 的分量  70 个
s=15.0  范数  91.65(训练尺度的 5.73 倍)  越过 3 的分量 152 个

⭐ 关键不是「变大了」,是「变成了模型没见过的东西」:s=7.5 合成出的范数已是训练尺度的 2.95 倍、70 个分量越过 3,s=15 是 5.73 倍 —— 而每一步都要减掉它,误差还逐步累积。

落到图上是三个症状:过饱和(颜色发死、对比拉爆)、构图崩坏(结构变形、纹理反复)、⚠️ 多样性塌缩(换十个种子出十张越来越像的图)。第三条最容易漏掉,因为单看一张图发现不了。

⭐ 「调大一点更听话」只在一段区间里成立,过了那段换来的是一张更响亮的坏图。


💰 五、代价:每步计算量翻倍

CFG 每步跑两次前向,50 步就是 100 次网络调用 —— 不是常数开销,是乘 2。实现上一般把两次拼成 batch=2 一起送:权重只从显存搬一次,省的是访存;乘加实实在在两份,算力那份省不掉。

⭐ 于是很多「加速采样」的比较是不公平的:有的把「一步」算成一次网络调用,有的算成一次去噪步(=两次调用)。看到「20 步出图」先问是 20 次还是 40 次。


🌡️ 六、它和 temperature 是同一类旋钮

语言模型的 temperature 扩散的 s
在调什么 采样分布的尖锐程度 条件方向被外推多远
更听话往哪调 ⚠️ 调小 ⚠️ 调大(方向相反,别记混)
走极端 复读、干瘪 过饱和、构图崩、多样性塌缩
额外算力代价 没有 ⚠️ 每步两次前向

共同点才是重点:都在「多听话」和「多自由」之间取舍,都没有安全默认值,都得按任务调。

⚠️ 这是类比不是等同:temperature 改的是分布形状,s 改的是预测向量的方向和长度,可迁移的只有那个权衡结构。但正因结构一样,那边的规矩这边也成立:别两个旋钮一起动,出问题先调回默认。


🔗 这一章连到哪里

去哪 为什么
大模型全景导论 06c · 解码参数怎么调 ⭐⭐ 上一节那个类比的另一半:它把 temperature 的数学摊开了(logits 差 2.0 时 T=0.5 把概率比放大到 54.6 倍),并给了一条这里也适用的规矩 —— 和 top_p 只调一个
大模型全景导论 主线 2 · 模型怎样看懂一句话 交叉注意力的底子:那页把 Q/K/V 讲成三个角色(我想找什么/能被怎样匹配/能提供什么内容),本章只是把它们拆到两个东西上
大模型全景导论 10d · CLIP 与对比学习 那个「已训好并冻住」的文本编码器是怎么来的:几亿对(图片,描述)用 InfoNCE 训,一个 batch 的 N 对编成 N×N 相似度矩阵、要求对角线最大
大模型全景导论 06 · 推理优化:省显存 「计算量翻倍」这类账在语言模型那边怎么算:核心是瓶颈分两种(算力密集 vs 访存密集),正是「batch=2 省搬权重、不省乘加」背后的判据

✅ 检查点

  1. 文本编码器输出的是一个向量还是一串?为什么这个区别关键?
  2. 交叉注意力里 Q、K、V 各来自哪边?打分矩阵是什么形状?
  3. CFG 的合成公式是什么?s = 1 时会发生什么?为什么说 s > 1 是外推不是插值?
  4. s 调得很大会坏成什么样?其中哪一条单看一张图发现不了?
  5. CFG 的算力代价是多少?拼成 batch=2 之后省掉的是什么、没省掉的是什么?
👀 答案
  1. 是一串:CLIP ViT-L/14 固定输出 77 × 768。因为交叉注意力要做对齐 —— 「红」要影响帽子那片像素、不影响爪子。
  2. Q 来自图像特征(每个空间位置一个,共 h×w 个),K/V 来自文本编码器输出(77 个)。打分矩阵是 (h·w) × 77。
  3. ε = ε_uncond + s·(ε_cond − ε_uncond),s = 1 时等于 ε_cond,即完全不引导。整理成 (1−s)·ε_uncond + s·ε_cond 就看得出 s > 1 时前一项系数是负数,结果落在 ε_cond 之外。
  4. 过饱和、构图崩坏、多样性塌缩,第三条单看一张图发现不了。数值上 s=7.5 合成出的范数是训练尺度的 2.95 倍、70 个分量越过 3。
  5. 翻倍(50 步 = 100 次调用)。省掉的是访存,乘加那份省不掉。

🛑 可以停在这里

你已经能解释一张图怎么「听懂」提示词:文本编码器 → 每层每步的交叉注意力 → 采样时的两次前向外推。⚠️ 什么时候回来:图颜色发死、结构反复、换种子也总是那几张 —— 先把 s 调回默认再查别的。

⚡ 走神救援

先记住这几件事

下一节 👉 07-去噪主干-UNet与DiT.md

打卡记录保存在你的浏览器里,首页能看到总进度