1、引言
1.1 潜伏空间的概念
潜伏空间(Latent Space)是在呆板学习和深度学习中一个紧张的概念,它指的是用于体现数据的一种低维空间。这个空间编码了数据中包含的所有有用信息的压缩体现,通常比原始数据空间的维数更低,从而使其更容易进行分析和明白。
1.1.1 潜伏空间的定义与生成
潜伏空间可以被视为数据的一种抽象体现,它捕获了数据的内涵结构和特征。这种空间可以通过各种方法生成,如主成分分析(PCA)和深度神经网络(DNN)等。PCA是一种线性降维方法,它通过寻找原始数据的线性组合来生成潜伏空间。而DNN则是一种非线性降维方法,它通过学习数据中的非线性关系来生成潜伏空间。
1.1.2 潜伏空间的应用
潜伏空间在多个领域有着广泛的应用。首先,它可以用于数据压缩,将数据压缩到更小的空间,从而节省存储空间和盘算资源。其次,潜伏空间也可以用于数据可视化,资助人们明白数据中隐藏的模式和趋势。别的,在数据挖掘和呆板学习中,潜伏空间也发挥着紧张作用,用于发现数据中隐藏的知识和提高模型的性能。
1.1.3 潜伏空间的特性
潜伏空间具有一些独特的特性。首先,它是低维的,这使得数据更易于处理和分析。其次,潜伏空间中的点或向量通常代表原始数据空间中的不同实例或特征,因此可以通过操纵这些点或向量来影响原始数据。最后,潜伏空间中的结构和关系每每反映了原始数据的内涵属性和规律。
潜伏空间是呆板学习和深度学习中一个紧张的概念,它通过将数据压缩到低维空间来捕获数据的内涵结构和特征。潜伏空间具有广泛的应用,包罗数据压缩、数据可视化、数据挖掘和呆板学习等。通过探索和利用潜伏空间,我们可以更好地明白数据、发现数据中隐藏的知识并提高模型的性能。
1.2 潜伏空间搜索
1.2.1 定义
潜伏空间搜索(Latent Space Search)的概念紧张围绕在深度学习和呆板学习中对潜伏空间(Latent Space)的利用和查询。潜伏空间是呆板学习中的一个低维空间,它编码了数据的潜伏特征,用于简化数据特征的表达,以便发现某种规律模式。潜伏空间搜索指的是在这个低维空间中寻找与特定查询或目标相关的点或区域的过程。
1.2.2 方法
潜伏空间搜索可以接纳多种方法,包罗但不限于偏最小二乘(PLS)、潜伏空间中的规则化匹配(RMLS)以及监督语义索引(SSI)等。这些方法通常涉及定义一个匹配函数,该函数将查询(如搜索查询或用户请求)和文档(或数据点)映射到潜伏空间,并盘算它们之间的相似度。
1.2.3 应用
潜伏空间搜索在信息检索、推荐体系、图像搜索等领域有着广泛的应用。比方,在图像搜索中,可以利用潜伏空间搜索来找到与查询图像在视觉上相似的其他图像。通过潜伏空间搜索,我们能够快速准确地找到与查询相关的内容,提高搜索效率和用户体验。
1.2.3 技能细节
潜伏空间搜索通常涉及将查询和文档映射到潜伏空间的映射函数。这些映射函数可以是线性的(如PLS)或非线性的(如通过深度神经网络学习得到的)。在某些情况下,可能必要学习这些映射函数,以便它们能够最好地捕获数据中的结构和关系。这通常涉及到优化目标函数,如基于点击数据的pointwise loss。
1.2.4 优势
潜伏空间搜索能够处理高维数据,并通过将数据映射到低维空间来减少盘算复杂度。它还能够捕获数据中的非线性关系,并发现传统方法可能无法发现的模式。这些优势使得潜伏空间搜索在处理复杂数据和发现隐藏模式方面更加有用。
1.2.5 挑战
潜伏空间搜索可能面临非凸优化问题的挑战,必要接纳合适的优化算法来找到全局最优解。别的,在某些情况下,可能必要大量的训练数据来学习有用的映射函数。这些挑战限制了潜伏空间搜索的适用性和结果,必要进一步的研究和探索来办理。
潜伏空间搜索是在深度学习和呆板学习中用于在潜伏空间中寻找与特定查询或目标相关的点或区域的过程。它接纳了多种方法和技能,并在多个领域有着广泛的应用。通过潜伏空间搜索,我们能够更有用地处理高维数据,并发现数据中的隐藏模式和结构。
1.3 Stable Diffusion简介
1.3.1 Stable Diffusion定义
Stable Diffusion是一种深度学习模型,特殊用于生成高质量的图像。它属于生成对抗网络(GANs)的一种,但与其他GANs相比,Stable Diffusion特殊夸大生成过程中的稳定性和高分辨率输出。该模型由Stability AI公司开发并于2022年发布。
1.3.2 核心技能
- 去噪扩散过程:Stable Diffusion的核心是一个去噪扩散过程,能够徐徐将随机噪声转化为具有清晰结构的图像。
- 模型架构:Stable Diffusion接纳了先辈的模型架构,特殊是其最新的版本Stable Diffusion 3,接纳了与Sora相同的DiT(Diffusion Transformer)架构,支持多主题提示,并改进了文字誊写结果。
1.3.3 Stable Diffusion特点
- 高质量图像生成:Stable Diffusion能够生成非常高质量的图像,包罗艺术作品、风景、人脸等,且分辨率可以非常高。最新的XL版本可以在1024*1024像素的级别上生成可控的图像。
- 稳定性:在生成图像时,Stable Diffusion减少了传统GANs中常见的不稳定性和模式崩塌问题。
- 速度和效率:该模型优化了盘算资源的利用,可以在相对较短的时间内生成图像,提高了实际应用中的效率。
- 文本到图像的生成:用户可以输入文本描述,模型会根据这些描述生成相应的图像。
- 开源与可控性:Stable Diffusion的部门版本是开源的,用户可以根据需求自由利用和修改模型。同时,用户可以较精确地控制生成图像的风格和内容。
1.3.4 应用领域
Stable Diffusion在艺术创作、游戏开发、虚拟实际等领域具有广泛的应用潜力。用户可以利用该模型生成各种高质量的图像内容,以满足不同的创作和娱乐需求。
1.3.5 挑战与前景
虽然Stable Diffusion已经取得了明显的结果,但其利用也伴随着责任和伦理考量。比方,必要制止滥用该技能生成虚假信息或侵犯版权的内容。未来,随着技能的不断发展和完善,Stable Diffusion有望在更多领域显现出其巨大的潜力和价值。
2. Stable Diffusion潜伏空间搜索的实现
2.1 技能概要
生成图像模型学习视觉世界的“潜伏空间”:这是一个低维向量空间,此中每个点映射到一个图像。从流形上的这样一个点回到一个可显示图像的过程称为“解码”——在Stable Diffusion模型中,这一过程由“解码器”模型来处理。
这个图像的潜伏空间是连续和可插值的,意味着:
- 在空间上稍微移动只会稍微改变相应的图像(连续性)。
- 对于空间上的任意两个点A和B(即任意两幅图像),可以通过一条路径从A移动到B,路径上的每个中间点也在流形上(也就是也是有用的图像)。这些中间点将被称为两个起始图像之间的“插值”。
Stable Diffusion不仅仅是一个图像模型,它也是一个自然语言模型。它有两个潜伏空间:一个是由训练期间利用的编码器学习到的图像体现空间,另一个是提示潜伏空间,这是通过预训练和训练时微调的组合学习到的。
“潜伏空间探索”,是采样潜伏空间中的一个点并徐徐改变潜伏体现的过程。它最常见的应用是生成动画,此中每个采样点被送入解码器,并存储为最终动画的一帧。对于高质量的潜伏体现,这可以产生连贯的动画。这些动画可以提供潜伏空间特征图的洞察,并最终带来训练过程的改进。
本文将展示如何利用KerasCV中的Stable Diffusion API来执行提示插值,以及通过Stable Diffusion的视觉潜伏空间和文本编码器的潜伏空间进行循周游走。
2.1.1 设置
首先,我们导入KerasCV并利用教程中讨论的优化加载Stable Diffusion模型。
- !pip install keras-cv --upgrade --quiet
复制代码- import keras_cv
- import keras
- import matplotlib.pyplot as plt
- from keras import ops
- import numpy as np
- import math
- from PIL import Image
- # 启用混合精度
- # (只有在您有较新的NVIDIA GPU时才这样做)
- keras.mixed_precision.set_global_policy("mixed_float16")
- # 实例化Stable Diffusion模型
- model = keras_cv.models.StableDiffusion(jit_compile=True)
复制代码- 通过使用此模型检查点,您承认其使用受CreativeML Open RAIL-M许可条款的约束,详情请见 https://raw.githubusercontent.com/CompVis/stable-diffusion/main/LICENSE
复制代码 2.2 在文本提示之间进行插值
在Stable Diffusion中,文本提示首先被编码成向量,并且该编码用来引导扩散过程。潜伏编码向量的形状是77x768(非常大!),当我们给Stable Diffusion一个文本提示时,我们实际上是从潜伏流形上的这样一个点生成图像。
为了探索这个流形的更多部门,我们可以在两个文本编码之间进行插值,并在这些插值点生成图像。
下面的代码演示了如何通过两个文本提示来生成它们在潜伏空间中的编码,并在这些编码之间进行插值。首先,我们定义了两个文本提示:一个描述了一只金毛寻回犬在海滩上的水彩画,另一个描述了一碗水果的静物DSLR照片。然后,我们设置了一个插值步骤数,即5,这意味着我们希望在两个编码之间生成5个等距的点。
接下来,我们利用模型的encode_text()函数对这两个文本提示进行编码,将它们转换为潜伏空间中的向量。为了确保编码结果具有正确的形状,我们利用了ops.squeeze()函数来去除任何单一维度。
一旦我们有了这两个编码,我们就可以利用ops.linspace()函数在它们之间进行线性插值。这个函数会生成一个数组,此中包含interpolation_steps个等距的点,每个点都位于原始的两个编码之间。
最后,为了了解潜伏空间中的编码大小,我们打印了第一个编码(encoding_1)的形状。由于两个编码都是通过相同的函数生成的,并且没有改变其形状,所以第二个编码(encoding_2)的形状也将是相同的。这个形状通常体现了潜伏空间中向量的维度。
- prompt_1 = "一只金毛寻回犬在海滩上的水彩画"
- prompt_2 = "一碗水果的静物DSLR照片"
- interpolation_steps = 5
- encoding_1 = ops.squeeze(model.encode_text(prompt_1))
- encoding_2 = ops.squeeze(model.encode_text(prompt_2))
- interpolated_encodings = ops.linspace(encoding_1, encoding_2, interpolation_steps)
- # 显示潜在流形的大小
- print(f"编码形状: {encoding_1.shape}")
复制代码- Downloading data from https://github.com/openai/CLIP/blob/main/clip/bpe_simple_vocab_16e6.txt.gz?raw=true
- 1356917/1356917 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step
- Downloading data from https://huggingface.co/fchollet/stable-diffusion/resolve/main/kcv_encoder.h5
- 492466864/492466864 ━━━━━━━━━━━━━━━━━━━━ 7s 0us/step
- Encoding shape: (77, 768)
复制代码 一旦我们进行了编码插值,我们就可以生成每个点的图像。留意,为了保持结果图像之间的稳定性,我们保持图像之间的扩散噪声不变。
首先,设置一个随机数种子seed,以确保结果的可复现性。程序员利用这个种子生成了一组具有特定形状和维度的随机噪声noise。在这里,噪声的形状是(512 // 8, 512 // 8, 4),这可能对应于某个特定分辨率图像的降采样版本(通常扩散模型会在较低分辨率上工作,然后徐徐上采样到目标分辨率),而4可能代表噪声的不同通道或时间步长。
接下来,调用模型的generate_image方法,并通报了之前盘算得到的插值编码interpolated_encodings、批处理大小batch_size(设置为插值步骤数interpolation_steps),以及前面生成的随机噪声noise。
- seed = 12345
- noise = keras.random.normal((512 // 8, 512 // 8, 4), seed=seed)
- images = model.generate_image(
- interpolated_encodings,
- batch_size=interpolation_steps,
- diffusion_noise=noise,
- )
复制代码- Downloading data from https://huggingface.co/fchollet/stable-diffusion/resolve/main/kcv_diffusion_model.h5
- 3439090152/3439090152 ━━━━━━━━━━━━━━━━━━━━ 26s 0us/step
- 50/50 ━━━━━━━━━━━━━━━━━━━━ 173s 311ms/step
- Downloading data from https://huggingface.co/fchollet/stable-diffusion/resolve/main/kcv_decoder.h5
- 198180272/198180272 ━━━━━━━━━━━━━━━━━━━━ 1s 0us/step
复制代码 如今我们已经生成了一些插值图像,让我们来看一下它们!
本文的源代码将把图像序列导出为gif,以便它们可以容易地在时间上下文中查看。对于概念上第一张和最后一张图像不匹配的图像序列,我们将橡皮筋绑在gif上。
- from IPython.display import Image as IImage
- IImage("doggo-and-fruit-5.gif")
复制代码- def export_as_gif(filename, images, frames_per_second=10, rubber_band=False):
- if rubber_band:
- images += images[2:-1][::-1]
- images[0].save(
- filename,
- save_all=True,
- append_images=images[1:],
- duration=1000 // frames_per_second,
- loop=0,
- )
- export_as_gif(
- "doggo-and-fruit-5.gif",
- [Image.fromarray(img) for img in images],
- frames_per_second=2,
- rubber_band=True,
- )
复制代码 结果可能会令人惊讶。通常,在提示之间进行插值会产生看起来连贯的图像,并且经常展示两个提示内容之间的渐进概念转变。这表明白一个高质量的体现空间,它精密地反映了视觉世界的自然结构。
为了更好地可视化这一点,我们应该进行更细粒度的插值,利用数百个步骤。为了保持批量大小小(这样我们就不会耗尽GPU内存),这必要我们手动批量处理我们的插值编码。
首先,设置了插值步骤数interpolation_steps为150,这意味着我们想要在文本描述1(encoding_1)和文本描述2(encoding_2)之间生成150个等距的插值点。然后,设置了批处理大小batch_size为3,这意味着每次处理3个插值点来生成图像。
接下来,通过ops.linspace函数在encoding_1和encoding_2之间进行线性插值,生成了interpolated_encodings数组,该数组包含了150个等距的插值点。
由于我们设置了批处理大小,所以必要将interpolated_encodings数组分割成多个批次。利用ops.split函数,我们根据batches的数量将interpolated_encodings分割成了多个部门,每个部门包含3个插值点(除了最后一个批次可能少于3个)。
然后,我们初始化一个空列表images来存储生成的图像。通过一个循环,我们遍历每个批次,并利用model.generate_image方法根据该批次的插值编码生成图像。model.generate_image方法吸收当前批次的插值编码、批处理大小(在这里是固定的3)、生成图像所需的步骤数(num_steps设置为25)、以及之前生成的随机噪声noise作为参数。
对于每个生成的图像,我们利用Image.fromarray(img)将其转换为PIL图像对象,并添加到images列表中。
最后,我们利用export_as_gif函数(这个函数不是Python标准库或常见图像处理库中的函数,可能是自定义的或来自某个特定库)将所有生成的图像导出为一个GIF动画文件,文件名为"doggo-and-fruit-150.gif"。rubber_band=True可能是一个参数,用于在生成GIF时应用某种平滑或过渡结果。
- interpolation_steps = 150
- batch_size = 3
- batches = interpolation_steps // batch_size
- interpolated_encodings = ops.linspace(encoding_1, encoding_2, interpolation_steps)
- batched_encodings = ops.split(interpolated_encodings, batches)
- images = []
- for batch in range(batches):
- images += [
- Image.fromarray(img)
- for img in model.generate_image(
- batched_encodings[batch],
- batch_size=batch_size,
- num_steps=25,
- diffusion_noise=noise,
- )
- ]
- export_as_gif("doggo-and-fruit-150.gif", images, rubber_band=True)
复制代码- 25/25 ━━━━━━━━━━━━━━━━━━━━━━ 77s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 208ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 211ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 215ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 203ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 211ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 215ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 208ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 210ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 203ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 213ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 211ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 216ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 207ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 209ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 213ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 213ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 203ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 208ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 213ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 208ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 213ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
复制代码
生成的gif展示了两个提示之间更清晰、更连贯的变化。试试您自己的提示并进行实行!
我们乃至可以扩展这个概念到多于一个图像。比方,我们可以在四个提示之间进行插值:
- prompt_1 = "海滩上的金毛寻回犬的水彩画"
- prompt_2 = "一碗水果的静物DSLR照片"
- prompt_3 = "星空下的埃菲尔铁塔"
- prompt_4 = "摩天大楼的建筑草图"
- interpolation_steps = 6
- batch_size = 3
- batches = (interpolation_steps**2) // batch_size
- encoding_1 = ops.squeeze(model.encode_text(prompt_1))
- encoding_2 = ops.squeeze(model.encode_text(prompt_2))
- encoding_3 = ops.squeeze(model.encode_text(prompt_3))
- encoding_4 = ops.squeeze(model.encode_text(prompt_4))
- interpolated_encodings = ops.linspace(
- ops.linspace(encoding_1, encoding_2, interpolation_steps),
- ops.linspace(encoding_3, encoding_4, interpolation_steps),
- interpolation_steps,
- )
- interpolated_encodings = ops.reshape(
- interpolated_encodings, (interpolation_steps**2, 77, 768)
- )
- batched_encodings = ops.split(interpolated_encodings, batches)
- images = []
- for batch in range(batches):
- images.append(
- model.generate_image(
- batched_encodings[batch],
- batch_size=batch_size,
- diffusion_noise=noise,
- )
- )
复制代码- def plot_grid(images, path, grid_size, scale=2):
- fig, axs = plt.subplots(
- grid_size, grid_size, figsize=(grid_size * scale, grid_size * scale)
- )
- fig.tight_layout()
- plt.subplots_adjust(wspace=0, hspace=0)
- plt.axis("off")
- for ax in axs.flat:
- ax.axis("off")
- images = images.astype(int)
- for i in range(min(grid_size * grid_size, len(images))):
- ax = axs.flat[i]
- ax.imshow(images[i].astype("uint8"))
- ax.axis("off")
- for i in range(len(images), grid_size * grid_size):
- axs.flat[i].axis("off")
- axs.flat[i].remove()
- plt.savefig(
- fname=path,
- pad_inches=0,
- bbox_inches="tight",
- transparent=False,
- dpi=60,
- )
- images = np.concatenate(images)
- plot_grid(images, "4-way-interpolation.jpg", interpolation_steps)
复制代码- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 209ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 204ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 209ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 11s 210ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 210ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 205ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 11s 210ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 210ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 208ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 205ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 210ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 11s 210ms/step
复制代码
我们还可以在插值时允许扩散噪声变化,方法是省略diffusion_noise参数:
- images = []
- for batch in range(batches):
- images.append(model.generate_image(batched_encodings[batch], batch_size=batch_size))
- images = np.concatenate(images)
- plot_grid(images, "4-way-interpolation-varying-noise.jpg", interpolation_steps)
复制代码- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 11s 215ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 13s 254ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 12s 235ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 12s 230ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 11s 214ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 210ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 208ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 11s 210ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 209ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 208ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 10s 205ms/step
- # 50/50 ━━━━━━━━━━━━━━━━━━━━ 11s 213ms/step
复制代码
2.3 围绕文本提示进行搜索
我们的下一个实行将从特定提示产生的点开始,在潜伏空间周围搜索。
首先先,我们设定了行走的步数walk_steps为150,以及每次生成图像时模型处理的批次大小batch_size为3。由此盘算出总共必要的批次数batches,它是通过walk_steps除以batch_size得到的。我们还设定了每一步的步长step_size为0.005。
接着,我们利用模型将文本提示“星夜风格的埃菲尔铁塔”编码为潜伏空间中的向量encoding,并通过ops.squeeze()函数去除任何多余的维度。这个编码的形状是(77, 768)。
然后,我们创建了一个与编码相同形状的单位矩阵delta,并将其所有元素乘以步长step_size。这个delta将作为每次在潜伏空间中“行走”的步长。
在for循环中,我们模拟了在潜伏空间中的“行走”过程。对于每一步,我们都将encoding添加到walked_encodings列表中,并将encoding增长delta以进行下一步。留意,这个“行走”过程是在潜伏空间中进行的,而不是在图像空间。
完成所有步数的“行走”后,我们利用ops.stack()函数将walked_encodings列表中的所有编码堆叠成一个数组。然后,我们利用ops.split()函数将这个数组分割成多个批次,每个批次包含batch_size个编码。
接下来,我们再次利用for循环遍历每个批次,并利用模型为每个批次的编码生成图像。生成的图像被转换为PIL的Image对象,并添加到images列表中。在生成图像时,我们指定了生成步骤数num_steps为25,并提供了预定义的噪声noise。
最后,我们利用一个假设的函数export_as_gif()将所有生成的图像导出为一个GIF动画文件eiffel-tower-starry-night.gif。这个函数可能还接受一个rubber_band参数,用于控制GIF动画的平滑度。
- walk_steps = 150
- batch_size = 3
- batches = walk_steps // batch_size
- step_size = 0.005
- encoding = ops.squeeze(
- model.encode_text("星夜风格的埃菲尔铁塔")
- )
- # 注意,文本编码的形状是(77, 768)。
- delta = ops.ones_like(encoding) * step_size
- walked_encodings = []
- for step_index in range(walk_steps):
- walked_encodings.append(encoding)
- encoding += delta
- walked_encodings = ops.stack(walked_encodings)
- batched_encodings = ops.split(walked_encodings, batches)
- images = []
- for batch in range(batches):
- images += [
- Image.fromarray(img)
- for img in model.generate_image(
- batched_encodings[batch],
- batch_size=batch_size,
- num_steps=25,
- diffusion_noise=noise,
- )
- ]
- export_as_gif("eiffel-tower-starry-night.gif", images, rubber_band=True)
复制代码- 25/25 ━━━━━━━━━━━━━━━━━━━━ 6s 228ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 210ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 208ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 210ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 207ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 209ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 218ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 210ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 210ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 215ms/step
复制代码 可能不敷为奇,搜索离编码器的潜伏空间太远会产生看起来不连贯的图像。程序员可以通过设置自己的提示,并调整step_size来增长或减少散步的幅度来切身实行。留意,当散步的幅度变大时,散步经常会进入产生极其嘈杂图像的区域。
2.4 单个提示的扩散噪声空间搜索
我们最后的实行是利用同一个提示,并探索扩散模型可以从该提示产生的形象多样性。我们通过控制用于种子扩散过程的噪声来做到这一点。
我们创建两个噪声分量,x和y,并从0到2π进行散步,将我们的x分量的余弦和我们的y分量的正弦相加以产生噪声。通过这种方法,我们的散步结束时到达与开始时相同的噪声输入,因此我们得到一个“可循环”的结果!
首先,定义了一个文本提示prompt,即“荷兰风车旁田野中的奶牛油画”,然后利用模型model的encode_text方法将其编码为潜伏空间中的向量encoding,并利用ops.squeeze去除任何多余的维度。
接下来,设置了行走步数walk_steps为150,批次大小batch_size为3,并盘算出所需的批次数batches。
为了生成不同的噪声,代码利用了两个随机噪声向量walk_noise_x和walk_noise_y,它们的形状与预定义的噪声noise相同,并利用了Keras的random.normal函数来生成。
然后,代码定义了两组比例因子walk_scale_x和walk_scale_y,它们通过余弦和正弦函数以及线性空间函数ops.linspace生成,这样可以在不同的步数中引入不同的噪声权重。
接着,通过ops.tensordot函数将比例因子与噪声向量相乘,得到两个新的噪声向量noise_x和noise_y。然后,将这两个噪声向量相加得到最终的噪声noise。
由于生成过程是分批进行的,因此将总的噪声noise分割为多个批次batched_noise,每个批次的大小由batch_size决定。
如今,开始生成图像。在循环中,对于每个批次,利用模型model的generate_image方法生成图像。这里将固定的encoding(即文本提示的编码)作为输入,同时传入当前的噪声批次batched_noise[batch]、批次大小batch_size、生成步骤数num_steps(设为25)以及其他可能的参数(如diffusion_noise)。生成的图像被转换为PIL的Image对象,并添加到images列表中。
最后,利用假设的函数export_as_gif将所有生成的图像导出为一个名为cows.gif的GIF动画文件。
- prompt = "荷兰风车旁田野中的奶牛油画"
- encoding = ops.squeeze(model.encode_text(prompt))
- walk_steps = 150
- batch_size = 3
- batches = walk_steps // batch_size
- walk_noise_x = keras.random.normal(noise.shape, dtype="float64")
- walk_noise_y = keras.random.normal(noise.shape, dtype="float64")
- walk_scale_x = ops.cos(ops.linspace(0, 2, walk_steps) * math.pi)
- walk_scale_y = ops.sin(ops.linspace(0, 2, walk_steps) * math.pi)
- noise_x = ops.tensordot(walk_scale_x, walk_noise_x, axes=0)
- noise_y = ops.tensordot(walk_scale_y, walk_noise_y, axes=0)
- noise = ops.add(noise_x, noise_y)
- batched_noise = ops.split(noise, batches)
- images = []
- for batch in range(batches):
- images += [
- Image.fromarray(img)
- for img in model.generate_image(
- encoding,
- batch_size=batch_size,
- num_steps=25,
- diffusion_noise=batched_noise[batch],
- )
- ]
- export_as_gif("cows.gif", images)
复制代码- 25/25 ━━━━━━━━━━━━━━━━━━━━ 35s 216ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 216ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 210ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 208ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 215ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 207ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 213ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 207ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 216ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 209ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 216ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 209ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 213ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 207ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 218ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 207ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 211ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 210ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 217ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 204ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 208ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 214ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 207ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 215ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 212ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 209ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 216ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 205ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 206ms/step
- 25/25 ━━━━━━━━━━━━━━━━━━ 5s 214ms/step
复制代码
程序员可以实行利用自己的文本提示,并实行不同的unconditional_guidance_scale值!
3. 实行源代码
- """代码是关于如何利用Stable Diffusion模型在潜伏空间中进行文本提示插值和搜索的。```shell# 安装keras-cv库,升级到最新版本并保持安静(不显示安装过程)pip install keras-cv --upgrade --quiet"""# 导入所需的库import keras_cvimport kerasimport matplotlib.pyplot as pltfrom keras import opsimport numpy as npimport mathfrom PIL import Image# 启用混淆精度(仅适用于较新的NVIDIA GPU)keras.mixed_precision.set_global_policy("mixed_float16")# 实例化Stable Diffusion模型model = keras_cv.models.StableDiffusion(jit_compile=True)## 在文本提示之间进行插值"""在Stable Diffusion中,文本提示首先被编码成向量,并用于引导扩散过程。潜伏编码向量的形状是77x768,给定Stable Diffusion一个文本提示时,我们实际上是从潜伏流形上的这样一个点生成图像。为了探索这个流形的更多部门,我们可以在两个文本编码之间进行插值,并在这些插值点生成图像:"""# 定义两个文本提示prompt_1 = "海滩上的水彩画,画的是一只金毛寻回犬"prompt_2 = "一碗水果的静物DSLR照片"interpolation_steps = 5# 对文本提示进行编码encoding_1 = ops.squeeze(model.encode_text(prompt_1))encoding_2 = ops.squeeze(model.encode_text(prompt_2))# 在两个编码之间进行插值interpolated_encodings = ops.linspace(encoding_1, encoding_2, interpolation_steps)# 显示潜伏流形的大小print(f"编码形状: {encoding_1.shape}")"""一旦我们进行了编码插值,我们就可以生成每个点的图像。为了保持生成图像间的稳定性,我们保持图像间的扩散噪声不变。"""# 设置随机种子并生成扩散噪声seed = 12345noise = keras.random.normal((512 // 8, 512 // 8, 4), seed=seed)# 生成插值编码的图像images = model.generate_image( interpolated_encodings, batch_size=interpolation_steps, diffusion_noise=noise,)"""如今我们已经生成了一些插值图像,让我们来看看它们!整个教程中,我们将把图像序列导出为gif,这样它们就可以在时间上下文中轻松查看。对于概念上第一张和最后一张图像不匹配的图像序列,我们将橡皮筋绑在gif上。如果您在Colab中运行,您可以通过运行以下代码查看自己的GIF:"""from IPython.display import Image as IImage
- IImage("doggo-and-fruit-5.gif")
- """定义一个函数,将图像序列导出为GIF格式:"""def export_as_gif(filename, images, frames_per_second=10, rubber_band=False): if rubber_band: images += images[2:-1][::-1] # 如果必要,添加橡皮筋结果 images[0].save( filename, save_all=True, append_images=images[1:], duration=1000 // frames_per_second, loop=0, )# 导出GIFexport_as_gif( "doggo-and-fruit-5.gif", [Image.fromarray(img) for img in images], frames_per_second=2, rubber_band=True,)"""### 接下来是关于如安在潜伏空间中进行“散步”的实行#### 围绕文本提示的散步我们的下一个实行将从特定提示产生的点开始,在潜伏流形周围散步。"""walk_steps = 150 # 散步的步数batch_size = 3 # 批量大小batches = walk_steps // batch_size # 盘算批次数量step_size = 0.005 # 步长# 编码文本提示 "星夜风格的埃菲尔铁塔"encoding = ops.squeeze(model.encode_text("The Eiffel Tower in the style of starry night"))delta = ops.ones_like(encoding) * step_size # 创建步进向量# 存储散步过程中的编码walked_encodings = []for step_index in range(walk_steps): walked_encodings.append(encoding) encoding += deltawalked_encodings = ops.stack(walked_encodings) # 将编码堆叠成一个列表batched_encodings = ops.split(walked_encodings, batches) # 将编码分批# 生成图像images = []for batch in range(batches): images += [ Image.fromarray(img) for img in model.generate_image( batched_encodings[batch], batch_size=batch_size, num_steps=25, diffusion_noise=noise, ) ]# 导出散步过程为GIFexport_as_gif("eiffel-tower-starry-night.gif", images, rubber_band=True)"""#### 在单个提示的扩散噪声空间中进行圆形散步我们的最后一个实行是坚持利用一个提示,并探索扩散模型可以从该提示产生的形象多样性。我们通过控制用于种子扩散过程的噪声来实现这一点。我们创建两个噪声分量,`x` 和 `y`,并从 0 到 2π 进行散步,将 `x` 分量的余弦和 `y` 分量的正弦相加以产生噪声。通过这种方法,我们的散步结束时到达与开始时相同的噪声输入,因此我们得到一个“可循环”的结果。"""prompt = "荷兰风车旁田野中的奶牛油画"encoding = ops.squeeze(model.encode_text(prompt))walk_steps = 150batch_size = 3batches = walk_steps // batch_size# 创建噪声分量和散步比例walk_noise_x = keras.random.normal(noise.shape, dtype="float64")walk_noise_y = keras.random.normal(noise.shape, dtype="float64")walk_scale_x = ops.cos(ops.linspace(0, 2, walk_steps) * math.pi)walk_scale_y = ops.sin(ops.linspace(0, 2, walk_steps) * math.pi)noise_x = ops.tensordot(walk_scale_x, walk_noise_x, axes=0)noise_y = ops.tensordot(walk_scale_y, walk_noise_y, axes=0)noise = ops.add(noise_x, noise_y)batched_noise = ops.split(noise, batches)# 生成图像images = []for batch in range(batches): images += [ Image.fromarray(img) for img in model.generate_image( encoding, batch_size=batch_size, num_steps=25, diffusion_noise=batched_noise[batch], ) ]# 导出散步过程为GIFexport_as_gif("cows.gif", images)"""实行利用您自己的提示,并实行不同的 `unconditional_guidance_scale` 值!结果可能会令人惊讶。通常,在提示之间进行插值会产生看起来连贯的图像,并且经常展示两个提示内容之间的渐进概念转变。这表明白一个高质量的体现空间,它精密地反映了视觉世界的自然结构。为了更好地可视化这一点,我们应该进行更细粒度的插值,利用数百个步骤。为了保持批量大小小(这样我们就不会耗尽GPU内存),这必要我们手动批量处理我们的插值编码。接下来的部门代码展示了如何进行更细粒度的插值,创建四向插值,以及如安在潜伏流形周围进行“散步”。最后,代码展示了如何通过控制扩散过程中利用的噪声来探索扩散模型从一个提示生成的图像多样性。"""
复制代码 4. 总结
本文关于Stable Diffusion模型利用于潜伏空间搜索的讨论,详细地介绍了如何探索这个模型所学习的潜伏空间。
Stable Diffusion是一个强大的生成模型,它能够学习视觉世界的潜伏流形,这是一个低维向量空间,此中每个点都对应一个图像。在Stable Diffusion中,将一个潜伏空间中的点解码回一个可显示的图像是由解码器模型处理的。这个潜伏流形是连续和可插值的,意味着小的移动只会导致图像的小变化,并且任何两个点之间都可以通过流形上的路径进行插值。
Stable Diffusion不仅是一个图像模型,它照旧一个自然语言模型,拥有两个潜伏空间:一个由训练期间利用的编码器学习得到的图像体现空间,另一个是通过预训练和训练时微调学习到的提示潜伏空间。在潜伏空间中游走,即采样潜伏空间中的点并徐徐改变潜伏体现,是生成动画的常见应用,这些动画可以提供潜伏空间特征图的洞察,从而可能带来训练过程的改进。
本文利用KerasCV中的Stable Diffusion API,引导读者如何执行提示插值,以及如安在Stable Diffusion的视觉潜伏流形和文本编码器的潜伏流形中进行循周游走。
本文提供了详细的代码示例,包罗如何安装KerasCV库、实例化Stable Diffusion模型、进行文本编码插值、生成图像,以及将图像序列导出为GIF动画。它还描述了几种实行,如在文本提示周围进行“散步”,以及通过控制扩散过程中利用的噪声来探索模型可以产生的形象多样性。
Stable Diffusion不仅仅是单一的文本到图像生成工具。探索文本编码器的潜伏流形和扩散模型的噪声空间是两种风趣的方式,可以让程序员体验到这个模型的强大功能。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |