【深度学习】StableDiffusion的组件解析,运行一些基础组件效果

前言

看了篇文:

https://zhuanlan.zhihu.com/p/617134893

运行一些组件试试效果。

vae

代码:

import torch
from diffusers import AutoencoderKL
import numpy as np
from PIL import Image

# 加载模型: autoencoder可以通过SD权重指定subfolder来单独加载
autoencoder = AutoencoderKL.from_pretrained(
    "/ssd/xiedong/src_data/eff_train/Stable-diffusion/majicmixRealistic_v7_diffusers", subfolder="vae")
autoencoder.to("cuda", dtype=torch.float16)

# 读取图像并预处理
raw_image = Image.open("girl.png").convert("RGB").resize((512, 512))
image = np.array(raw_image).astype(np.float32) / 127.5 - 1.0
image = image[None].transpose(0, 3, 1, 2)
image = torch.from_numpy(image)

# 压缩图像为latent并重建
with torch.inference_mode():
    # latentx形状是 (B, C, H, W) 的张量  (1,4,64,64)
    latentx = autoencoder.encode(image.to("cuda", dtype=torch.float16)).latent_dist.sample()  # 压缩
    # 保存 latent 为 PNG 图像
    latent = latentx.permute(0, 2, 3, 1)  # 将 latent 重新排列为 (B, H, W, C) 格式
    latent = latent.cpu().numpy()  # 将 latent 转换为 NumPy 数组
    latent = (latent * 127.5 + 127.5).astype('uint8')  # 将值缩放到 [0, 255] 范围内,并转换为 uint8 类型
    latent = latent.squeeze(0)  # 去掉批次维度

    latent_image = Image.fromarray(latent)  # 将 NumPy 数组转换为 PIL Image
    latent_image.save("latent.png")  # 保存为 PNG 图像
    # shape
    print(latentx.shape)
    rec_image = autoencoder.decode(latentx).sample
    rec_image = (rec_image / 2 + 0.5).clamp(0, 1)
    rec_image = rec_image.cpu().permute(0, 2, 3, 1).numpy()
    rec_image = (rec_image * 255).round().astype("uint8")
    rec_image = Image.fromarray(rec_image[0])

# save
rec_image.save("demo.png")

原图:

在这里插入图片描述

encoder之后:

在这里插入图片描述
decoder之后:

在这里插入图片描述

clip

在自然语言处理任务中,tokenizer和text_encoder是两个重要的组件,用于将文本转换为模型可以理解的数值表示形式。

  1. Tokenizer:

Tokenizer的作用是将一个文本序列(如句子或段落)分割成一系列的token(通常是单词或子词)。它将文本映射到一个词表(vocabulary),每个token对应词表中的一个索引(index)。在您的代码示例中,tokenizer将输入的文本"a photograph of an astronaut riding a horse"转换为对应的token id序列。

  1. Text Encoder:

Text Encoder(文本编码器)是一个预训练的语言模型,它可以将token id序列编码为对应的向量表示(embeddings)。这些向量表示捕获了单词及其上下文的语义信息。

在您的代码中:

  • tokenizer(prompt) 将文本"a photograph of an astronaut riding a horse"转换为对应的token id序列(如[1, 27, 38, 61, …]等)。
  • text_encoder(text_input_ids) 将这个token id序列输入到文本编码器模型中,得到对应的向量表示text_embeddings

最终得到的text_embeddings的形状是 torch.Size([1, 77, 768])。其中:

  • 1 表示批次大小(batch size),对于单个输入就是1。
  • 77 表示输入token的数量(由于padding,长度被填充到模型的最大长度)。
  • 768 是每个token的向量表示的维度。

通过这种编码方式,原始的文本被转换为了具有丰富语义信息的数值向量表示,方便被深度学习模型进一步处理。这种编码过程是自然语言处理中的常见做法。

from transformers import CLIPTextModel, CLIPTokenizer

text_encoder = CLIPTextModel.from_pretrained(
    "/ssd/xiedong/src_data/eff_train/Stable-diffusion/majicmixRealistic_v7_diffusers", subfolder="text_encoder").to(
    "cuda")
# text_encoder = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14").to("cuda")
tokenizer = CLIPTokenizer.from_pretrained(
    "/ssd/xiedong/src_data/eff_train/Stable-diffusion/majicmixRealistic_v7_diffusers", 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 = tokenizer(
    prompt,
    padding="max_length",
    max_length=tokenizer.model_max_length,
    truncation=True,
    return_tensors="pt"
).input_ids

print(text_input_ids.shape)  # torch.Size([1, 77]
# 将token ids送入text model得到77x768的特征
text_embeddings = text_encoder(text_input_ids.to("cuda"))[0]
print(text_embeddings.shape)  # torch.Size([1, 77, 768])

UNet

稳定扩散(Stable Diffusion,SD)是一种基于扩散模型的生成式人工智能模型,用于创建图像。它的核心是一个860万参数的UNet结构,负责将文本提示转化为图像。

UNet结构主要由编码器(Encoder)和解码器(Decoder)两部分组成,两部分通过跳跃连接(Skip Connection)相连。编码器用于捕获输入的潜在表征(Latent Representation),解码器则根据这些表征生成最终图像。

具体而言,编码器包含:

  • 3个CrossAttnDownBlock2D模块,每个模块会对输入进行2倍下采样
  • 1个DownBlock2D模块,不进行下采样

中间是一个UNetMidBlock2DCrossAttn模块,用于连接编码器和解码器。

解码器包含:

  • 1个UpBlock2D模块
  • 3个CrossAttnUpBlock2D模块

编码器和解码器的模块数量和结构是对应的,通过跳跃连接融合不同尺度的特征。

在这里插入图片描述

CrossAttnDownBlock2D 和 CrossAttnUpBlock2D 是 Stable Diffusion 中 UNet 架构的关键模块,用于将文本条件(text condition)融入到图像生成的过程中。它们利用了自注意力(Self-Attention)机制,将文本嵌入与图像特征进行交叉注意力(Cross-Attention)操作。

以 CrossAttnDownBlock2D 为例,其核心是一个 CrossAttention 模块,该模块的工作原理如下:

  1. 将文本条件(如"一只可爱的小狗")编码为文本嵌入(text embeddings),记为 E t e x t \mathbf{E}_{text} Etext

  2. 从 UNet 的中间层获取图像特征,记为 F i m a g e \mathbf{F}_{image} Fimage

  3. 在 CrossAttention 中,将 F i m a g e \mathbf{F}_{image} Fimage 作为 Query,而 E t e x t \mathbf{E}_{text} Etext 作为 Key 和 Value,计算注意力权重:

    Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dk QKT)V

    其中 Q = F i m a g e Q=\mathbf{F}_{image} Q=Fimage, K = E t e x t K=\mathbf{E}_{text} K=Etext, V = E t e x t V=\mathbf{E}_{text} V=Etext, d k d_k dk 是缩放因子。

  4. 将注意力权重与 F i m a g e \mathbf{F}_{image} Fimage 相加,得到融合了文本条件的新特征表示。

这个过程可以用以下伪代码表示:

class CrossAttention(nn.Module):
    def forward(self, x, context):
        """
        x: 图像特征 [batch, channels, height, width]
        context: 文本嵌入 [batch, text_len, text_dim]
        """
        q = x.view(x.size(0), -1, x.size(-1))  # [batch, channels*height*width, dim]
        k = context.permute(0, 2, 1)  # [batch, text_dim, text_len]
        v = context.permute(0, 2, 1)  # [batch, text_dim, text_len]
        
        attn = torch.bmm(q, k)  # [batch, channels*height*width, text_len]
        attn = attn / sqrt(k.size(-1))
        attn = softmax(attn, dim=-1)
        
        x = torch.bmm(attn, v)  # [batch, channels*height*width, text_dim]
        x = x.view(x.size(0), -1, x.size(-1))  # [batch, channels, height, width]
        
        return x

CrossAttnUpBlock2D 的工作方式与 CrossAttnDownBlock2D 类似,只是它位于 UNet 的解码器部分。通过这种交叉注意力机制,Stable Diffusion 能够将文本条件与图像特征进行融合,从而根据提示生成所需的图像。

CrossAttention 模块是 Stable Diffusion 中实现文本到图像生成的关键部分,它利用注意力机制将文本信息注入到图像特征中,使生成的图像能够匹配给定的文本描述。

unet训练

SD训练过程原理:

SD模型的训练过程可以概括为以下几个步骤:

  1. 将图像编码到潜在空间,获得潜在向量表示。
  2. 使用CLIP文本编码器获取文本的嵌入向量表示。
  3. 在潜在空间中采样噪声,并将其添加到潜在向量中,进行扩散过程。
  4. U-Net模型接收扩散后的噪声潜在向量和文本嵌入,预测原始的噪声向量。
  5. 计算预测噪声和实际噪声之间的均方误差作为损失函数。
  6. 通过反向传播优化U-Net模型参数,使其能够更好地预测噪声。

整个过程可以用以下公式表示:

L = E x 0 , ϵ , t [ ∥ ϵ − ϵ θ ( x t , t , y ) ∥ 2 ] \mathcal{L} = \mathbb{E}_{x_0, \epsilon, t} \left[\left\| \epsilon - \epsilon_\theta(x_t, t, y) \right\|^2\right] L=Ex0,ϵ,t[ϵϵθ(xt,t,y)2]

其中:

  • x 0 x_0 x0是原始的潜在向量
  • ϵ \epsilon ϵ是添加到潜在向量上的噪声
  • t t t是随机采样的时间步长
  • x t x_t xt是扩散后的噪声潜在向量
  • y y y是文本嵌入向量
  • ϵ θ \epsilon_\theta ϵθ是U-Net模型预测的噪声

通过最小化这个损失函数,U-Net模型可以学习到从噪声潜在向量和文本嵌入中预测原始噪声的能力。在生成图像时,可以通过从随机噪声开始,逐步去噪,并根据文本嵌入对每一步的去噪过程进行条件控制,最终得到符合文本描述的图像。

Classifier-Free Guidance (CFG)是一种在训练和生成过程中提高文本条件控制的技术。它的核心思想是在训练时,以一定概率随机丢弃文本嵌入,这样模型就可以同时学习条件预测和无条件预测。在生成时,将条件预测和无条件预测的结果进行加权融合,从而增强文本条件的影响。CFG可以用以下公式表示:

ϵ θ ( C F G ) = ϵ θ ( x t , t , y ) + s ⋅ ( ϵ θ ( x t , t , ∅ ) − ϵ θ ( x t , t , y ) ) \epsilon_\theta^{(CFG)} = \epsilon_\theta(x_t, t, y) + s \cdot (\epsilon_\theta(x_t, t, \emptyset) - \epsilon_\theta(x_t, t, y)) ϵθ(CFG)=ϵθ(xt,t,y)+s(ϵθ(xt,t,)ϵθ(xt,t,y))

其中:

  • ϵ θ ( x t , t , y ) \epsilon_\theta(x_t, t, y) ϵθ(xt,t,y)是条件预测的噪声
  • ϵ θ ( x t , t , ∅ ) \epsilon_\theta(x_t, t, \emptyset) ϵθ(xt,t,)是无条件预测的噪声
  • s s s是一个scale参数,用于控制条件和无条件预测的权重

通过CFG,可以有效地提高生成图像与输入文本的一致性。

帮助、问询

https://docs.qq.com/sheet/DUEdqZ2lmbmR6UVdU?tab=BB08J2

相关推荐

  1. flutter学习-day9-基础组件

    2024-04-08 13:42:07       52 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-08 13:42:07       91 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-08 13:42:07       97 阅读
  3. 在Django里面运行非项目文件

    2024-04-08 13:42:07       78 阅读
  4. Python语言-面向对象

    2024-04-08 13:42:07       88 阅读

热门阅读

  1. Python基于Tkinter的加法游戏

    2024-04-08 13:42:07       29 阅读
  2. 利用hdfs gateway挂载NFS到本地

    2024-04-08 13:42:07       37 阅读
  3. GitOps是DevOps的下一个风口吗?

    2024-04-08 13:42:07       38 阅读
  4. FDA 上市库 Mini | 药物筛选 | MCE

    2024-04-08 13:42:07       36 阅读
  5. HyperBus协议--HyperFLASH中Program Suspend 功能的理解

    2024-04-08 13:42:07       33 阅读