AI 绘画Stable Diffusion 研究(七) 一文读懂 Stable Diffusion 工作原理(2)

简介: AI 绘画Stable Diffusion 研究(七) 一文读懂 Stable Diffusion 工作原理

(5)、将文本转换为tokens

使用CLIP模型的tokenize方法将文本转换为tokens

text_tokens = openai.clip.tokenize(text_description)


这里,text_tokens是一个PyTorch张量,形状为(1, N),其中N是文本描述中的token数量。

在这个例子中,N=3,因为"苹果"被分成了3个tokens。


(6)、 查看tokens

print(f"Tokens: {text_tokens}")

输出结果可能类似于:

Tokens: tensor([[49406, 3782, 49407]])


这里,49406表示开始符号(start-of-sentence),3782表示“苹果”,49407表示结束符号(end-of-sentence)。

通过以上步骤,我们将文本“苹果”转换为了tokens。


PS:

  • Stable Diffusion v1使用了CLIP模型的tokenizer
  • Tokenizer只能将其在训练过程中见到过的单词进行分词
    例如:假设CLIP模型里有“dream”与“beach”单词,但是没有“dreambeach”单词。
    Tokenizer会将“dreambeach”分成2个单词“dream”与“beach”。
  • 1个单词并非代表1个token,而是有可能进一步进行拆分
  • 空格也是token的一部分
    例如:短语 “dream beach” 产生了两个token “dream” 和 “[space]beach”。
    这些标记与 “dreambeach” 产生的标记不同,后者是 “dream” 和 “beach”(beach 前没有空格)。

2、词嵌入(Embedding)


(1)、为什么需要词嵌入(Embedding)?

因为有些单词相互之间是非常相似,我们希望利用到这些语义信息。


例如:

man、gentleman、guy的词嵌入是非常相近的,因此它们可以相互替换。

Monet、Manet以及Degas都以印象派的风格绘画,但是方式各不相同。

这些名字看起来是非常相似,但是在词嵌入(Embedding)里是不一样的。


(2)、词嵌入(Embedding) 是如何工作的?


Embedding 将输入的tokens转换为一个连续的向量来表示,这个向量可以捕捉文本中的语义信息。在我们的例子中,"苹果"的tokens经过CLIP模型的encode_text方法后,会得到一个特征向量。


这个特征向量是一个高维空间中的点,通常具有固定的维度(在CLIP模型中,维度为512)。请注意,由于模型权重和随机性的原因,每次运行时生成的特征向量可能略有不同。以下是一个示例输出:


print(f"Text features: {text_features}")

输出结果可能类似于:

Text features: tensor([[-0.0123,  0.0345, -0.0678, ...,  0.0219, -0.0456,  0.0789]])


这里,text_features是一个形状为(1, 512)的PyTorch张量,其中包含了“苹果”这个词的向量表示。神经网络可以利用这个向量表示进行训练和预测任务。


Stable diffusion v1使用Open AI的ViT-L/14模型,词嵌入为768维的向量。


3、文本转换器(text transformer)


(1)、为什么需要text transformer


既然通过embedding后可以直接输入到模型中进行训练,为何在stable diffusion中还需要将embedding通过text transformer转换后再作为模型的输入呢?


这是因为Stable Diffusion模型是一个图像生成模型,它需要理解输入文本的语义信息以生成与之相关的图像。直接使用基本的文本embedding可能无法充分捕捉到文本中的复杂语义关系。通过使用text transformer,可以获得一个更丰富、更具表现力的文本表示,这有助于提高生成图像的质量和与输入文本的相关性。


使用text transformer 在捕捉文本语义信息时,能够考虑到更多上下文关系和抽象概念

这个转换器就像是一个通用的条件(conditioning)适配器。


(2)、text transformer转换示例


下面以"苹果"为例进行说明。

假设我们已经获得了"苹果"的基本embedding(一个形状为(1, 512)的PyTorch张量):

text_features = tensor([[-0.0123,  0.0345, -0.0678, ...,  0.0219, -0.0456,  0.0789]])


接下来,我们将这个张量输入到text transformer中:

transformed_text_features = text_transformer(text_features)


经过text transformer处理后,我们可能会得到一个新的张量,如:

print(f"Transformed text features: {transformed_text_features}")

输出结果可能类似于:

Transformed text features: tensor([[ 0.0234, -0.0567,  0.0890, ..., -0.0321,  0.0672, -0.0813]])


这个新的张量(形状仍为(1, 512))包含了更丰富的语义信息,例如上下文关系和抽象概念。

这有助于Stable Diffusion模型更好地理解输入文本,并生成与之相关的图像。


请注意:

由于模型权重和随机性的原因,每次运行时生成的特征向量可能略有不同。

此外,具体的变化过程取决于所使用的text transformer结构和参数。


六、Stable Diffusion Cross-attention技术


Cross-attention 是通过提示词产生图片的核心技术。

文本转换器的输出,会被noise predictor在U-Net中使用到多次。

U-Net以一个叫做cross-attention机制的方式来使用它,cross-attention机制允许模型在不同的特征层次上关注相关的区域,从而提高生成结果的质量,这即是prompt适配图片的地方。


下面代码是stable diffusion所使用的transformers块,实现了cross-attention:

class SpatialTransformer(nn.Module):
    """
    Transformer block for image-like data.
    First, project the input (aka embedding)
    and reshape to b, t, d.
    Then apply standard transformer action.
    Finally, reshape to image
    """
    def __init__(self, in_channels, n_heads, d_head,
                 depth=1, dropout=0., context_dim=None):
        super().__init__()
        self.in_channels = in_channels
        inner_dim = n_heads * d_head
        self.norm = Normalize(in_channels)
        self.proj_in = nn.Conv2d(in_channels,
                                 inner_dim,
                                 kernel_size=1,
                                 stride=1,
                                 padding=0)
        self.transformer_blocks = nn.ModuleList(
            [BasicTransformerBlock(inner_dim, n_heads, d_head, dropout=dropout, context_dim=context_dim)
                for d in range(depth)]
        )
        self.proj_out = zero_module(nn.Conv2d(inner_dim,
                                              in_channels,
                                              kernel_size=1,
                                              stride=1,
                                              padding=0))
    def forward(self, x, context=None):
        # note: if no context is given, cross-attention defaults to self-attention
        b, c, h, w = x.shape
        x_in = x
        x = self.norm(x)
        x = self.proj_in(x)
        x = rearrange(x, 'b c h w -> b (h w) c')
        for block in self.transformer_blocks:
            x = block(x, context=context)
        x = rearrange(x, 'b (h w) c -> b c h w', h=h, w=w)
        x = self.proj_out(x)
        return x + x_in


七、Stable Diffusion noise schedule 技术


1、什么是 noise schedule ?

噪声通过多次U-Net的处理,最终会输出我们想要的图片。

在这多次处理中,每一次的降噪的幅度是不同的,所以我们就要通过schedulers来控制每次降噪的幅度(幅度一般是递减的)。这个技术就叫做 noise schedule

如图:



那么为什么要使用 noise schedule 技术呢?


在 Stable Diffusion 这种生成模型中,U-Net 是一个核心组件,用于从噪声图像中逐步恢复出原始图像。在多次迭代过程中,降噪幅度逐渐减小的原因是为了更精细地恢复图像的细节和结构。


Stable Diffusion 的过程可以看作是一个逆向扩散过程,它从一个高度噪声的图像开始,然后通过多个步骤逐渐去除噪声以重建原始图像。在这个过程中,U-Net 被用来预测每一步的降噪操作。


在前几轮迭代中,图像中的噪声较大,因此需要较大的降噪幅度来消除这些噪声。随着迭代次数的增加,图像中的噪声逐渐减小,因此降噪幅度也应相应减小。这样做的目的是避免过度平滑或损坏已经恢复的图像细节。


通过逐渐减小降噪幅度,U-Net 可以更好地控制去噪过程,使其在保留图像细节的同时有效地去除噪声。这有助于生成更清晰、更真实的图像。


这里举一个文生图的代码,用于说明noise schedule技术:

def txt2img():
    #unet
    unet = load_unet()
    #调度器
    scheduler = lms_scheduler()
    scheduler.set_timesteps(100)
    #文本编码
    prompts = ["a photograph of an astronaut riding a horse"]
    text_embeddings = prompts_embedding(prompts)
    text_embeddings = text_embeddings.cuda()     #(1, 77, 768)
    uncond_prompts = [""]
    uncond_embeddings = prompts_embedding(uncond_prompts)
    uncond_embeddings = uncond_embeddings.cuda() #(1, 77, 768)
    #初始隐变量
    latents = torch.randn( (1, 4, 64, 64))  #(1, 4, 64, 64)
    latents = latents * scheduler.sigmas[0]    #sigmas[0]=157.40723
    latents = latents.cuda()
    #循环步骤
    for i, t in enumerate(scheduler.timesteps):  #timesteps=[999.  988.90909091 978.81818182 ...100个
        latent_model_input = latents  #(1, 4, 64, 64)  
        sigma = scheduler.sigmas[i]
        latent_model_input = latent_model_input / ((sigma**2 + 1) ** 0.5)
        timestamp = torch.tensor([t]).cuda()
        with torch.no_grad():  
            noise_pred_text = unet(latent_model_input, timestamp, text_embeddings)
            noise_pred_uncond = unet(latent_model_input, timestamp, uncond_embeddings)
            guidance_scale = 7.5 
            noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
            latents = scheduler.step(noise_pred, i, latents)
    vae = load_vae()
    latents = 1 / 0.18215 * latents
    image = vae.decode(latents.cpu())  #(1, 3, 512, 512)
    save_image(image,"txt2img.png")
txt2img()


八、Stable Diffusion 文生图底层运行演示


在文本生成图的场景下,我们给SD模型输入一组文本提示词,它可以返回一张图片。


第一步、 Stable Diffusion在潜空间里生成一个随机张量。

我们通过设置随机种子seed来控制这个张量的生成。如果我们设置这个随机种子为一个特定的值,则会得到相同的随机张量。这就是我们在潜空间里的图片。但是当前还全是噪点。


第二步、 Noise predictor U-Net将潜噪点图已经文本提示词作为输入,并预测噪点

此噪点同样也在潜空间内(一个4 x 64 x 64的张量)


第三步、从潜图片中抽取潜噪点,并生成了新的潜图片


第二步 与 第三步重复特定采样次数,例如20次。


第四步、VAE 的decoder将潜图片转回像素空间

这便是我们通过SD模型最终得到的图片。


参考资料:

1. How does Stable Diffusion work?

2. stable-diffusion

3.扩散模型详解原理+代码

相关文章
|
12天前
|
人工智能 自然语言处理 算法
谷歌DeepMind研究再登Nature封面,隐形水印让AI无所遁形
近日,谷歌DeepMind团队在《自然》期刊上发表了一项名为SynthID-Text的研究成果。该方法通过引入隐形水印,为大型语言模型(LLM)生成的文本添加统计签名,从而实现AI生成文本的准确识别和追踪。SynthID-Text采用独特的Tournament采样算法,在保持文本质量的同时嵌入水印,显著提高了水印检测率。实验结果显示,该方法在多个LLM中表现出色,具有广泛的应用潜力。论文地址:https://www.nature.com/articles/s41586-024-08025-4。
49 26
|
1月前
|
人工智能 前端开发 编译器
【AI系统】LLVM 架构设计和原理
本文介绍了LLVM的诞生背景及其与GCC的区别,重点阐述了LLVM的架构特点,包括其组件独立性、中间表示(IR)的优势及整体架构。通过Clang+LLVM的实际编译案例,展示了从C代码到可执行文件的全过程,突显了LLVM在编译器领域的创新与优势。
85 3
|
18天前
|
人工智能 UED
VersaGen:生成式 AI 代理,基于 Stable Diffusion 生成图像,专注于控制一至多个视觉主体等生成细节
VersaGen 是一款生成式 AI 代理,专注于文本到图像合成中的视觉控制能力,支持多种视觉控制类型,并通过优化策略提升图像生成质量和用户体验。
38 8
VersaGen:生成式 AI 代理,基于 Stable Diffusion 生成图像,专注于控制一至多个视觉主体等生成细节
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
AI自己长出了类似大脑的脑叶?新研究揭示LLM特征的惊人几何结构
近年来,大型语言模型(LLM)的内部运作机制备受关注。麻省理工学院的研究人员在论文《The Geometry of Concepts: Sparse Autoencoder Feature Structure》中,利用稀疏自编码器(SAE)分析LLM的激活空间,揭示了其丰富的几何结构。研究发现,特征在原子、大脑和星系三个尺度上展现出不同的结构,包括晶体结构、中尺度模块化结构和大尺度点云结构。这些发现不仅有助于理解LLM的工作原理,还可能对模型优化和其他领域产生重要影响。
68 25
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
【AI系统】知识蒸馏原理
本文深入解析知识蒸馏(Knowledge Distillation, KD),一种将大型教师模型的知识高效转移至小型学生模型的技术,旨在减少模型复杂度和计算开销,同时保持高性能。文章涵盖知识蒸馏的基本原理、不同类型的知识(如响应、特征、关系知识)、蒸馏方式(离线、在线、自蒸馏)及Hinton的经典算法,为读者提供全面的理解。
77 2
【AI系统】知识蒸馏原理
|
30天前
|
人工智能 程序员 Linux
神秘山洞惊现AI绘画至宝Stable Diffusion残卷
随着AI神器的现世,不少修士担忧其会取代人类职业。然而,自女娲创造人类以来,法宝虽强,始终只是辅助工具,需修士操控才能发挥威力。如今修仙界最大的至宝是GPT,它能以文字为引,转化出所需答案。图片处理方面也有Stable Diffusion、DALL-E等法宝。这些AI工具并非替代修士,而是提升效率的助手。例如,Stable Diffusion最初由慕尼黑和海德堡大学宗师炼制,现已发展多个版本,如v1、v2.0、SDXL等,帮助修士更便捷地生成图像。通过合理使用这些工具,修士们可以更好地实现心中所想,而非被技术取代。
40 6
|
1月前
|
存储 人工智能 JavaScript
【AI系统】公共表达式消除原理
公共子表达式消除(CSE)是编译器优化技术,旨在通过识别并消除重复计算的表达式,减少计算量,提升程序执行效率。CSE分为局部和全局两种,局部CSE仅在单个基本块内操作,而全局CSE跨越多个基本块。技术手段包括局部值编号和缓式代码移动等,广泛应用于传统编译器及AI编译器中,有效简化计算图,降低计算成本。
49 4
|
1月前
|
存储 人工智能 缓存
【AI系统】布局转换原理与算法
数据布局转换技术通过优化内存中数据的排布,提升程序执行效率,特别是对于缓存性能的影响显著。本文介绍了数据在内存中的排布方式,包括内存对齐、大小端存储等概念,并详细探讨了张量数据在内存中的排布,如行优先与列优先排布,以及在深度学习中常见的NCHW与NHWC两种数据布局方式。这些布局方式的选择直接影响到程序的性能,尤其是在GPU和CPU上的表现。此外,还讨论了连续与非连续张量的概念及其对性能的影响。
67 3
|
1月前
|
机器学习/深度学习 人工智能 编译器
【AI系统】Auto-Tuning 原理
本文探讨了在多样化硬件平台上部署深度神经网络(DNN)时,传统算子库面临的挑战及解决方案。随着硬件平台的多样化和快速迭代,手动编写高性能算子库变得日益困难。文中介绍了基于TVM的三种自动调优系统——AutoTVM、Ansor和Meta Scheduler,它们通过自动生成高性能算子,有效解决了传统方法的局限性,提高了DNN在不同硬件平台上的执行效率。
31 1
|
2月前
|
机器学习/深度学习 人工智能 并行计算
【AI系统】NVLink 原理剖析
随着AI技术的发展,大模型参数量激增,对底层硬件和网络架构提出新挑战。高效训练这些模型需要大规模GPU集群及高速网络连接,以实现快速数据交换。然而,网络瓶颈限制了GPU性能的充分发挥,表明单纯增加GPU数量不能线性提升算力。因此,算存互连和算力互连技术成为关键,如PCIe、NVLink和NVSwitch等,它们通过提高数据传输速度和效率,支持大规模并行计算,解决了大规模GPU集群中的通信延迟问题,推动了万亿级模型训练的实现。
79 2

热门文章

最新文章