生图条件控制算法
frc99 中本葱

本文主要介绍基于Stable Diffusion下的对生图进行条件控制的几种常用算法。

1 ControlNet

1.1 UNet

在介绍ControlNet之前首先介绍一下Stable Diffusion的基础网络UNet,UNet网络结构分为上采样和下采样,上采样是编码,下采样是解码。输入一张64x64的潜空间图片,通过下采样逐渐缩减尺寸,提取图片更高维度的特征,这将有利于捕捉图片的语义特征,下采样则是逐渐恢复成原图的尺寸。同时,为了防止信息的丢失,通过跳跃连接上下采样,上采样每一层,都在拼接了左边下采样对应层的数据后,再一起作为下一层上采样的输入。
unet

1.2 ControlNet

基础的生图通常不能满足实际的需求,ControlNet作为一种控制图像生成的神经网络结构,通过添加额外的引导图片输入(如边缘图、姿态图等)来控制Stable Diffusion模型的扩散生成方向,实现对图像生成过程的精确引导。下图依次是scribble、normalmap、depth、canny、mlsd、lineart、segmentation、openpose的效果,从左往右,从上往下。
效果
那么,ControlNet是如何与SD的UNet结合的呢?ControlNet拷贝原始UNet Encoder和Middle块,再用零卷积(zero convolution)连接到原 SD 网络。零卷积是 1×1卷积,并且权重和偏置都初始化为零,这样在开始训练ControlNet之前,所有zero convolution模块的输出都为零,使得ControlNet完完全全就在原模型的能力上进行微调训练。
ControlNet
详细来说,ControlNet block 的输出会和对应的 Unet Encoder(Middle) block 的输出元素加在一起, 然后通过跳跃连接输入给对应的 Unet Decoder block 。注意,这个 ControlNet 的 block 输出和 Unet Encoder(Middle) block 输出加一起的结果, 并不会向下传递给下一层的 Unet Encoder(Middle) block,只会通过跳跃连接输入给对应的 Unet Decoder block, 所以增加了 ControlNet 并不影响原来 Unet 的网络结构。

在推理中,ControlNet代码实现逻辑:

1、Control计算:初始化一个total_controlnet_embedding = [0.0] * 13,13个块,预处理图片将control增量信息存入context,加载control_model后计算control,将control计算结果嵌入total_controlnet_embedding。
2、UNet计算:会有一个栈hs,每计算完一个encoder block将结果依次入栈hs,middle block和controlnet对应块(total_controlnet_embedding.pop())相加(不是张量拼接),在计算decoder block时,将栈hs中的值依次弹出,和controlnet的结果相加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# U-Net Encoder
hs = []
with th.no_grad():
t_emb = cond_cast_unet(timestep_embedding(timesteps, self.model_channels, repeat_only=False))
emb = self.time_embed(t_emb)

if is_sdxl:
assert y.shape[0] == x.shape[0]
emb = emb + self.label_emb(y)

h = x
for i, module in enumerate(self.input_blocks):
self.current_h_shape = (h.shape[0], h.shape[1], h.shape[2], h.shape[3])
h = module(h, emb, context)

t2i_injection = [3, 5, 8] if is_sdxl else [2, 5, 8, 11]

if i in t2i_injection:
h = aligned_adding(h, total_t2i_adapter_embedding.pop(0), require_inpaint_hijack)

hs.append(h)

self.current_h_shape = (h.shape[0], h.shape[1], h.shape[2], h.shape[3])
h = self.middle_block(h, emb, context)

# U-Net Middle Block
h = aligned_adding(h, total_controlnet_embedding.pop(), require_inpaint_hijack)

if len(total_t2i_adapter_embedding) > 0 and is_sdxl:
h = aligned_adding(h, total_t2i_adapter_embedding.pop(0), require_inpaint_hijack)

# U-Net Decoder
for i, module in enumerate(self.output_blocks):
self.current_h_shape = (h.shape[0], h.shape[1], h.shape[2], h.shape[3])
h = th.cat([h, aligned_adding(hs.pop(), total_controlnet_embedding.pop(), require_inpaint_hijack)], dim=1)
h = module(h, emb, context)

# U-Net Output
h = h.type(x.dtype)
h = self.out(h)

因为复制了一半的UNet权重,通常ControlNet的模型也会比较大(1.5G左右),因此会增加模型加载和执行推理的时间。

2 IPAdapter

以往要想实现一个具体的设计风格,需要针对性的训练lora,背后涉及训练素材的搜集、打标、模型训练、效果检验,而通过IP-Adapter可以快速直观得生成效果图。
垫图神器IPAdapter提供整图风格迁移能力,与直接图生图有本质区别,原理上图生图是在原图加噪点基础上做演化生成,IPAdapter 是让模型认识图片风格要素,生成跟原图宏观风格一致的图片。
效果
IPAdapter同时也专门针对换脸做了一些效果优化:

  • 用 InsightFace 提取人脸特征
  • 人脸特征不像图像特征那么容易学习,因此这个模型配套训练了一个 LoRA 提高学习效果。
  • 仅使用人脸特征的话模型生成结果会不稳定,受 Prompt 的影响很大,因此 IPAdapter-FaceID-Plus版本尝试将人脸特征和 CLIP 编码的图像特征结合起来。
    FaceID
    IPAdapter架构:
    IPAdapter
    主要有以下两大改进:
  • 特征处理:通过由Linear layer 和 Layer Normalization组成的投影网络结构,将图像特征投影到长度4的特征序列中,进入交叉注意力网络。
  • 解耦交叉注意力(decoupled cross-attention):
    • 原本Stable Diffusion是将图片特征和文字特征相加合成一个向量输入UNet,现在将文本特征的Cross-Attention 和图像特征的Cross-Attention区分开来,在Unet的模块中新增了一路Cross-Attention模块,用于引入图像特征。这样图像特征可以更好得被保留下来,从而实现对图像特征更显性的继承和保留。prompt还是最关键的,只是强化了参考图的引导作用。

关于交叉注意力解耦,下图有更直观的表示,其中λ是控制图片embeding的影响权重。
交叉注意力解耦
ps:在Stable Diffusion WebUI中要求InstantID必须在IPAdapter后使用。

3 InstantID

小红书出品的InstantID相较于IPAdapter更侧重于人脸迁移,效果也更好。
InstantID
InstantID架构:
InstantID
有以下三处改进:

  • 用 InsightFace 提取人脸特征,通过投影(Projection)使得人脸特征和文本的特征空间具有一致的向量表示。
  • 解耦交叉注意力层:这一点和IPAdpater一致。
  • IdentifyNet:类似于提取人脸特征的ControlNet,同样是复制了上采样部分。
    • 输入:只使用五个面部关键点(两个用于眼睛,一个用于鼻子,两个用于嘴巴)而不是细粒度的 OpenPose (ControlNet)面部关键点,防止强调多余的面部特性,其它面部特征使用prompt去控制
    • 没有加入文本Prompt进行训练,只用人脸信息作为ControlNet 中交叉注意力层的条件,主要是希望这个网络只控制人脸,不受文本对人脸描述的影响。

4 T2IAdapter

另一种和ControlNet思想相似的条件控制算法T2IAdapter
T2IAdapter
同样也是一种条件控制策略,T2I冻结了UNet,只需要对适配器进行微调,它可以任意组合多个条件的适配器。每种 Adapter 对应一类 condition,如线稿图、深度图、骨架图等,多个 Adapter 可以叠加使用,通过Adapter得到的特征再加到原 UNet 下采样的各个层。
T2IAdapter
适配器Adapter:

  • 输入引导图像512X512,会通过pixel unshuffle下采样到64X64
  • 分别通过4个特征提取块和3个下采样块以获取多尺度特征,特征提取块由 Conv + residual blocks 组成。

与ControlNet的区别:

  • ControlNet的运行计算成本很高。这是因为在反向扩散过程的每个去噪步骤中,都需要运行 ControlNet 和 UNet。此外,ControlNet 复制一半的UNet 编码器,从而导致参数数量较大。因此,生成的瓶颈在于 ControlNet 的大小(越大,过程越慢),相比较而言,T2IAdapters体积更小。而且与 ControlNets 不同的是,由于忽略了关键的时间编码t,T2I-Adapters 可以在整个去噪过程中仅运行一次。
  • ControlNet的计算结果是加在Decoder上的,而T2IAdapter的计算结果是加载Encoder上的。这样设计可能的原因是,ControlNet由于训练数据量更大更完善,所以语义信息更强,不需要再通过UNet的编码器去获取更多的编码特征。
Model Type Model Parameters Storage (fp16)
ControlNet-SDXL 1251 M 2.5 GB
ControlLoRA (with rank 128) 197.78 M (84.19% reduction) 396 MB (84.53% reduction)
T2I-Adapter-SDXL 79 M (93.69% reduction) 158 MB (94% reduction)
由 Hexo 驱动 & 主题 Keep