加载管道
[[open-in-colab]]
扩散系统由多个组件组成,如参数化模型和调度器,这些组件以复杂的方式相互作用。因此,我们设计了 [DiffusionPipeline
] 以将整个扩散系统的复杂性封装成一个易于使用的 API。同时,[DiffusionPipeline
] 完全可定制,你可以修改每个组件以构建适用于你用例的扩散系统。
本指南将向你展示如何加载:
- 从 Hub 和本地加载管道
- 将不同组件加载到管道中
- 在不增加内存使用的情况下加载多个管道
- 加载不同浮点类型或非指数移动平均(EMA)权重的检查点变体
加载管道
TIP
如果你对 [DiffusionPipeline
] 类的工作原理感兴趣,可以跳到 DiffusionPipeline 详解 部分。
有两种方法可以为任务加载管道:
- 加载通用的 [
DiffusionPipeline
] 类,并允许它从检查点自动检测正确的管道类。 - 加载特定任务的特定管道类。
使用下面的空间来评估管道的内存需求,以确保在下载和加载后它能在你的硬件上运行。
本地管道
要加载本地管道,请使用 git-lfs 手动将检查点下载到你的本地磁盘。
git-lfs install
git clone https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5
这将在你的磁盘上创建一个本地文件夹 ./stable-diffusion-v1-5
,你应该将该文件夹的路径传递给 [~DiffusionPipeline.from_pretrained
]。
from diffusers import DiffusionPipeline
stable_diffusion = DiffusionPipeline.from_pretrained("./stable-diffusion-v1-5", use_safetensors=True)
[~DiffusionPipeline.from_pretrained
] 方法在检测到本地路径时不会从 Hub 下载文件,但这同时也意味着它不会下载和缓存检查点的最新更改。
自定义管道
你可以通过加载不同的组件来自定义管道。这很重要,因为你可以:
- 根据需要选择生成速度更快或生成质量更高的调度器(调用管道上的
scheduler.compatibles
方法以查看兼容的调度器) - 将默认的管道组件替换为更新且性能更好的组件
例如,让我们自定义默认的 stabilityai/stable-diffusion-xl-base-1.0 检查点,使用:
- [
HeunDiscreteScheduler
] 以牺牲生成速度为代价生成更高质量的图像。你必须在 [~HeunDiscreteScheduler.from_pretrained
] 中传递subfolder="scheduler"
参数,以将调度器配置加载到管道仓库的正确 子文件夹 中。 - 一个更稳定的以 fp16 运行的 VAE。
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
)添加到从现有的 [StableDiffusionPipeline
] 实例化的 [AnimateDiffPipeline
] 中。同样,这两个管道共享相同的预训练模型,因此再次加载一个全新的管道会浪费内存。
通过使用 [DiffusionPipeline.from_pipe
] API,你可以在多个管道之间切换,利用它们的不同功能而不增加内存使用。这类似于在你的管道中开启和关闭某个功能。
TIP
要在任务之间切换(而不是功能),请使用带有 AutoPipeline 类的 [~DiffusionPipeline.from_pipe
] 方法,该方法会根据任务自动识别管道类(更多内容请参阅 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"
现在,使用 [~DiffusionPipeline.from_pipe
] 方法从 [StableDiffusionPipeline
] 重用相同的管道组件到 [StableDiffusionSAGPipeline
]。
WARNING
使用 [~DiffusionPipeline.from_pipe
] 创建的新管道可能无法正确运行某些管道方法。例如,[~DiffusionPipeline.enable_model_cpu_offload
] 方法基于每个管道的唯一卸载序列在模型组件上安装钩子。如果新管道中的模型执行顺序不同,CPU 卸载可能无法正常工作。
为了确保一切按预期工作,我们建议在使用 [~DiffusionPipeline.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 组件
使用 [~DiffusionPipeline.from_pipe
] 加载的管道可以通过不同的模型组件或方法进行自定义。然而,每当修改模型组件的 状态 时,这会影响所有共享相同组件的其他管道。例如,如果你在 [StableDiffusionSAGPipeline
] 上调用 [~diffusers.loaders.IPAdapterMixin.unload_ip_adapter
],你将无法在 [StableDiffusionPipeline
] 中使用 IP-Adapter,因为 IP-Adapter 已从它们共享的组件中移除。
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'"
从管道加载的内存使用
使用 [~DiffusionPipeline.from_pipe
] 加载多个管道的内存需求由内存使用最高的管道决定,而与你创建的管道数量无关。
Pipeline | Memory usage (GB) |
---|---|
StableDiffusionPipeline | 4.400 |
StableDiffusionSAGPipeline | 4.400 |
AnimateDiffPipeline | 15.178 |
[AnimateDiffPipeline
] 具有最高的内存需求,因此 总内存使用量 仅基于 [AnimateDiffPipeline
]。如果你创建额外的管道,只要它们的内存需求不超过 [AnimateDiffPipeline
],你的内存使用量不会增加。每个管道可以互换使用,而不会产生额外的内存开销。
安全检查器
Diffusers 为 Stable Diffusion 模型实现了一个 安全检查器,这些模型可能会生成有害内容。安全检查器会筛查生成的输出,以防止已知的不适宜工作内容(NSFW)。如果你出于任何原因希望禁用安全检查器,可以在调用 [~DiffusionPipeline.from_pretrained
] 方法时传递 safety_checker=None
。
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 .
"""
Checkpoint 变体
Checkpoint 变体通常是指权重:
- 存储在不同的浮点类型中,例如 torch.float16,因为它只需要一半的带宽和存储空间来下载。如果你要继续训练或使用 CPU,则不能使用此变体。
- 非指数移动平均(EMA)权重,这些权重不应用于推理。你应该使用此变体来继续微调模型。
TIP
当 checkpoint 具有相同的模型结构,但它们是在不同的数据集和不同的训练设置下训练时,应将它们存储在不同的仓库中。例如,stabilityai/stable-diffusion-2 和 stabilityai/stable-diffusion-2-1 存储在不同的仓库中。
否则,变体与原始 checkpoint 完全相同。它们具有完全相同的序列化格式(如 safetensors),模型结构相同,权重的张量形状也完全相同。
checkpoint type | weight name | argument for loading weights |
---|---|---|
original | diffusion_pytorch_model.safetensors | |
floating point | diffusion_pytorch_model.fp16.safetensors | variant , torch_dtype |
non-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
指定了应从仓库中加载哪些文件。例如,如果你想从 stable-diffusion-v1-5/stable-diffusion-v1-5 加载 UNet 的非 EMA 变体,设置variant="non_ema"
以下载non_ema
文件。
使用 variant
参数在 [DiffusionPipeline.save_pretrained
] 方法中将检查点保存为不同的浮点类型或非 EMA 变体。你应该尝试将变体保存到与原始检查点相同的文件夹中,这样你就可以从同一个文件夹中加载两者。
如果你不将变体保存到现有文件夹中,必须指定 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
] 对应于文件夹结构 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
] 的实例,它由七个组件组成:
"feature_extractor"
: 一个来自 🤗 Transformers 的 [~transformers.CLIPImageProcessor
]。"safety_checker"
: 一个用于筛查有害内容的 组件。"scheduler"
: 一个 [PNDMScheduler
] 的实例。"text_encoder"
: 一个来自 🤗 Transformers 的 [~transformers.CLIPTextModel
]。"tokenizer"
: 一个来自 🤗 Transformers 的 [~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"
]
}
将管道实例的组件与 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.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 创建的,记录在
_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"
]
}