Classifier Guidance 与 Classifier-Free Guidance

Classifier Guidance 与 Classifier-Free Guidance

DDPM 终于把 diffusion 模型做 work 了,但无条件的生成在现实中应用场景不多,我们终归还是要可控的图像生成。本文简要介绍两篇关于 diffusion 模型可控生成的工作。其中 Classifier-Free Guidance 的方法还是现在多数条件生成 diffusion 模型的主流思路。

Classifier Guidance: Diffusion Models Beat GANs on Image Synthesis

Classifier-Free Guidance: Classifier-Free Diffusion Guidance

Classifier Guidance

要做可控生成,即条件生成,首先想到我们可以拿类别来作为条件,比如要指定类别猫,就生成猫的图片。也就是说要给定类别 y y y,生成图片 x x x,即 P ( x ∣ y ) P(x|y) P(xy) 。而一般分类器做的事情正好是反过来,给定图片,预测类别,即 P ( y ∣ x ) P(y|x) P(yx) 。这刚好是一对逆条件概率,应该敏锐地想到贝叶斯公式就是处理这类逆概率问题的。推导如下:
∇ log ⁡ P ( x ∣ y ) = ∇ log ⁡ P ( x ) P ( y ∣ x ) P ( y ) = ∇ log ⁡ P ( x ) + ∇ log ⁡ P ( y ∣ x ) − ∇ log ⁡ P ( y ) = ∇ log ⁡ P ( x ) + ∇ log ⁡ P ( y ∣ x ) \begin{aligned} \nabla\log P(x|y)&=\nabla\log\frac{P(x)P(y|x)}{P(y)} \\ &=\nabla\log P(x)+\nabla\log P(y|x)-\nabla\log P(y) \\ &=\nabla\log P(x)+\nabla\log P(y|x) \end{aligned} logP(xy)=logP(y)P(x)P(yx)=logP(x)+logP(yx)logP(y)=logP(x)+logP(yx)
其中 P ( y ) P(y) P(y) 是某个类别的先验概率,是一个常数,其梯度为 0,故直接丢掉。这里的 ∇ P ( x ) \nabla P(x) P(x) 实际就是 score-base model 中所谓的 score,score-based model 实际可以看作是 diffusion model 的另一种形式,这里不展开。

在结果中,第一项 ∇ log ⁡ P ( x ) \nabla\log P(x) logP(x) 就是原本无条件生成的梯度,而第二项 ∇ P ( y ∣ x ) \nabla P(y|x) P(yx) 则相当于是分类器进行图形分类的梯度。也就是说,我们只要在无条件生成的基础上,加上想要的类别的分类器梯度,作为引导(或者称为条件的梯度修正),就可以导出以类别作为条件的生成。

推导看起来并不复杂,具体怎么实现呢?怎么在生成的时候加入分类器的梯度作为引导呢?这里我们参考 OpenAI 原 Classifier Guidance 给出的代码来理解:

# https://github.com/openai/guided-diffusion/blob/main/scripts/classifier_sample.py#L54
# 核心就是这里的cond_fn函数

import torch as th
import torch.nn.functional as F
classifier = ... # 加载一个(噪声)图像分类器

def cond_fn(x, t, y=None):
    assert y is not None
    with th.enable_grad():
        x_in = x.detach().requires_grad_(True)
        logits = classifier(x_in, t)
        log_probs = F.log_softmax(logits, dim=-1)
        selected = log_probs[range(len(logits)), y.view(-1)]
        return th.autograd.grad(selected.sum(), x_in)[0] * args.classifier_scale

这里的 t 是当前时间步,x 是当前步的去噪结果图,y 是类别索引。我们看到,计算分类器梯度的过程其实很简单:

  1. 首先把 x 和原始的梯度断开(detach),准备计算分类器的梯度
  2. 把 x_in 和 t 都输入到分类其中,得到分类器预测的类别 logits
    • 注意,这里的分类器实际上需要是一个能够分类带噪声图像的分类器,不仅需要输入图像,还要输入当前时间步 t,相当于告知分类器当前噪声的强度。所以说,在 Classifier Guidance 的方法中,我们虽然不需要重新训练 diffusion 模型,但是我们需要单独训练一个噪声图像分类器。
  3. 再把预测的类别 logits 过一下 softmax,得到各类别的概率 log_probs
  4. 从 log_probs 中取出我们指定的类别 y 对应的概率,即 selected
  5. 最后将 selected 中各个目标类别的概率值加在一起,希望该值越大越好,取该值对于 x 的梯度,即为分类器引导的梯度。

Classifier-Free Guidance

Classifier Guidance 的方法虽然不需要重新训练 diffusion 模型,但是需要额外的训练一个噪声图像分类器,并且在采样时需要额外的梯度引导。最关键的是,其作为可控生成的方法,对结果的控制能力十分有限,仅能够支持分类器所认识的有限类别,这无疑是不能满足我们多种多样的使用需求的。我们想要的肯定是 zeroshot 的,能够直接理解自然语言的可控生成。所幸在 CLIP 之后,图像文本特征已经能够在一定程度上对齐,CV 各个方向都基于 CLIP 实现了 zeroshot / open-vocab,图像生成也不例外。

Classifier-Free Guidance 的方法训练额外的分类器,并且,可以实现各种条件的引导生成。以最火爆的文生图为例,只要结合 CLIP 文本编码器提取 prompt 的文本特征 embedding,输入到 diffusion 模型中作为条件,即可实现。目前,Classifier-Free Guidance 已经成为条件生成的主流思路。

Classifier-Free Guidance 的想法是这样的:同时训练无条件生成模型和条件生成模型(实际上这俩是一个模型,只是训练时有概率输入是有条件的,有概率是无条件的),在推理时,同时 forward 带输入条件的生成和无条件的生成吗,然后把俩结果进行线性组合外推,得到最终的条件生成结果。

直接来看一下伪代码(参考 diffusers 的 API):

unet = ... # 加载unet去噪模型
clip_model = ...  # 加载CLIP模型

text = "a cat"  # 文本条件
text_embeddings = clip_model.text_encode(text)  # 编码条件文本,cond
empty_embeddings = clip_model.text_encode("")  # 编码空文本,uncond
text_embeddings = torch.cat(empty_embeddings, text_embeddings)  # concat到一起,只做一次forward

input = torch.randn((1, 3, sample_size, sample_size), device="cuda") # 采样初始噪声

for t in scheduler.timesteps:
    # 用 unet 推理,预测噪声
    with torch.no_grad():
        # 这里同时预测出了有文本的和空文本的图像噪声
        noise_pred = unet(input, t, encoder_hidden_states=text_embeddings).sample

    noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)  # 拆成无条件和有条件的噪声
    
		# Classifier-Free Guidance 引导 
    noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)

    # 用预测出的 noise_pred 和 x_t 计算得到 x_t-1
    latents = scheduler.step(noise_pred, t, latents).prev_sample

代码里的写法是 ϵ ˉ = ϵ u + s ( ϵ c − ϵ u ) \bar\epsilon=\epsilon_u+s(\epsilon_c-\epsilon_u) ϵˉ=ϵu+s(ϵcϵu), 论文里的公式是 ϵ ˉ = ( 1 + w ) ϵ c − w ϵ u \bar{\epsilon}=(1+w)\epsilon_c-w\epsilon_u ϵˉ=(1+w)ϵcwϵu,二者是等价的,只是做了下变换 s = 1 + w s=1+w s=1+w。个人感觉代码里这个形式更好理解一点: ϵ c − ϵ w \epsilon_c-\epsilon_w ϵcϵw 表示从无条件到目标条件的一个方向, w w w 是多大程度上考虑条件的系数,在无条件 ϵ u \epsilon_u ϵu 的基础上,再朝目标类别移动一定距离,即: ϵ ˉ = ϵ u + s ( ϵ c − ϵ u ) \bar\epsilon=\epsilon_u+s(\epsilon_c-\epsilon_u) ϵˉ=ϵu+s(ϵcϵu)。(也可能是作者大佬的思路我没领悟到

Classifier-Free Guidance 的做法看起来并不复杂,但有几个问题值得讨论(笔者自己也很不明白,希望有大佬指点一下):

  1. 为什么不像 cvae 一样直接把 embedding 丢进去做条件生成,而是非要同时训练无条件生成的情况,再做一个线性作何外推呢?
    • 可能是因为采样时现需要有一个无条件的基准,然后像目标条件的方向再修正?
  2. 关于空门大佬提到的 Classifier-Free Guidance 破坏了 Neural Diffusion Operator 的准线性性质。
    • 提到在 w w w 很大时,采样结果会崩掉,实践中确实这种现象。但笔者目前尚在学习,还无法完全理解。贴一下大佬的文章链接:Classifer-free Guidance 是万恶之源

这些问题有大佬了解,可以指点一下,或者介绍下应该去补充哪些理论知识来深化理解,感激不尽。

  • 28
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
classifier-free diffusion guidance(无分类器扩散引导)是一种新兴的技术,用于在无需提前训练分类器的情况下进行目标导航。 传统的目标导航技术通常需要使用先验知识和已经训练好的分类器来辨别和识别目标。然而,这些方法存在许多限制和缺点,如对精确的先验知识的需求以及对大量标记数据的依赖。 相比之下,classifier-free diffusion guidance 可以在目标未知的情况下进行导航,避免了先验知识和训练好的分类器的依赖。它的主要思想是利用传感器和环境反馈信息,通过推测和逐步调整来实现导航。 在这种方法中,机器人通过感知环境中的信息,例如物体的形状、颜色、纹理等特征,获取关于目标位置的信息。然后,它将这些信息与先验的环境模型进行比较,并尝试找到与目标最相似的区域。 为了进一步提高导航的准确性,机器人还可以利用扩散算法来调整自己的位置和方向。通过比较当前位置的特征与目标位置的特征,机器人可以根据这些差异进行调整,逐渐接近目标。 需要注意的是,classifier-free diffusion guidance还处于研究阶段,目前还存在许多挑战和问题。例如,对于复杂的环境和多个目标,算法的性能可能会下降。然而,随着技术的发展,我们可以预见classifier-free diffusion guidance将会在未来的目标导航中发挥重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值