条件生成模型
DDPM的提出的扩散模型已经非常强大了,但短板也很明显,没有办法按照指令控制模型生成指定的图像。往往就是你训练过什么类别,它就只会生成什么类别的图片,模型的泛化性比较差。而Guidance技术则是引导扩散模型生成指定类别的图像。那么今天来讲解一下两种Guidance方法:
- Classifier guidance
- Classifier-free guidance
Classifier guidance
条件生成模型,指的是给定一个条件
y
y
y,我们根据这个条件生成对应的内容
x
x
x,其概率表示为
p
(
x
∣
y
)
p(x|y)
p(x∣y)。例如在文生图的场景中,
y
y
y是文本提示词,
x
x
x是对应的图片。根据贝叶斯公式,我们可以得到:
p
(
x
∣
y
)
=
p
(
y
∣
x
)
⋅
p
(
x
)
p
(
y
)
p(x|y) = \frac{p(y|x) \cdot p(x)}{p(y)}
p(x∣y)=p(y)p(y∣x)⋅p(x)
对其求梯度,有:
∇ x log p ( x ∣ y ) = ∇ x log ( p ( x ) ⋅ p ( y ∣ x ) p ( y ) ) = ∇ x ( log p ( x ) + log p ( y ∣ x ) − log p ( y ) ) \begin{aligned} \nabla_x \log p(x|y) &= \nabla_x \log(\frac{p(x) \cdot p{(y|x)}}{p(y)}) \\ & = \nabla_x (\log p(x) + \log p(y|x) - \log p(y)) \\ \end{aligned} ∇xlogp(x∣y)=∇xlog(p(y)p(x)⋅p(y∣x))=∇x(logp(x)+logp(y∣x)−logp(y))
我们解释一下这个公式,log的运算规律,乘法变加法,除法变减法。其中
p
(
y
)
p(y)
p(y)是某个类别的先验概率,是一个常数,其梯度为0,所以直接忽略。可得:
∇
x
p
(
x
∣
y
)
=
∇
x
log
p
(
x
)
⏟
u
n
c
o
n
d
i
t
i
o
n
a
l
s
c
o
r
e
+
∇
x
log
p
(
y
∣
x
)
⏟
c
l
a
s
s
i
f
i
e
r
g
r
a
d
i
e
n
t
(1)
\nabla_x p(x|y) = \underbrace{\nabla_x \log p(x)}_{\scriptstyle{unconditional\ score}} + \underbrace{\nabla_x \log p(y|x)}_{\scriptstyle{ \quad classifier\ gradient \quad}} \tag{1}
∇xp(x∣y)=unconditional score
∇xlogp(x)+classifier gradient
∇xlogp(y∣x)(1)
而
log
p
(
x
)
\log p(x)
logp(x)则是原本无条件生成的梯度,即从一个噪声图直接生成图像的过程。在SDE中,对其求梯度,也就是算score,这两者也是等价的。而
log
p
(
y
∣
x
)
\log p(y|x)
logp(y∣x)这一项就比较有趣了,已知生成的结果
x
x
x,求其是某种文本提示词的概率,那这不就是一个分类器吗?所以按照这个思路,我们用神经网络再训练一个分类器就可以了。作者在此基础上,又引入了一个控制参数
λ
\lambda
λ。
∇
x
p
λ
(
x
∣
y
)
=
∇
x
log
p
(
x
)
+
λ
∇
x
log
p
(
y
∣
x
)
(2)
\nabla_x p_\lambda(x|y) = \nabla_x \log p(x) + \lambda \nabla_x \log p(y|x) \tag{2}
∇xpλ(x∣y)=∇xlogp(x)+λ∇xlogp(y∣x)(2)
当
λ
=
0
\lambda = 0
λ=0,表示不加入任何条件。当
λ
\lambda
λ很大时,模型会产生大量附带条件信息的样本。
因此在向无条件生成模型引入条件时,我们只需要额外训练一个分类模型就可以了。这就是Classifier-Guidance。优点很明显,即**可以直接用别人训好的大数据集下的无条件生成模型,直接自己训练一个分类模型,就可以进行采样生成条件下的生成数据。**但是!这个推理过程并没有将分类模型限制在扩散模型上,所以也带来了一些问题。
我们知道在扩散模型中,模型的输入是随机噪声,然后通过逐时间片的随机去噪来生成一个真实的图像。而且Classifier Gudiance使用分类器的方式也非常“粗暴”,它是直接将分类器的梯度直接加到模型的梯度上。从公式上看,也没有限制加的时间点,那么问题就来了:
- 在扩散模型的靠前的step中,它生成的图像的噪声是非常多的,这个分类器能够根据噪声图像预测最终图像的类别,但这些噪声图像却没有足够的信息能够让分类模型准确的预测它的类别。所以这个分类器很难训练,训练成本高。
- 因为噪声图中包含的信息不足以正确训练分类器,因此分类器得到的参数,不一定是图像 x x x到文本 y y y的正确映射。这个错误的梯度加到生成模型上,那么训练结果反而偏离目标,直接体现的结果就是模型生成的结果不对或者没有按照文本提示生成指定的类别。这也就是Classifier-free Guidance论文中所说的对抗攻击(adversarial attack)。
- 分类器的类别毕竟是有限集,不能涵盖全部情况,对于没有覆盖的标签类别会很不友好。而且采样的时候,需要用到两个模型,采样效率比较低。
Classifier-free guidance
面对上述Classifier Guidance的种种缺点,Classifier-free Guidance技术应运而生。
首先要提及的一点是:Classifier-Free Guidance 是一种用于扩散模型(Diffusion Models)的推理技术,而不是训练技术。它的目的是在推理阶段(生成阶段)提高生成样本的质量。但是需要注意的是其效果依赖于训练阶段的特定设置。也就代表,采用这个方法,在训练Diffusion Models的时候就需要考虑。
下面,我们从数学角度理解一下,对分类器使用贝叶斯公式:
p
(
y
∣
x
)
=
p
(
x
∣
y
)
⋅
p
(
y
)
p
(
x
)
p(y|x) = \frac{p(x|y) \cdot p(y)}{p(x)}
p(y∣x)=p(x)p(x∣y)⋅p(y)
同样对其求梯度,根据log的运算规律,乘法变加法,除法变减法。
∇
x
log
p
(
y
∣
x
)
=
∇
x
(
log
p
(
x
∣
y
)
+
log
p
(
y
)
−
log
p
(
x
)
)
∇
x
log
p
(
y
∣
x
)
=
∇
x
log
p
(
x
∣
y
)
−
∇
x
log
p
(
x
)
(3)
\begin{aligned} \nabla_x \log p(y|x) &= \nabla_x(\log p(x|y) + \log p(y) - \log p(x)) \\ \nabla_x \log p (y|x) &= \nabla_x \log p(x|y) - \nabla_x \log p(x) \end{aligned} \tag{3}
∇xlogp(y∣x)∇xlogp(y∣x)=∇x(logp(x∣y)+logp(y)−logp(x))=∇xlogp(x∣y)−∇xlogp(x)(3)
把式子(3)代入到公式(2)中可得:
∇
x
p
λ
(
x
∣
y
)
=
∇
x
log
p
(
x
)
+
λ
(
∇
x
log
p
(
x
∣
y
)
−
∇
x
log
p
(
x
)
)
=
(
1
−
λ
)
∇
x
log
p
(
x
)
⏟
u
n
c
o
n
d
i
t
i
o
n
a
l
s
c
o
r
e
+
λ
∇
x
log
p
(
x
∣
y
)
⏟
c
o
n
d
i
t
i
o
n
a
l
s
c
o
r
e
(4)
\begin{aligned} \nabla_x p_\lambda(x|y) &= \nabla_x \log p(x) + \lambda (\nabla_x \log p(x|y) - \nabla_x \log p(x)) \\ & = \underbrace{(1-\lambda) \nabla_x \log p(x)}_{\scriptstyle{unconditional\ score}} + \underbrace{\lambda \nabla_x \log p(x|y)}_{\scriptstyle{conditional\ score}} \end{aligned} \tag{4}
∇xpλ(x∣y)=∇xlogp(x)+λ(∇xlogp(x∣y)−∇xlogp(x))=unconditional score
(1−λ)∇xlogp(x)+conditional score
λ∇xlogp(x∣y)(4)
从式(4)我们可以看出,Classifier-free Guidance其实就是条件得分(conditional score)和无条件得分(unconditional score)线性组合。
此时我们来考虑一下,不同 λ \lambda λ取值时的情况:
- 当 λ = 0 \lambda = 0 λ=0时,第二项完全为0,会忽略条件;
- 当 λ = 1 \lambda = 1 λ=1时,完全使用第二项,就是有附带条件情况下的的生成;
- 当 λ > 1 \lambda > 1 λ>1时,模型会优先考虑条件生成样本,并且远离无条件生成,更明确使用有条件生成;
因为Classifier-free Guidance向无条件的扩散模型中添加了一个条件信息,但是又没有像Classifier Gudiance那样添加一个显式的分类器。虽然Classifier-free Guidance的论文读起来很困难,但是它的实现方式却非常简单。
clip_model = ...
text = "apple"
uncond_embeddings = clip_model.text_encode("") # 空文本编码(unconditional score)
cond_embeddings = clip_model.text_encode(text) # 条件文本编码(conditional score)
text_embeddings = torch.cat(uncond_embeddings, cond_embeddings) # 把它俩 concate 到一起作为控制条件
input = get_noise(...) # 从高斯分布随机取一个跟输出图像一样 shape 的噪声图
for t in tqdm(scheduler.timesteps):
with torch.no_grad():
# 这里同时预测出了空文本编码的和条件文本编码的图像噪声
noise_pred = unet(input, t, encoder_hidden_states=text_embeddings).sample
# Classifier-Free Guidance 引导
noise_pred_uncond, noise_pred_cond = noise_pred.chunk(2) # 拆成无条件和有条件的噪声
# 把【“无条件噪声”指向“有条件噪声”】看做一个向量,根据 guidance_scale 的值放大这个向量
# 当 guidance_scale = 1 时,下面这个式子退化成 noise_pred_cond
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_cond - noise_pred_uncond)
# 用预测出的 noise_pred 和 x_t 计算得到 x_t-1
latents = scheduler.step(noise_pred, t, latents).prev_sample
图片来源为知乎作者蓟梗
guidance scale是一个放缩系数,这个值越大,生成的结果越倾向于输入条件,多样性会下降。越小,多样性越大。通常来讲这个值被设为 7.5 比较合适。在ComfyUI和WebUI中就是名为cfg的超参数。