加载管道
扩散系统包含多个组件,例如参数化模型和调度器,它们以复杂的方式交互。这就是我们设计 DiffusionPipeline 的原因,它将整个扩散系统的复杂性封装到易于使用的 API 中。同时,DiffusionPipeline 完全可定制,因此您可以修改每个组件以构建适合您的用例的扩散系统。
本指南将向您展示如何加载
- 来自 Hub 和本地的管道
- 管道中的不同组件
- 多个管道,而不会增加内存使用量
- 检查点变体,例如不同的浮点类型或非指数平均(EMA)权重
加载管道
如果您对 DiffusionPipeline 解释 部分感兴趣,请跳过它。它解释了 DiffusionPipeline 类是如何工作的。
有两种方法可以加载用于特定任务的管道
- 加载通用 DiffusionPipeline 类,并让它从检查点自动检测正确的管道类。
- 为特定任务加载特定的管道类。
DiffusionPipeline 类是加载来自 Hub 的最新流行扩散模型的简单通用方法。它使用 from_pretrained() 方法从检查点自动检测用于特定任务的正确管道类,下载并缓存所有必需的配置和权重文件,并返回一个准备好用于推理的管道。
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", use_safetensors=True)
同一个检查点也可用于图像到图像的任务。只要您提供适当的输入,DiffusionPipeline 类可以处理任何任务。例如,对于图像到图像的任务,您需要将初始图像传递给管道。
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", use_safetensors=True)
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/img2img-init.png")
prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
image = pipeline("Astronaut in a jungle, cold color palette, muted colors, detailed, 8k", image=init_image).images[0]
使用下面的 Space 在您下载并加载管道之前评估管道的内存需求,看看它是否可以在您的硬件上运行。
本地管道
要本地加载管道,请使用 git-lfs 手动将检查点下载到您的本地磁盘。
git-lfs install
git clone https://huggingface.co/runwayml/stable-diffusion-v1-5
这将在您的磁盘上创建一个本地文件夹 ./stable-diffusion-v1-5,您应该将它的路径传递给 from_pretrained()。
from diffusers import DiffusionPipeline
stable_diffusion = DiffusionPipeline.from_pretrained("./stable-diffusion-v1-5", use_safetensors=True)
当 from_pretrained() 方法检测到本地路径时,它不会从 Hub 下载文件,但这意味着它也不会下载并缓存检查点的最新更改。
自定义管道
您可以通过将不同的组件加载到管道中来对其进行自定义。这很重要,因为您可以
- 根据您的需要更改到具有更快生成速度或更高生成质量的调度器(在您的管道上调用
scheduler.compatibles
方法以查看兼容的调度器) - 将默认管道组件更改为更新且性能更好的组件
例如,让我们使用以下方法自定义默认 stabilityai/stable-diffusion-xl-base-1.0 检查点
- HeunDiscreteScheduler 以牺牲生成速度为代价生成更高质量的图像。您必须在 from_pretrained() 中传递
subfolder="scheduler"
参数才能将调度器配置加载到管道存储库的正确 子文件夹 中。 - 一个更稳定的 VAE,可以在 fp16 中运行。
from diffusers import StableDiffusionXLPipeline, HeunDiscreteScheduler, AutoencoderKL
import torch
scheduler = HeunDiscreteScheduler.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", subfolder="scheduler")
vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.float16, use_safetensors=True)
现在,将新的调度器和 VAE 传递给 StableDiffusionXLPipeline。
pipeline = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
scheduler=scheduler,
vae=vae,
torch_dtype=torch.float16,
variant="fp16",
use_safetensors=True
).to("cuda")
重用管道
当您加载多个共享相同模型组件的管道时,重用共享组件而不是将所有内容再次加载到内存中是有意义的,尤其是在您的硬件内存受限的情况下。例如
- 您使用 StableDiffusionPipeline 生成了一张图片,但您想用 StableDiffusionSAGPipeline 提高它的质量。这两个管道都共享相同的预训练模型,因此两次加载相同的模型会浪费内存。
- 您想将模型组件(如
MotionAdapter
)添加到 AnimateDiffPipeline,该管道是从现有的 StableDiffusionPipeline 实例化的。同样,这两个管道都共享相同的预训练模型,因此再次加载一个全新的管道会浪费内存。
使用 DiffusionPipeline.from_pipe() API,您可以在多个管道之间切换,以利用它们的差异,而不会增加内存使用量。这类似于在管道中打开和关闭功能。
要切换任务(而不是功能),请将 from_pipe() 方法与 AutoPipeline 类一起使用,该类会根据任务自动识别管道类(在 AutoPipeline 教程中了解更多信息)。
让我们从 StableDiffusionPipeline 开始,然后重用加载的模型组件以创建 StableDiffusionSAGPipeline 来提高生成质量。您将使用带 IP-Adapter 的 StableDiffusionPipeline 来生成一只吃披萨的熊。
from diffusers import DiffusionPipeline, StableDiffusionSAGPipeline
import torch
import gc
from diffusers.utils import load_image
from accelerate.utils import compute_module_sizes
image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/load_neg_embed.png")
pipe_sd = DiffusionPipeline.from_pretrained("SG161222/Realistic_Vision_V6.0_B1_noVAE", torch_dtype=torch.float16)
pipe_sd.load_ip_adapter("h94/IP-Adapter", subfolder="models", weight_name="ip-adapter_sd15.bin")
pipe_sd.set_ip_adapter_scale(0.6)
pipe_sd.to("cuda")
generator = torch.Generator(device="cpu").manual_seed(33)
out_sd = pipe_sd(
prompt="bear eats pizza",
negative_prompt="wrong white balance, dark, sketches,worst quality,low quality",
ip_adapter_image=image,
num_inference_steps=50,
generator=generator,
).images[0]
out_sd
作为参考,您可以查看此过程消耗了多少内存。
def bytes_to_giga_bytes(bytes):
return bytes / 1024 / 1024 / 1024
print(f"Max memory allocated: {bytes_to_giga_bytes(torch.cuda.max_memory_allocated())} GB")
"Max memory allocated: 4.406213283538818 GB"
现在,使用 from_pipe() 方法,在 StableDiffusionSAGPipeline 中重用来自 StableDiffusionPipeline 的相同管道组件。
使用 from_pipe() 创建的新管道上,一些管道方法可能无法正常工作。例如,enable_model_cpu_offload() 方法根据每个管道的唯一卸载顺序在模型组件上安装钩子。如果模型在新管道中以不同的顺序执行,CPU 卸载可能无法正常工作。
为了确保一切按预期工作,我们建议在使用 from_pipe() 创建的新管道上重新应用管道方法。
pipe_sag = StableDiffusionSAGPipeline.from_pipe(
pipe_sd
)
generator = torch.Generator(device="cpu").manual_seed(33)
out_sag = pipe_sag(
prompt="bear eats pizza",
negative_prompt="wrong white balance, dark, sketches,worst quality,low quality",
ip_adapter_image=image,
num_inference_steps=50,
generator=generator,
guidance_scale=1.0,
sag_scale=0.75
).images[0]
out_sag
如果您检查内存使用情况,您会发现它与之前保持一致,因为 StableDiffusionPipeline 和 StableDiffusionSAGPipeline 共享相同的管道组件。这使您可以互换使用它们,而不会产生任何额外的内存开销。
print(f"Max memory allocated: {bytes_to_giga_bytes(torch.cuda.max_memory_allocated())} GB")
"Max memory allocated: 4.406213283538818 GB"
让我们使用 AnimateDiffPipeline 为图像添加动画,并向管道添加 MotionAdapter
模块。对于 AnimateDiffPipeline,您需要先卸载 IP-Adapter,然后在创建新管道之后重新加载它(这仅适用于 AnimateDiffPipeline)。
from diffusers import AnimateDiffPipeline, MotionAdapter, DDIMScheduler
from diffusers.utils import export_to_gif
pipe_sag.unload_ip_adapter()
adapter = MotionAdapter.from_pretrained("guoyww/animatediff-motion-adapter-v1-5-2", torch_dtype=torch.float16)
pipe_animate = AnimateDiffPipeline.from_pipe(pipe_sd, motion_adapter=adapter)
pipe_animate.scheduler = DDIMScheduler.from_config(pipe_animate.scheduler.config, beta_schedule="linear")
# load IP-Adapter and LoRA weights again
pipe_animate.load_ip_adapter("h94/IP-Adapter", subfolder="models", weight_name="ip-adapter_sd15.bin")
pipe_animate.load_lora_weights("guoyww/animatediff-motion-lora-zoom-out", adapter_name="zoom-out")
pipe_animate.to("cuda")
generator = torch.Generator(device="cpu").manual_seed(33)
pipe_animate.set_adapters("zoom-out", adapter_weights=0.75)
out = pipe_animate(
prompt="bear eats pizza",
num_frames=16,
num_inference_steps=50,
ip_adapter_image=image,
generator=generator,
).frames[0]
export_to_gif(out, "out_animate.gif")
AnimateDiffPipeline 更占内存,消耗 15GB 的内存(请参阅 from_pipe 的内存使用情况 部分了解这对您的内存使用情况意味着什么)。
print(f"Max memory allocated: {bytes_to_giga_bytes(torch.cuda.max_memory_allocated())} GB")
"Max memory allocated: 15.178664207458496 GB"
修改 from_pipe 组件
使用 from_pipe() 加载的管道可以使用不同的模型组件或方法进行自定义。但是,无论何时修改模型组件的状态,都会影响所有共享相同组件的其他管道。例如,如果您在 StableDiffusionSAGPipeline 上调用 unload_ip_adapter(),您将无法使用 IP-Adapter 与 StableDiffusionPipeline 一起使用,因为它已从它们的共享组件中删除。
pipe.sag_unload_ip_adapter()
generator = torch.Generator(device="cpu").manual_seed(33)
out_sd = pipe_sd(
prompt="bear eats pizza",
negative_prompt="wrong white balance, dark, sketches,worst quality,low quality",
ip_adapter_image=image,
num_inference_steps=50,
generator=generator,
).images[0]
"AttributeError: 'NoneType' object has no attribute 'image_projection_layers'"
from_pipe 的内存使用情况
使用 from_pipe() 加载多个管道的内存需求取决于内存使用量最大的管道,无论您创建了多少个管道。
管道 | 内存使用情况 (GB) |
---|---|
StableDiffusionPipeline | 4.400 |
StableDiffusionSAGPipeline | 4.400 |
AnimateDiffPipeline | 15.178 |
AnimateDiffPipeline 的内存需求最高,因此总内存使用量仅基于 AnimateDiffPipeline。只要其内存需求不超过 AnimateDiffPipeline,即使您创建其他管道,您的内存使用量也不会增加。每个管道可以互换使用,而不会产生任何额外的内存开销。
安全检查器
Diffusers 为 Stable Diffusion 模型实施了 安全检查器,该模型可以生成有害内容。安全检查器会根据已知的硬编码非安全工作 (NSFW) 内容筛选生成的输出。如果出于任何原因您想禁用安全检查器,请将 safety_checker=None
传递给 from_pretrained() 方法。
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", safety_checker=None, use_safetensors=True)
"""
You have disabled the safety checker for <class 'diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline'> by passing `safety_checker=None`. Ensure that you abide by the conditions of the Stable Diffusion license and do not expose unfiltered results in services or applications open to the public. Both the diffusers team and Hugging Face strongly recommend keeping the safety filter enabled in all public-facing circumstances, disabling it only for use cases that involve analyzing network behavior or auditing its results. For more information, please have a look at https://github.com/huggingface/diffusers/pull/254 .
"""
检查点变体
检查点变体通常是指权重为
- 存储在不同的浮点类型中,例如 torch.float16,因为它只需要一半的带宽和存储空间来下载。如果您要继续训练或使用 CPU,则无法使用此变体。
- 非指数移动平均 (EMA) 权重,不应用于推理。您应该使用此变体继续微调模型。
当检查点具有相同的模型结构,但它们是在不同的数据集上训练的,并使用不同的训练设置时,它们应该存储在单独的存储库中。例如,stabilityai/stable-diffusion-2 和 stabilityai/stable-diffusion-2-1 存储在单独的存储库中。
否则,变体与原始检查点完全相同。它们具有完全相同的序列化格式(如 safetensors)、模型结构,并且它们的权重具有相同的张量形状。
检查点类型 | 权重名称 | 加载权重的参数 |
---|---|---|
原始 | diffusion_pytorch_model.safetensors | |
浮点数 | diffusion_pytorch_model.fp16.safetensors | variant 、torch_dtype |
非 EMA | diffusion_pytorch_model.non_ema.safetensors | variant |
加载变体有两个重要的参数
torch_dtype
指定加载的检查点的浮点精度。例如,如果您想通过加载 fp16 变体来节省带宽,则应将variant="fp16"
和torch_dtype=torch.float16
设置为将权重转换为 fp16。否则,fp16 权重将转换为默认的 fp32 精度。如果您只设置
torch_dtype=torch.float16
,则会先下载默认的 fp32 权重,然后将其转换为 fp16。variant
指定应从存储库中加载哪些文件。例如,如果您想从 runwayml/stable-diffusion-v1-5 加载 UNet 的非 EMA 变体,请将variant="non_ema"
设置为下载non_ema
文件。
from diffusers import DiffusionPipeline
import torch
pipeline = DiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5", variant="fp16", torch_dtype=torch.float16, use_safetensors=True
)
在 DiffusionPipeline.save_pretrained() 方法中使用 variant
参数将检查点保存为不同的浮点类型或非 EMA 变体。您应该尝试将变体保存到与原始检查点相同的文件夹中,这样您就可以选择从同一个文件夹加载两者。
from diffusers import DiffusionPipeline
pipeline.save_pretrained("runwayml/stable-diffusion-v1-5", variant="fp16")
如果您没有将变体保存到现有文件夹中,则必须指定 variant
参数,否则它将抛出 Exception
,因为它找不到原始检查点。
# 👎 this won't work
pipeline = DiffusionPipeline.from_pretrained(
"./stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True
)
# 👍 this works
pipeline = DiffusionPipeline.from_pretrained(
"./stable-diffusion-v1-5", variant="fp16", torch_dtype=torch.float16, use_safetensors=True
)
DiffusionPipeline 解释
作为类方法,DiffusionPipeline.from_pretrained() 负责两件事
- 下载推理所需的最新版本文件夹结构并将其缓存。如果本地缓存中存在最新文件夹结构,DiffusionPipeline.from_pretrained() 会重用缓存,不会重新下载文件。
- 将缓存的权重加载到正确的管道 类 中 - 从
model_index.json
文件中检索 - 并返回一个实例。
管道的底层文件夹结构直接对应于它们的类实例。例如,StableDiffusionPipeline 对应于 runwayml/stable-diffusion-v1-5
中的文件夹结构。
from diffusers import DiffusionPipeline
repo_id = "runwayml/stable-diffusion-v1-5"
pipeline = DiffusionPipeline.from_pretrained(repo_id, use_safetensors=True)
print(pipeline)
您会看到管道是 StableDiffusionPipeline 的一个实例,它包含七个组件
"feature_extractor"
: 来自 🤗 Transformers 的 CLIPImageProcessor。"safety_checker"
: 用于筛选有害内容的 组件。"scheduler"
: 一个 PNDMScheduler 的实例。"text_encoder"
: 来自 🤗 Transformers 的 CLIPTextModel。"tokenizer"
: 来自 🤗 Transformers 的 CLIPTokenizer。"unet"
: 一个 UNet2DConditionModel 的实例。"vae"
: 一个 AutoencoderKL 的实例。
StableDiffusionPipeline {
"feature_extractor": [
"transformers",
"CLIPImageProcessor"
],
"safety_checker": [
"stable_diffusion",
"StableDiffusionSafetyChecker"
],
"scheduler": [
"diffusers",
"PNDMScheduler"
],
"text_encoder": [
"transformers",
"CLIPTextModel"
],
"tokenizer": [
"transformers",
"CLIPTokenizer"
],
"unet": [
"diffusers",
"UNet2DConditionModel"
],
"vae": [
"diffusers",
"AutoencoderKL"
]
}
将管道实例的组件与 runwayml/stable-diffusion-v1-5
文件夹结构进行比较,您会发现存储库中为每个组件都设置了单独的文件夹。
.
├── feature_extractor
│ └── preprocessor_config.json
├── model_index.json
├── safety_checker
│ ├── config.json
| ├── model.fp16.safetensors
│ ├── model.safetensors
│ ├── pytorch_model.bin
| └── pytorch_model.fp16.bin
├── scheduler
│ └── scheduler_config.json
├── text_encoder
│ ├── config.json
| ├── model.fp16.safetensors
│ ├── model.safetensors
│ |── pytorch_model.bin
| └── pytorch_model.fp16.bin
├── tokenizer
│ ├── merges.txt
│ ├── special_tokens_map.json
│ ├── tokenizer_config.json
│ └── vocab.json
├── unet
│ ├── config.json
│ ├── diffusion_pytorch_model.bin
| |── diffusion_pytorch_model.fp16.bin
│ |── diffusion_pytorch_model.f16.safetensors
│ |── diffusion_pytorch_model.non_ema.bin
│ |── diffusion_pytorch_model.non_ema.safetensors
│ └── diffusion_pytorch_model.safetensors
|── vae
. ├── config.json
. ├── diffusion_pytorch_model.bin
├── diffusion_pytorch_model.fp16.bin
├── diffusion_pytorch_model.fp16.safetensors
└── diffusion_pytorch_model.safetensors
您可以将管道实例中的每个组件作为属性进行访问,以查看其配置。
pipeline.tokenizer
CLIPTokenizer(
name_or_path="/root/.cache/huggingface/hub/models--runwayml--stable-diffusion-v1-5/snapshots/39593d5650112b4cc580433f6b0435385882d819/tokenizer",
vocab_size=49408,
model_max_length=77,
is_fast=False,
padding_side="right",
truncation_side="right",
special_tokens={
"bos_token": AddedToken("<|startoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True),
"eos_token": AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True),
"unk_token": AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True),
"pad_token": "<|endoftext|>",
},
clean_up_tokenization_spaces=True
)
每个管道都期望一个 model_index.json
文件,该文件告诉 DiffusionPipeline
- 要从
_class_name
加载哪个管道类。 - 在
_diffusers_version
中,使用了哪个版本的 🧨 Diffusers 来创建模型。 - 哪些库中的哪些组件存储在子文件夹中(
name
对应于组件和子文件夹名称,library
对应于要从中加载类的库名称,class
对应于类名称)。
{
"_class_name": "StableDiffusionPipeline",
"_diffusers_version": "0.6.0",
"feature_extractor": [
"transformers",
"CLIPImageProcessor"
],
"safety_checker": [
"stable_diffusion",
"StableDiffusionSafetyChecker"
],
"scheduler": [
"diffusers",
"PNDMScheduler"
],
"text_encoder": [
"transformers",
"CLIPTextModel"
],
"tokenizer": [
"transformers",
"CLIPTokenizer"
],
"unet": [
"diffusers",
"UNet2DConditionModel"
],
"vae": [
"diffusers",
"AutoencoderKL"
]
}