diffusers pipeline拆解:理解pipelines、models和schedulers

diffusers pipeline拆解:理解pipelines、models和schedulers

翻译自:https://huggingface.co/docs/diffusers/using-diffusers/write_own_pipeline v0.24.0

diffusers 设计初衷就是作为一个简单且易用的工具包,来帮助你在自己的使用场景中构建 diffusion 系统。diffusers 的核心是 models 和 schedulers。而 DiffusionPipeline 则将这些组件打包到一起,从而可以简便地使用。在了解其中原理之后,你也可以将这些组件(models 和 schedulers)拆开,来构建适合自己场景的 diffusion 系统。

本文将介绍如何使用 models 和 schedulers 来组建一个 diffusion 系统用作推理生图。我们先从最基础的 DDPMPipeline 开始,然后介绍更复杂、更常用的 StableDiffusionPipeline。

解构DDPMPipeline

以下是 DDPMPipeline 构建和推理的示例:

from diffusers import DDPMPipelineddpm = DDPMPipeline.from_pretrained("google/ddpm-cat-256", use_safetensors=True).to("cuda")
image = ddpm(num_inference_steps=25).images[0]
image

在这里插入图片描述

这就是 diffusers 中使用 pipeline 进行推理生图的全部步骤了,是不是超级简单!那么,在 pipeline 背后实际上都做了什么呢?我们接下来将 pipeline 拆解开,看一下它具体做了什么事。

我们提到,pipeline 主要的组件是 models 和 schedulers,在上面的 DDPMPipeline 中,就包含了 UNet2DModel 和 DDPMScheduler。该 pipeline 首先产生一个与输出图片尺寸相同的噪声图,在每个时间步(timestep),将噪声图传给 model 来预测噪声残差(noise residual),然后 scheduler 会根据预测出的噪声残差得到一张噪声稍小的图像,如此反复,直到达到预设的最大时间步,就得到了一张高质量生成图像。

我们可以不直接调用 pipeline 的 API,根据下面的步骤自己走一遍 pipeline 做的事情:

加载模型 model 和 scheduler
from diffusers import DDPMScheduler, UNet2DModelscheduler = DDPMScheduler.from_pretrained("google/ddpm-cat-256")
model = UNet2DModel.from_pretrained("google/ddpm-cat-256", use_safetensors=True).to("cuda")
设置timesteps
scheduler.set_timesteps(50)
scheduler.timesteps
# 输出:
tensor([980, 960, 940, 920, 900, 880, 860, 840, 820, 800, 780, 760, 740, 720,700, 680, 660, 640, 620, 600, 580, 560, 540, 520, 500, 480, 460, 440,420, 400, 380, 360, 340, 320, 300, 280, 260, 240, 220, 200, 180, 160,140, 120, 100,  80,  60,  40,  20,   0])

在对 scheduler 设置好总的去噪步数之后,ddpm scheduler 会创建一组均匀间隔的数组,本例中我们将 temesteps 设置为 50,所以该数组的长度为 50。在进行去噪时,数组中的每个元素对应了一个时间步,在之后不断循环的去噪中,我们在每一步会遍历用到这个数组的元素。

采样随机噪声

采样一个与输出图片尺寸相同的随机噪声:

import torchsample_size = model.config.sample_size
noise = torch.randn((1, 3, sample_size, sample_size), device="cuda")
实现迭代去噪循环

然后我们写一个循环,来迭代这些时间步。在每个 step,UNet2DModel 都会进行一次 forward,并返回预测的噪声残差。scheduler 的 step 方法接收 噪声残差 noisy_residual 、当前时间步 tinput 作为输入,输出前一时间步的噪声稍小的图片。然后该输出会作为下一时间步的模型输入。反复迭代这个过程,直到将 timesteps 迭代完。

input = noisefor t in scheduler.timesteps:with torch.no_grad():noisy_residual = model(input, t).sampleprevious_noisy_sample = scheduler.step(noisy_residual, t, input).prev_sampleinput = previous_noisy_sample

以上就是完整的去噪过程了,你也可以使用类似的方式来实现自己的 diffusion 系统。

  1. 最后一步我们将去噪输出转换为 pillow 图片,看一下结果:

    from PIL import Image
    import numpy as npimage = (input / 2 + 0.5).clamp(0, 1).squeeze()
    image = (image.permute(1, 2, 0) * 255).round().to(torch.uint8).cpu().numpy()
    image = Image.fromarray(image)
    image
    

以上就是基础的 DDPMPipeline 背后实际做的事情了。首先,初始化 model 和 scheduler,然后为 scheduler 设置最大时间步,创建一个时间步数组,然后我们采样一个随机噪声,循环遍历 timestep,在每个 step,模型会预测出一个噪声残差,scheduler 根据这个噪声残差来生成一个噪声稍小的图片,如此迭代,直到走完所有 step。

接下来我们将看一下更复杂、更强大的 StableDiffusionPipeline,整体的步骤与上面的 DDPMPipeline 类似。

解构StableDiffusionPipeline

Stable Diffusion 是一种 latent diffusion 的文生图模型。所谓 latent diffusion,指的是其扩散过程是发生在低维度的隐层空间,而非真实的像素空间。这样的模型比较省内存。vae encoder 将图片压缩成一个低维的表示,vae decoder 则负责将压缩特征转换回为真实图片。对于文生图的模型,我们还需要一个 tokenizer 和一个 text encoder 来生成 text embedding,还有,在前面的 DDPMPipeline 中已经提到的 Unet model 和 scheduler。可以看到,Stable Diffusion 已经比 DDPM pipeline 要复杂的多了,它包含了三个独立的预训练模型。

加载模型、设置参数

现在我们先将各个组件通过 from_pretrained 方法加载进来。这里我们先用 SD1.5 的预训练权重,每个组件存放在不同的子目录中:

from PIL import Image
import torch
from transformers import CLIPTextModel, CLIPTokenizer
from diffusers import AutoencoderKL, UNet2DConditionModel, PNDMSchedulervae = AutoencoderKL.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="vae", use_safetensors=True)
tokenizer = CLIPTokenizer.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="tokenizer")
text_encoder = CLIPTextModel.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="text_encoder", use_safetensors=True
)
unet = UNet2DConditionModel.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="unet", use_safetensors=True
)

这里我们使用 UniPCMultistepScheduler 来替换掉默认的 PNDMScheduler。没别的意思,就为了展示一下替换一个其他的 scheduler 组件有多么简单:

from diffusers import UniPCMultistepSchedulerscheduler = UniPCMultistepScheduler.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="scheduler")

然后将各个模型放到 cuda 上:

torch_device = "cuda"
vae.to(torch_device)
text_encoder.to(torch_device)
unet.to(torch_device)

配置一些参数:

prompt = ["a photograph of an astronaut riding a horse"] # prompt按自己喜好设置,想生成什么就描述什么
height = 512  # SD 默认高
width = 512  # SD 默认款
num_inference_steps = 25  # 去噪步数
guidance_scale = 7.5  # classifier-free guidance (CFG) scale
generator = torch.manual_seed(0)  # 随机种子生成器,用于控制初始的噪声图
batch_size = len(prompt)

其中 guidance_scale 参数表示图片生成过程中考虑 prompt 的权重。

创建 text embedding

接下来,我们来对条件 prompt 进行 tokenize,并通过 text encoder 模型产生文本 embedding:

text_input = tokenizer(prompt, padding="max_length", max_length=tokenizer.model_max_length, truncation=True, return_tensors="pt"
)with torch.no_grad():text_embeddings = text_encoder(text_input.input_ids.to(torch_device))[0]

我们还需要产生无条件的 text tokens,其完全有 padding token 组成,然后经过 text encoder,得到 uncond_embedding 的 batch_size 和 seq_length 需要与刚刚得到的条件 text embedding 相等。我们将 条件 embedding 和无条件 embedding 拼起来,从而进行并行的 forward:

max_length = text_input.input_ids.shape[-1]
uncond_input = tokenizer([""] * batch_size, padding="max_length", max_length=max_length, return_tensors="pt")
uncond_embeddings = text_encoder(uncond_input.input_ids.to(torch_device))[0]text_embeddings = torch.cat([uncond_embeddings, text_embeddings])
采样随机噪声

之前提到,SD 的扩散过程是在低维度的 latent 空间进行的,因此此时采样的随机噪声的尺寸比最终生成图片小。对这个 latent 噪声进行迭代去噪。我们随后会通过 vae decoder 将它解码到真实图片的尺寸,即 512。

vae enoder (在 img2img 中使用, text2img 不需要) 和 vae decoder 分别用于将真实尺寸的图片映射到低维 latent 空间,和将低维 latent 解码为真实图片。由于 vae 有三个降采样层,每次会将图片尺寸缩小一半,从而总共缩小了 2**3=8 倍,因此我们将原图的尺寸缩小 8 倍,得到 latent 空间的噪声尺寸。

# 2 ** (len(vae.config.block_out_channels) - 1) == 8latents = torch.randn((batch_size, unet.config.in_channels, height // 8, width // 8),generator=generator,device=torch_device,
)
对图像进行去噪

首先我们要先对噪声进行放缩,乘上一个系数 sigma,这可以提升某些 schedulers 的效果,比如我们刚替换的 UniPCMultistepScheduler:

latents = latents * scheduler.init_noise_sigma

然后,我们写一个循环,将 latent 空间的纯噪声一步步地去噪为关于我们 prompt 的 latent 图。和之前 DDPM 的循环类似,整体上我们要做三件事情:

  1. 设置 scheduler 的总去噪步数
  2. 迭代进行这些去噪步
  3. 在每一步,使用 UNet model 来预测噪声残差,并将其传给 scheduler ,生成出上一步的噪声图片

不同的是,我们这里的 SD 需要做 classifer-guidance generation:

from tqdm.auto import tqdmscheduler.set_timesteps(num_inference_steps)for t in tqdm(scheduler.timesteps):# 我们要做 classifier-guidance generation,所以先扩一下 latent,方便并行推理latent_model_input = torch.cat([latents] * 2)latent_model_input = scheduler.scale_model_input(latent_model_input, timestep=t)# 预测噪声残差with torch.no_grad():noise_pred = unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample# 进行引导noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)# 生成前一步的 x_t -> x_t-1latents = scheduler.step(noise_pred, t, latents).prev_sample
图片解码

最后一步我们使用 vae decoder 来对去噪之后 latent representation 进行解码生成出真实图片。并转换成 pillow image 查看结果。

# scale and decode the image latents with vae
latents = 1 / 0.18215 * latents
with torch.no_grad():image = vae.decode(latents).sampleimage = (image / 2 + 0.5).clamp(0, 1).squeeze()
image = (image.permute(1, 2, 0) * 255).to(torch.uint8).cpu().numpy()
images = (image * 255).round().astype("uint8")
image = Image.fromarray(image)
image

在这里插入图片描述

从基础的 DDPMPipeline 到更复杂的 StableDiffusionPipeline,我们了解了如何构建自己的 diffusion 系统。关键就是在迭代去噪循环的视线。主要包含设定 timesteps、遍历 timesteps 并交替使用 UNet model 进行噪声预测和使用 scheduler 进行前一步图的计算。这就是 diffusers 库的设计理念,既可以直接通过封装好的 pipeline 直接生图,也可以用其中的各个组件方便地自己构建 diffusion 系统的 pipeline。

下一步,我们可以:

  1. 探索其他 diffusers 库中已有的 pipeline,像本文介绍的那样试着自己对其进行结构,并自行从头实现。
  2. 试着自己构造一个全新的 pipeline 并贡献到 diffusers 库 参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/215427.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django讲课笔记02:Django环境搭建

文章目录 一、学习目标二、相关概念(一)Python(二)Django 三、环境搭建(一)安装Python1. 从官方网站下载最新版本的Python2. 运行安装程序并按照安装向导进行操作3. 勾选添加到路径复选框4. 完成安装过程5.…

记录 | xftp远程连接两台windows

1、打开openssh 设置 -> 应用 -> 可选功能 -> 添加功能 -> OpenSSH 客户端,将 ssh 客户端安装将两台电脑的 ssh 开启,cmd 中输入 net start sshd2、配置 win10 账号密码 3、进行 xftp 连接

移液器吸头材质选择——PFA吸头在半导体化工行业的应用

PFA吸头是一种高性能移液器配件,这种材料具有优异的耐化学品、耐热和电绝缘性能,使得PFA吸头在应用中表现出色。那么它有哪些特点呢? 首先,PFA吸头具有卓越的耐化学腐蚀性能。无论是酸性溶液、碱性溶液还是有机溶剂,P…

做数据分析为何要学统计学(5)——什么问题适合使用卡方检验?

卡方检验作为一种非常著名的非参数检验方法(不受总体分布因素的限制),在工程试验、临床试验、社会调查等领域被广泛应用。但是也正是因为使用的便捷性,造成时常被误用。本文参阅相关的文献,对卡方检验的适用性进行粗浅…

(代码详解)饼图绘制+参数讲解+饼图内外标签字体大小设置+添加图例,并调整图例大小与位置+调整标题与图之间的距离

大家好,本篇的目的是使用python画出如下的饼图,并且介绍其中参数的作用 目录 完整代码 一、导入所需的库 二、中文显示 三、调整图例的大小(长、宽) 四、导入数据 五、绘制饼图参数介绍 (重点) 六、调整饼图外标签和内标签…

SpringBoot3-集成mybatis

1、pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.…

Java EE 多线程之多线程案例

文章目录 1. 多线程案例1.1 单例模式1.1.1 饿汉模式1.1.2 懒汉模式1.1.3 多线程下的单例模式 1.2 阻塞队列1.2.1 阻塞队列定义1.2.2 生产者消费者模型的意义1.2.4 标准库中的阻塞队列1.2.5 实现阻塞队列1.2.6 用阻塞队列实现生产者消费者模型 1.3 实现定时器1.3.1 标准库中的定…

酷开科技多维度赋能营销,实力斩获三项大奖

在数智化新阶段、广告新生态、传播新业态的背景下&#xff0c;“第30届中国国际广告节广告主盛典暨网易传媒态度营销峰会”于11月18日在厦门国际会展中心盛大举行。来自全国的品牌方、战略决策者、媒体平台和品牌服务机构等汇聚一堂。在50000&#xff0b;现场观众和数千万线上观…

Altman作了多少恶?排挤首席科学家出GPT5开发、离间董事会、PUA员工

在山姆奥特曼&#xff08;Sam Altman&#xff09;被OpenAI董事会突然解职后的几天里&#xff0c;这个消息在科技圈引发轰动&#xff0c;该公司内部员工和许多科技界人士甚至将此举比作一场政变。 奥特曼被解雇后立即传出的说法是&#xff0c;OpenAI的广大员工都很喜欢他&#x…

打包CSS

接上一个打包HTML继续进行CSS的打包 1.在之前的文件夹里的src文件夹创建一个css文件 2.在浏览器打开webpack——>中文文档——>指南——>管理资源——>加载CSS 3.复制第一句代码到终端 4.复制下图代码到webpack.config.js脚本的plugins&#xff1a;[.....]内容下…

android项目实战之编辑器集成

引言 项目需要用到编辑器&#xff0c;采用RichEditor&#xff0c;如下效果 实现 1. 引入库2 implementation jp.wasabeef:richeditor-android:2.0.0 2. XML <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width&q…

持续集成和持续交付

引言 CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。作为一种面向开发和运维团队的解决方案&#xff0c;CI/CD 主要针对在集成新代码时所引发的问题&#xff08;亦称&#xff1a;“集成地狱”&#…

ubuntu install sqlmap

refer: https://github.com/sqlmapproject/sqlmap 安装sqlmap&#xff0c;可以直接使用git 克隆整个sqlmap项目&#xff1a; git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev 2.然后进入sqlmap-dev&#xff0c;使用命令&#xff1a; python s…

ARP协议:地址解析协议

目录 引言 什么是ARP协议&#xff1f; ARP协议的工作原理 1. ARP请求 2. ARP应答 3. ARP缓存 ARP协议的应用 结语 其他链接 引言 在计算机网络中&#xff0c;地址解析协议&#xff08;ARP&#xff0c;Address Resolution Protocol&#xff09;扮演着重要的角色。ARP协议…

功率放大器有哪些功能和作用

功率放大器是一种电子设备&#xff0c;主要用于将输入的低功率信号放大为更大的功率信号。功率放大器的主要功能和作用包括&#xff1a; 信号放大&#xff1a;功率放大器可以将输入的低功率信号放大为更大的功率信号。这对于一些需要输出更大功率的应用来说非常重要&#xff0c…

企业电子招投标采购系统源码之鸿鹄电子招投标系统+电子招投标的组成

鸿鹄招投标系统的功能描述 1、门户管理&#xff1a;用户可通过门户页面查看所有公告信息及相关通知信息&#xff0c;包括招标公告、非招标公告、系统通知和政策法规等板块。 2、立项管理&#xff1a;企业用户可以对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0…

TypeScript 常用高级类型

目录 前言&#xff1a; TypeScript 常用高级类型 基本概念 高级类型 1. 交叉类型&#xff08;Intersection Types&#xff09; 2. 联合类型&#xff08;Union Types&#xff09; 3. 映射类型&#xff08;Mapped Types&#xff09; 4. 条件类型&#xff08;Conditional…

共筑首都绿色美好出行!苏州金龙再向北京交付33辆纯电动旅游大巴

应时而动&#xff0c;创新求变&#xff0c;方能始终屹立于市场。2023年&#xff0c;随着旅游市场的复苏&#xff0c;北京市旅游消费潜力进一步释放&#xff0c;客运需求量快速增长&#xff0c;与此同时绿色、高端、个性化旅游客车也成为市场趋势。 11月底&#xff0c;33辆苏州…

C++STL的list(超详解)

文章目录 前言构造函数capacitylist的访问insertswapsort 前言 看一下list, 在任意位置可以进行O(1)插入删除的操作。 它怎么实现这个东西&#xff1f;它其实就是一个带头双向循环链表。 #成员函数 构造函数 这里面的构造函数学完string和vector之后已经相当熟悉了。 capaci…

万户协同办公平台ezoffice wpsservlet接口任意文件上传漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 一、漏洞描述 万户ezOFFICE协同管理平台是一个综合信息基础应用平台&am…