0%

使用diffusers训练定制化场景的controlnet

controlnet模型结构分析

diffusers中的controlnet训练pipeline

diffusers相关介绍

diffusers的设计逻辑是以可用性,可迁移性强为目标设计的,并不保证最高效的训练或推理效率。因此精度一般用float32,对于其他库的依赖也尽量做到精简。

Pipelines

Pipeline只和推理有关。在pipeline里所有的子类流水线都继承自DiffusionPipeline,如果子类的推理需要lora,adapter等定制化模块,则会继承相关的Mixin类。所有的pipeline的推理都是直接调用call函数。
Controlnet推理时载入的模块中,首先是正常的sd推理时需要的tokenizer,text encoder,unet,vae,然后是controlnet给unet提供额外的降噪信息注入。注意:如果使用了多个controlnet,最后输出的结果将是所有的controlnet直接add到一起输出成一个denosing的条件。scheduler主要是用来规划unet的降噪过程。
以下是pipeline_controlnet在推理过程中的伪代码,省略了一部分额外参数和额外插件,只保留主要的推理loop。每个step里image和latent先进入controlnet得到image对应的guidance

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
# 得到prompt经过Tokenizer和text encoder后对应的embedding
prompt_embeds, negative_prompt_embeds = self.encode_prompt(
prompt,
num_images_per_prompt,
negative_prompt,
prompt_embeds=prompt_embeds, # 如果有
negative_prompt_embeds=negative_prompt_embeds,# 如有
clip_skip=self.clip_skip,
)
for i, t in enumerate(timesteps):
# 先过control得到输入img相关的guidance
down_block_res_samples, mid_block_res_sample = self.controlnet(
latents,
t,
encoder_hidden_states=controlnet_prompt_embeds,
controlnet_cond=image,
conditioning_scale=cond_scale,
)
# 预测噪声
noise_pred = self.unet(
latent_model_input,
t,
encoder_hidden_states=prompt_embeds, # 语义guide
down_block_additional_residuals=down_block_res_samples, # controlnet的guide
mid_block_additional_residual=mid_block_res_sample,
)[0]
# 计算x_t到x_t-1之间的噪声样本
latents = self.scheduler.step(noise_pred, t, latents)[0]

# 经过vae可视化
image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False, generator=generator)
# 后续可以经过一个safechecker来过滤调nsfw内容,checker的主要逻辑是利用clip来提取特征检查是不是nsfw。

Models

SD中是把高维度的照片使用类似VQA的方法降维到例如6464的低维度latent,一方面可以提取后的latents特征更抽象,而文本特征也属于高纬度的抽象特征,所以猜测这样的操作也有利于将文本的guidance,另一方面也可以减少计算量。
controlnet为了和SD对齐,也需要先将输入的image降维到64
64,这里使用的方法是用卷积块,所以这里的特征提取也是属于需要学习的参数。

schedulers

训练的数据准备

训练注意

效果评估