Stable Diffusion
paper https://arxiv.org/abs/2112.10752 Stable Diffusion Version 1https://github.com/CompVis/stable-diffusion https://github.com/CompVis/latent-diffusion Stable Diffusion Version 2https://github.com/Stability-AI/stablediffusion/ text2img-figure
2022年可谓是AIGC(AI Generated Content)元年 ,上半年有文生图大模型DALL-E2 和Stable Diffusion ,下半年有OpenAI的文本对话大模型ChatGPT 问世,这让冷却的AI又沸腾起来了,因为AIGC能让更多的人真真切切感受到AI的力量。
2022年1月推出Dall-E 2
2022年8月推出Stable Diffusion
SD功能
1)其核心功能为仅根据文本提示作为输入来生成的图像(text2img)
2)你也可以用它对图像根据文字描述进行修改(即输入为文本 + 图像)
SD v1.0 这篇文章将介绍比较火的文生图模型Stable Diffusion(简称SD) ,Stable Diffusion不仅是一个完全开源的模型(代码,数据,模型全部开源),而且是它的参数量只有1B左右,大部分人可以在普通的显卡上进行推理甚至精调模型。毫不夸张的说,Stable Diffusion的出现和开源对AIGC的火热和发展是有巨大推动作用的,因为它让更多的人能快地上手AI作画。 SD是一个基于latent的扩散模型 ,它在UNet中引入text condition来实现基于文本生成图像。SD的核心来源于Latent Diffusion 这个工作,常规的扩散模型是基于pixel的生成模型,而Latent Diffusion是基于latent的生成模型,它先采用一个autoencoder将图像压缩到latent空间,然后用扩散模型来生成图像的latents,最后送入autoencoder的decoder模块就可以得到生成的图像。 基于latent的扩散模型的优势在于计算效率更高效,因为图像的latent空间要比图像pixel空间要小,这也是SD的核心优势 。文生图模型往往参数量比较大,基于pixel的方法往往限于算力只生成64x64大小的图像,比如OpenAI的DALL-E2和谷歌的Imagen,然后再通过超分辨模型将图像分辨率提升至256x256和1024x1024;而基于latent的SD是在latent空间操作的,它可以直接生成256x256和512x512甚至更高分辨率的图像。CLIP 图像及其标题,数据集中大约包含 4 亿张图像及描述
img
是图像编码器和文本编码器的组合,其训练过程可以简化为拍摄图像和文字说明,使用两个编码器对数据分别进行编码,然后使用余弦距离比较结果嵌入,刚开始训练时,即使文本描述与图像是相匹配的,它们之间的相似性肯定也是很低的。随着模型的不断更新,在后续阶段,编码器对图像和文本编码得到的嵌入会逐渐相似。通过在整个数据集中重复该过程,并使用大 batch size 的编码器,最终能够生成一个嵌入向量,其中狗的图像和句子「一条狗的图片」之间是相似的。就像在 word2vec 中一样,训练过程也需要包括不匹配的图片和说明的负样本,模型需要给它们分配较低的相似度分数。
img
CLIP text encoder 模型中的语言理解组件使用的是 Transformer 语言模型,可以将输入的文本提示转换为 token 嵌入向量。
1
Imagen 论文中的实验表明,相比选择更大的图像生成组件,更大的语言模型可以带来更多的图像质量提升。
Stable Diffusion 模型使用的是 OpenAI 发布的经过预训练的 ClipText 模型 Stable Diffusion V2 中已经转向了最新发布的、更大的 CLIP 模型变体 OpenClip. Components img
img
1)Clip Text 用于文本编码
输入:文本
输出:77 个 token 嵌入向量,其中每个向量包含 768 个维度
2)UNet + Scheduler 在信息(潜)空间中逐步处理 / 扩散信息。
输入:文本嵌入和一个由噪声组成的初始多维数组(结构化的数字列表,也叫张量 tensor)。
输出:一个经过处理的信息阵列
3)自编码解码器(Autoencoder Decoder),使用处理过的信息矩阵绘制最终图像的解码器。
输入:处理过的信息矩阵,维度为(4, 64, 64)
输出:结果图像,各维度为(3,512,512)
主要包括三个模型:
autoencoder :encoder将图像压缩到latent空间,而decoder将latent解码为图像;CLIP text encoder :提取输入text的text embeddings,通过cross attention方式送入扩散模型的UNet中作为condition;UNet :扩散模型的主体,用来实现文本引导下的latent生成。image-20230825104753889
对于SD模型,其autoencoder模型参数大小为84M,CLIP text encoder模型大小为123M,而UNet参数大小为860M,所以SD模型的总参数量约为1B
autoencoder autoencoder是一个基于encoder-decoder架构的图像压缩模型,对于一个大小为的输入图像,encoder模块将其编码为一个大小为的latent,其中为下采样率(downsampling factor)。在训练autoencoder过程中,除了采用L1重建损失 外,还增加了感知损失 (perceptual loss,即LPIPS,具体见论文The Unreasonable Effectiveness of Deep Features as a Perceptual Metric)以及基于patch的对抗训练 。辅助loss主要是为了确保重建的图像局部真实性以及避免模糊,具体损失函数见latent diffusion的loss部分。同时为了防止得到的latent的标准差过大,采用了两种正则化方法:第一种是KL-reg ,类似VAE增加一个latent和标准正态分布的KL loss,不过这里为了保证重建效果,采用比较小的权重(~10e-6);第二种是VQ-reg ,引入一个VQ (vector quantization)layer,此时的模型可以看成是一个VQ-GAN,不过VQ层是在decoder模块中,这里VQ的codebook采样较高的维度(8192)来降低正则化对重建效果的影响。latent diffusion论文中实验了不同参数下的autoencoder模型,如下表所示,可以看到当较小和较大时,重建效果越好(PSNR越大),这也比较符合预期,毕竟此时压缩率小。 D采用基于KL-reg的autoencoder,其中下采样率,特征维度为,当输入图像为512x512大小时将得到64x64x4大小的latent。autoencoder模型时在OpenImages数据集上基于256x256大小训练的,但是由于autoencoder的模型是全卷积结构的(基于ResnetBlock),所以它可以扩展应用在尺寸>256的图像上。下面我们给出使用diffusers库来加载autoencoder模型,并使用autoencoder来实现图像的压缩和重建 由于SD采用的autoencoder是基于KL-reg的,所以这个autoencoder在编码图像时其实得到的是一个高斯分布DiagonalGaussianDistribution(分布的均值和标准差),然后通过调用sample方法来采样一个具体的latent(调用mode方法可以得到均值)。由于KL-reg的权重系数非常小,实际得到latent的标准差还是比较大的,latent diffusion论文中提出了一种rescaling方法:首先计算出第一个batch数据中的latent的标准差,然后采用的系数来rescale latent,这样就尽量保证latent的标准差接近1(防止扩散过程的SNR较高,影响生成效果,具体见latent diffusion论文的D1部分讨论),然后扩散模型也是应用在rescaling的latent上,在解码时只需要将生成的latent除以,然后再送入autoencoder的decoder即可。对于SD所使用的autoencoder,这个rescaling系数为0.18215。 CLIP text encoder SD采用CLIP text encoder来对输入text提取text embeddings ,具体的是采用目前OpenAI所开源的最大CLIP模型:clip-vit-large-patch14 ,这个CLIP的text encoder是一个transformer模型(只有encoder模块):层数为12,特征维度为768,模型参数大小是123M。对于输入text,送入CLIP text encoder后得到最后的hidden states(即最后一个transformer block得到的特征),其特征维度大小为77x768(77是token的数量),这个细粒度的text embeddings将以cross attention的方式送入UNet中 。在transofmers库中,可以如下使用CLIP text encoder from transformers import CLIPTextModel, CLIPTokenizer
text_encoder = CLIPTextModel.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="text_encoder").to("cuda")
# text_encoder = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14").to("cuda")
tokenizer = CLIPTokenizer.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="tokenizer")
# tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14")
# 对输入的text进行tokenize,得到对应的token ids
prompt = "a photograph of an astronaut riding a horse"
text_input_ids = text_tokenizer(
prompt,
padding="max_length",
max_length=tokenizer.model_max_length,
truncation=True,
return_tensors="pt"
).input_ids
# 将token ids送入text model得到77x768的特征
text_embeddings = text_encoder(text_input_ids.to("cuda"))[0]
值得注意的是,这里的tokenizer最大长度为77(CLIP训练时所采用的设置),当输入text的tokens数量超过77后,将进行截断,如果不足则进行paddings,这样将保证无论输入任何长度的文本(甚至是空文本)都得到77x768大小的特征。在训练SD的过程中,CLIP text encoder模型是冻结的 。在早期的工作中,比如OpenAI的GLIDE和latent diffusion中的LDM均采用一个随机初始化的tranformer模型来提取text的特征,但是最新的工作都是采用预训练好的text model。比如谷歌的Imagen采用纯文本模型T5 encoder来提出文本特征,而SD则采用CLIP text encoder,预训练好的模型往往已经在大规模数据集上进行了训练,它们要比直接采用一个从零训练好的模型要好。 UNet encoder部分包括3个CrossAttnDownBlock2D模块和1个DownBlock2D模块,而decoder部分包括1个UpBlock2D模块和3个CrossAttnUpBlock2D模块,中间还有一个UNetMidBlock2DCrossAttn模块。encoder和decoder两个部分是完全对应的,中间存在skip connection。注意3个CrossAttnDownBlock2D模块最后均有一个2x的downsample操作,而DownBlock2D模块是不包含下采样的。 1
其中CrossAttnDownBlock2D模块的主要结构如下图所示,text condition将通过CrossAttention模块嵌入进来,此时Attention的query是UNet的中间特征,而key和value则是text embeddings。 SD training
import torch
from diffusers import AutoencoderKL, UNet2DConditionModel, DDPMScheduler
from transformers import CLIPTextModel, CLIPTokenizer
import torch.nn.functional as F
# 加载autoencoder
vae = AutoencoderKL.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="vae")
# 加载text encoder
text_encoder = CLIPTextModel.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="text_encoder")
tokenizer = CLIPTokenizer.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="tokenizer")
# 初始化UNet
unet = UNet2DConditionModel(**model_config) # model_config为模型参数配置
# 定义scheduler
noise_scheduler = DDPMScheduler(
beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000
)
# 冻结vae和text_encoder
vae.requires_grad_(False)
text_encoder.requires_grad_(False)
opt = torch.optim.AdamW(unet.parameters(), lr=1e-4)
for step, batch in enumerate(train_dataloader):
with torch.no_grad():
# 将image转到latent空间
latents = vae.encode(batch["image"]).latent_dist.sample()
latents = latents * vae.config.scaling_factor # rescaling latents
# 提取text embeddings
text_input_ids = text_tokenizer(
batch["text"],
padding="max_length",
max_length=tokenizer.model_max_length,
truncation=True,
return_tensors="pt"
).input_ids
text_embeddings = text_encoder(text_input_ids)[0]
# 随机采样噪音
noise = torch.randn_like(latents)
bsz = latents.shape[0]
# 随机采样timestep
timesteps = torch.randint(0, noise_scheduler.num_train_timesteps, (bsz,), device=latents.device)
timesteps = timesteps.long()
# 将noise添加到latent上,即扩散过程
noisy_latents = noise_scheduler.add_noise(latents, noise, timesteps)
# 预测noise并计算loss
model_pred = unet(noisy_latents, timesteps, encoder_hidden_states=text_embeddings).sample
loss = F.mse_loss(model_pred.float(), noise.float(), reduction="mean")
opt.step()
opt.zero_grad()
How Diffusion img
Stable Diffusion 并没有选择在像素图像本身上运行扩散过程,而是选择在图像的压缩版本上运行,论文中也称之为「Departure to Latent Space」 前向扩散(forward diffusion)过程是在压缩 latents 完成的,噪声的切片(slices)是应用于 latents 上的噪声,而非像素图像,所以噪声预测器实际上是被训练用来预测压缩表示(潜空间)中的噪声。
前向过程,即使用使用自编码器中的编码器来训练噪声预测器。一旦训练完成后,就可以通过运行反向过程(自编码器中的解码器)来生成图像。
1前向和后向过程如下所示,图中还包括了一个 conditioning 组件,用来描述模型应该生成图像的文本提示。
1
How Stable 为了将文本条件融入成为图像生成过程的一部分,必须调整噪声预测器的输入为文本。
1
所有的操作都是在潜空间上,包括编码后的文本、输入图像和预测噪声。
1
1 Unet 噪声预测器中的层(无文本)
一个不使用文本的 diffusion Unet,其输入输出如下所示:
Jay Alammar 再发新作:超高质量图解 Stable Diffusion ,看完彻底搞懂「图像生成」原理
在模型内部,可以看到:
Unet 模型中的层主要用于转换 latents; 每层都是在之前层的输出上进行操作; 某些输出(通过残差连接)将其馈送到网络后面的处理中 将时间步转换为时间步长嵌入向量,可以在层中使用。 1
2 Unet 噪声预测器中的层(带文本)
现在就需要将之前的系统改装成带文本版本的。
1
主要的修改部分就是增加对文本输入(术语:text conditioning)的支持,即在 ResNet 块之间添加一个注意力层。
1
需要注意的是,ResNet 块没有直接看到文本内容,而是通过注意力层将文本在 latents 中的表征合并起来,然后下一个 ResNet 就可以在这一过程中利用上文本信息。
SD v2.0 Stability AI公司在2022年11月(stable-diffusion-v2-release)放出了SD 2.0版本,这里我们也简单介绍一下相比SD 1.x版本SD 2.0的具体改进点。SD 2.0相比SD 1.x版本的主要变动在于模型结构 和训练数据 两个部分。
模型结构:SD 1.x版本的text encoder采用的是OpenAI的CLIP ViT-L/14模型,其模型参数量为123.65M;而SD 2.0采用了更大的text encoder:基于OpenCLIP在laion-2b数据集上训练的CLIP ViT-H/14模型,其参数量为354.03M,相比原来的text encoder模型大了约3倍。 image-20230825110612927
可以看到CLIP ViT-H/14模型相比原来的OpenAI的L/14模型,在imagenet1K上分类准确率和mscoco多模态检索任务上均有明显的提升,这也意味着对应的text encoder更强,能够抓住更准确的文本语义信息。另外是一个小细节是SD 2.0提取的是text encoder倒数第二层的特征,而SD 1.x提取的是倒数第一层的特征。由于倒数第一层的特征之后就是CLIP的对比学习任务,所以倒数第一层的特征可能部分丢失细粒度语义信息,Imagen论文(见论文D.1部分)和novelai(见novelai blog)均采用了倒数第二层特征。对于UNet模型,SD 2.0相比SD 1.x几乎没有改变,唯一的一个小的变动是:SD 2.0不同stage的attention模块是固定attention head dim为64,而SD 1.0则是不同stage的attention模块采用固定attention head数量,明显SD 2.0的这种设定更常用,但是这个变动不会影响模型参数。然后是训练数据,前面说过SD 1.x版本其实最后主要采用laion-2B中美学评分为5以上的子集来训练,而SD 2.0版本采用评分在4.5以上的子集,相当于扩大了训练数据集,具体的训练细节见model card。另外SD 2.0除了512x512版本的模型,还包括768x768版本的模型(https://huggingface.co/stabilityai/stable-diffusion-2),所谓的768x768模型是在512x512模型基础上用图像分辨率大于768x768的子集继续训练的,不过优化目标不再是noise_prediction,而是采用Progressive Distillation for Fast Sampling of Diffusion Models论文中所提出的 v-objective。下图为SD 2.0和SD 1.x版本在COCO2017验证集上评测的对比,可以看到2.0相比1.5,CLIP score有一个明显的提升,同时FID也有一定的提升。但是正如前面所讨论的,FID和CLIP score这两个指标均有一定的局限性,所以具体效果还是上手使用来对比。 stability AI在发布SD 2.0的同时,还发布了另外3个模型:stable-diffusion-x4-upscaler ,stable-diffusion-2-inpainting 和stable-diffusion-2-depth 。stable-diffusion-x4-upscaler是一个基于扩散模型的4x超分模型,它也是基于latent diffusion,不过这里采用的autoencoder是基于VQ-reg的,下采样率为。在实现上,它是将低分辨率图像直接和noisy latent拼接在一起送入UNet,因为autoencoder将高分辨率图像压缩为原来的1/4,而低分辨率图像也为高分辨率图像的1/4,所以低分辨率图像的空间维度和latent是一致的。另外,这个超分模型也采用了Cascaded Diffusion Models for High Fidelity Image Generation所提出的noise conditioning augmentation,简单来说就是在训练过程中给低分辨率图像加上高斯噪音,可以通过扩散过程来实现,注意这里的扩散过程的scheduler与主扩散模型的scheduler可以不一样,同时也将对应的noise_level(对应扩散模型的time step)通过class labels的方式送入UNet,让UNet知道加入噪音的程度。stable-diffusion-x4-upscaler是使用LAION中>2048x2048大小的子集(10M)训练的,训练过程中采用512x512的crops来训练(降低显存消耗)。SD模型可以用来生成512x512图像,加上这个超分模型,就可以得到2048x2048大小的图像。Stability AI公司还开源了两个加强版的autoencoder:ft-EMA和ft-MSE(前者使用L1 loss后者使用MSE loss),前面已经说过,它们是在LAION数据集继续finetune decoder来增强重建效果 Reference https://jalammar.github.io/illustrated-stable-diffusion/ https://www.ithome.com/0/668/981.htm https://developer.aliyun.com/article/1238473