Diffusers 文档

加载 pipelines

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

加载 pipelines

扩散系统由多个组件构成,例如参数化模型和调度器,它们以复杂的方式相互作用。这就是我们设计 DiffusionPipeline 的原因,它将整个扩散系统的复杂性封装到一个易于使用的 API 中。同时,DiffusionPipeline 是完全可定制的,因此您可以修改每个组件,为您的用例构建一个扩散系统。

本指南将向您展示如何加载

  • 来自 Hub 和本地的 pipelines
  • 将不同的组件加载到 pipeline 中
  • 多个 pipelines,而不会增加内存使用量
  • 检查点变体,例如不同的浮点类型或非指数移动平均 (EMA) 权重

加载 pipeline

如果您对 DiffusionPipeline 类的工作原理感兴趣,请跳到 DiffusionPipeline 详解 部分。

有两种方法可以加载用于任务的 pipeline

  1. 加载通用的 DiffusionPipeline 类,并允许它从检查点自动检测正确的 pipeline 类。
  2. 为特定任务加载特定的 pipeline 类。
通用 pipeline
特定 pipeline

DiffusionPipeline 类是一种简单通用的方式,可以从 Hub 加载最新的热门扩散模型。它使用 from_pretrained() 方法从检查点自动检测任务的正确 pipeline 类,下载并缓存所有必需的配置和权重文件,并返回一个准备好进行推理的 pipeline。

from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", use_safetensors=True)

相同的检查点也可以用于图像到图像的任务。DiffusionPipeline 类可以处理任何任务,只要您提供适当的输入即可。例如,对于图像到图像的任务,您需要将初始图像传递给 pipeline。

from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/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]

在下载和加载 pipeline 之前,使用下面的 Space 评估其内存需求,以查看它是否在您的硬件上运行。

本地 pipeline

要在本地加载 pipeline,请使用 git-lfs 手动将检查点下载到本地磁盘。

git-lfs install
git clone https://huggingface.co/stable-diffusion-v1-5/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 下载文件,但这也意味着它不会下载和缓存检查点的最新更改。

自定义 pipeline

您可以通过将不同的组件加载到 pipeline 中来自定义 pipeline。这很重要,因为您可以

  • 根据您的需求更改为生成速度更快或生成质量更高的 scheduler(在您的 pipeline 上调用 scheduler.compatibles 方法以查看兼容的 schedulers)
  • 将默认 pipeline 组件更改为更新、性能更好的组件

例如,让我们使用以下组件自定义默认的 stabilityai/stable-diffusion-xl-base-1.0 检查点:

  • HeunDiscreteScheduler,以牺牲较慢的生成速度为代价生成更高质量的图像。您必须在 from_pretrained() 中传递 subfolder="scheduler" 参数,以将 scheduler 配置加载到 pipeline 存储库的正确 子文件夹 中。
  • 更稳定的 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)

现在将新的 scheduler 和 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")

重用 pipeline

当您加载多个共享相同模型组件的 pipelines 时,重用共享组件而不是再次将所有内容重新加载到内存中是有意义的,特别是当您的硬件受到内存限制时。例如:

  1. 您使用 StableDiffusionPipeline 生成了一张图像,但您想使用 StableDiffusionSAGPipeline 提高其质量。这两个 pipelines 共享相同的预训练模型,因此两次加载相同的模型会浪费内存。
  2. 您想向 AnimateDiffPipeline 添加一个模型组件,例如 MotionAdapter,该 pipeline 是从现有的 StableDiffusionPipeline 实例化的。同样,这两个 pipelines 共享相同的预训练模型,因此再次加载全新的 pipeline 会浪费内存。

使用 DiffusionPipeline.from_pipe() API,您可以在多个 pipelines 之间切换,以利用它们的不同功能,而不会增加内存使用量。这类似于打开和关闭 pipeline 中的功能。

要切换任务(而不是功能),请将 from_pipe() 方法与 AutoPipeline 类一起使用,该类根据任务自动识别 pipeline 类(在 AutoPipeline 教程中了解更多信息)。

让我们从 StableDiffusionPipeline 开始,然后重用加载的模型组件来创建 StableDiffusionSAGPipeline 以提高生成质量。您将使用带有 IP-AdapterStableDiffusionPipeline 来生成一只吃披萨的熊。

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 的相同 pipeline 组件。

某些 pipeline 方法可能无法在新创建的 pipelines 上正常工作 from_pipe()。例如,enable_model_cpu_offload() 方法根据每个 pipeline 的唯一卸载顺序在模型组件上安装钩子。如果模型在新 pipeline 中以不同的顺序执行,则 CPU 卸载可能无法正常工作。

为了确保一切按预期工作,我们建议在新创建的 pipeline 上重新应用 pipeline 方法 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

如果您检查内存使用情况,您会发现它与之前保持不变,因为 StableDiffusionPipelineStableDiffusionSAGPipeline 共享相同的 pipeline 组件。这使您可以互换使用它们,而不会产生任何额外的内存开销。

print(f"Max memory allocated: {bytes_to_giga_bytes(torch.cuda.max_memory_allocated())} GB")
"Max memory allocated: 4.406213283538818 GB"

让我们使用 AnimateDiffPipeline 为图像添加动画,并向 pipeline 添加一个 MotionAdapter 模块。对于 AnimateDiffPipeline,您需要先卸载 IP-Adapter,然后在创建新 pipeline 重新加载它(这仅适用于 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() 加载的 pipelines 可以使用不同的模型组件或方法进行自定义。但是,每当您修改模型组件的状态时,它都会影响所有其他共享相同组件的 pipelines。例如,如果您在 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() 加载多个 pipelines 的内存需求由内存使用量最高的 pipeline 决定,而与您创建的 pipelines 数量无关。

Pipeline 内存使用量 (GB)
StableDiffusionPipeline 4.400
StableDiffusionSAGPipeline 4.400
AnimateDiffPipeline 15.178

AnimateDiffPipeline 具有最高的内存需求,因此总内存使用量仅基于 AnimateDiffPipeline。如果您创建额外的 pipelines,只要它们的内存需求不超过 AnimateDiffPipeline 的内存需求,您的内存使用量就不会增加。每个 pipeline 可以互换使用,而不会产生任何额外的内存开销。

安全检查器

Diffusers 为 Stable Diffusion 模型实现了一个 安全检查器,它可以生成有害内容。安全检查器会根据已知的硬编码不适合工作 (NSFW) 内容筛选生成的输出。如果出于任何原因您想禁用安全检查器,请将 safety_checker=None 传递给 from_pretrained() 方法。

from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/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-2stabilityai/stable-diffusion-2-1 存储在单独的存储库中。

否则,变体与原始检查点完全相同。它们具有完全相同的序列化格式(如 safetensors)、模型结构,并且它们的权重具有相同的张量形状。

检查点类型 权重名称 加载权重的参数
原始 diffusion_pytorch_model.safetensors
浮点 diffusion_pytorch_model.fp16.safetensors variant, torch_dtype
非 EMA diffusion_pytorch_model.non_ema.safetensors 变体

有两个重要的参数用于加载变体

  • torch_dtype 指定了加载的检查点的浮点精度。例如,如果您想通过加载 fp16 变体来节省带宽,您应该设置 variant="fp16"torch_dtype=torch.float16将权重转换为 fp16。否则,fp16 权重将转换为默认的 fp32 精度。

    如果您仅设置 torch_dtype=torch.float16,则会首先下载默认的 fp32 权重,然后再将其转换为 fp16。

  • variant 指定应从仓库加载哪些文件。例如,如果您想从 stable-diffusion-v1-5/stable-diffusion-v1-5 加载 UNet 的非 EMA 变体,请设置 variant="non_ema" 以下载 non_ema 文件。

fp16
非 EMA
from diffusers import DiffusionPipeline
import torch

pipeline = DiffusionPipeline.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-v1-5", variant="fp16", torch_dtype=torch.float16, use_safetensors=True
)

使用 DiffusionPipeline.save_pretrained() 方法中的 variant 参数,将检查点另存为不同的浮点类型或非 EMA 变体。您应该尝试将变体保存到与原始检查点相同的文件夹中,这样您就可以选择从同一文件夹加载两者。

fp16
non_ema
from diffusers import DiffusionPipeline

pipeline.save_pretrained("stable-diffusion-v1-5/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() 将重用缓存,而不会重新下载文件。
  • 将缓存的权重加载到正确的 pipeline 中(从 model_index.json 文件中检索),并返回它的一个实例。

pipeline 的底层文件夹结构与其类实例直接对应。例如,StableDiffusionPipeline 对应于 stable-diffusion-v1-5/stable-diffusion-v1-5 中的文件夹结构。

from diffusers import DiffusionPipeline

repo_id = "stable-diffusion-v1-5/stable-diffusion-v1-5"
pipeline = DiffusionPipeline.from_pretrained(repo_id, use_safetensors=True)
print(pipeline)

您会看到 pipeline 是 StableDiffusionPipeline 的一个实例,它由七个组件组成

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"
  ]
}

将 pipeline 实例的组件与 stable-diffusion-v1-5/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 的每个组件作为属性来查看其配置

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
)

每个 pipeline 都需要一个 model_index.json 文件,该文件告诉 DiffusionPipeline

  • _class_name 加载哪个 pipeline 类
  • 用于创建模型的 🧨 Diffusers 的哪个版本,在 _diffusers_version
  • 哪些库的哪些组件存储在子文件夹中(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"
  ]
}
< > 在 GitHub 上更新