2024不可不会的StableDiffusion(二)

时间:2024-01-27 17:58:28

1. 引言

这是我关于StableDiffusion学习系列的第二篇文章,如果第一篇你还没有阅读,强烈推荐大家翻看前篇内容。在本文中,我们将学习构成StableDiffusion的各个基础组件,并针对每个组件的功能进行阐述。

闲话少说,我们直接开始吧!


2. 概览

正如前文所述,扩散模型主要用来生成高质量的图像。稳定扩散模型(StableDiffusion)可以视为一种特殊的扩散模型,学术上叫做潜在扩散模型(Latent Diffusion model),相关概念来自于论文《 High-Resolution Image Synthesis with Latent Diffusion Models》。简单来说,原始的扩散模型通常会消耗更多的内存,因此创建了潜在扩散模型,可以在被称为潜在低维度特征空间进行扩散过程。更进一步,扩散模型是机器学习模型,它被训练来在带有高斯噪声的图像上逐步进行去噪,以获得高质量图像。而潜在扩散模型被训练成在低纬度的特征空间上进行同样的过程。

一般来说,潜在扩散模型含有三个组件:

  • a text encoder 文本编码器,这里主要指基于CLIP的文本编码器
  • an autoencoder 自编码器,这里主要指Variational Auto Encoder,也被简称为VAE
  • A Unet 扩散模型

本文先从最基础的text encoder讲起,主要介绍其在扩散过程中的用途。


3. CLIP Text Encoder作用

CLIP text Encoder 以文本作为输入,并在潜在特征空间中生成文本嵌入(text embeddings);我们也可以通过CLIP模型对图像进行特征编码一样。

2024不可不会的StableDiffusion(二)_Stable Diffusion

任何机器学习模型都无法直接理解原始文本输入数据。在机器学习领域,我们通常都需要将文本转换为包含文本含义的数字表示,称为嵌入(embedding)。将文本转换为数字表示的过程可以分为两部分:

  • Tokenizer - 将文本输入拆分为各个子单词,然后使用查找表将每个子单词转换为数字
  • Token_To_Embedding Encoder - 将每个子单词的数字表示转换为包含该文本语义信息的特征表示


4. 代码实践之Tokenizer

只看文字讲解还是不够直观,让我们不妨通过代码来进一步了解它。我们将从导入相关库开始:

import torch,logging
from transformers import CLIPTextModel,CLIPTokenizer
clip_path = "/media/stable_diffusion/clip-vit-large-patch14"
tokenizer = CLIPTokenizer.from_pretrained(clip_path,
                                              local_files_only=True,
                                              torch_dtype=torch.float16)

让我们初始化一个文本提示,并将其token化,代码如下:

prompt = [ 'a dog wearing hat' ]
tok = tokenizer(prompt,padding="max_length",
                max_length=tokenizer.model_max_length,
                truncation=True,
                return_tensors="pt")
print(tok.input_ids.shape)
print(tok)

输出结果如下:

2024不可不会的StableDiffusion(二)_多模态大模型_02

观察上述输出,tokenizer返回包含以下两个对象的字典:

input_ids- 表示一个文本提示被转化为一个1X77的tensor,其中49406表示start token, 而320表示对应单词“a”的token,1929对应单词“dog”,3309对应单词“wearing”,3801对应单词“hat”,49407表示end token,后面重复的多个49407为了padding至固定长度77

attention_mask - 这里的1表示对应有效的embeded值,0表示对应的为padding

进一步,我们可以使用一下代码来将input_ids表示的单词依次打印出来:

for token in list(tok.input_ids[0,:7]):
        print(f"{token}:{tokenizer.convert_ids_to_tokens(int(token))}")

得到结果如下:

2024不可不会的StableDiffusion(二)_Stable Diffusion_03

5. 代码实践之Text Encoder

这一节我们来介绍基于CLIPText Encoder,首先我们利用以下代码来进行初始化操作:

text_encoder = CLIPTextModel.from_pretrained(clip_path,
                                         local_files_only=True,                
                                         torch_dtype=torch.float16).to('cuda')

接着我们利用text_encoder来将由tokenizer 生成的input_ids 转化为对应的文本嵌入表示,代码如下:

emb = text_encoder(tok.input_ids.to("cuda"))[0].half()
print(f"shape of embedding: {emb.shape}")
print(emb)

得到结果如下:

2024不可不会的StableDiffusion(二)_生成式AI_04

如上所述,大小为1x77的每个token化后的输入现在已被转换为1x77x768嵌入(embedding)。因此,每个单词都被表示在768维的潜在特征空间中。


6. Text encoder在SD中的用途

事实上,Stable Diffusion仅仅使用训练好的CLIP模型来实现将文本转化为嵌入表示,这种嵌入表示作为扩散模型UNet的输入之一。

2024不可不会的StableDiffusion(二)_Stable Diffusion_05

一般来说,CLIP使用文本编码器和图像编码器来在潜在空间进行特征嵌入,通过对比学习来将文本和图像语义接近的在特征空间进行距离拉近。关于CLIP更多的信息,大家可以访问OpenAI的关于CLIP介绍的文章,链接见附录;也可以翻看我之前的博客。


7. 总结

本文重点介绍了SD模型中的文本编码器text encoder的相关功能和具体实现原理,并详细介绍了其两个具体操作步骤,并给出了相应的代码示例。

您学废了嘛!


8. 参考链接

本文涉及的相关参考链接如下:

LDM论文: 戳我

CLIP介绍: 戳我