Diffusers 文档

处理大型模型

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

处理大型模型

Stable Diffusion XL (SDXL) 这样的现代扩散模型,不仅仅是一个单一模型,而是多个模型的集合。SDXL 具有四个不同的模型级别组件

  • 变分自编码器 (VAE)
  • 两个文本编码器
  • 用于去噪的 UNet

通常,文本编码器和去噪器比 VAE 大得多。

随着模型变得更大更好,您的模型可能变得非常大,以至于即使是单个副本也无法装入内存。但这并不意味着它无法加载。如果您有多个 GPU,则有更多内存可用于存储您的模型。在这种情况下,最好将您的模型 checkpoint 拆分为几个较小的checkpoint 分片

当文本编码器 checkpoint 有多个分片时,例如 SD3 的 T5-xxlTransformers 库会自动处理它,因为当使用 StableDiffusion3Pipeline 时,它是 Diffusers 的必需依赖项。更具体地说,Transformers 将自动处理所请求模型类中多个分片的加载,并使其准备就绪,以便可以执行推理。

去噪器 checkpoint 也可以有多个分片,并且由于 Accelerate 库的支持,它支持推理。

有关处理难以装入内存的大型模型的一般指导,请参阅 处理大型模型以进行推理 指南。

例如,让我们为 SDXL UNet 保存分片 checkpoint

from diffusers import UNet2DConditionModel

unet = UNet2DConditionModel.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", subfolder="unet"
)
unet.save_pretrained("sdxl-unet-sharded", max_shard_size="5GB")

SDXL UNet checkpoint 的 fp32 变体大小约为 10.4GB。将 max_shard_size 参数设置为 5GB 以创建 3 个分片。保存后,您可以在 StableDiffusionXLPipeline 中加载它们

from diffusers import UNet2DConditionModel, StableDiffusionXLPipeline
import torch

unet = UNet2DConditionModel.from_pretrained(
    "sayakpaul/sdxl-unet-sharded", torch_dtype=torch.float16
)
pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", unet=unet, torch_dtype=torch.float16
).to("cuda")

image = pipeline("a cute dog running on the grass", num_inference_steps=30).images[0]
image.save("dog.png")

如果一次将所有模型级别组件都放在 GPU 上不可行,请使用 enable_model_cpu_offload() 来帮助您

- pipeline.to("cuda")
+ pipeline.enable_model_cpu_offload()

一般来说,我们建议当 checkpoint 大于 5GB(fp32)时进行分片。

设备放置

在分布式设置中,您可以使用 Accelerate 在多个 GPU 上运行推理。

此功能是实验性的,其 API 在未来可能会发生变化。

使用 Accelerate,您可以使用 device_map 来确定如何在多个设备之间分配 pipeline 的模型。这在您有多个 GPU 的情况下非常有用。

例如,如果您有两个 8GB GPU,那么使用 enable_model_cpu_offload() 可能效果不佳,因为

  • 它仅适用于单个 GPU
  • 单个模型可能无法装入单个 GPU(enable_sequential_cpu_offload() 可能会起作用,但速度会非常慢,并且也仅限于单个 GPU)

为了利用两个 GPU,您可以使用“balanced”设备放置策略,该策略将模型分布在所有可用的 GPU 上。

目前仅支持“balanced”策略,我们计划在未来支持其他映射策略。

from diffusers import DiffusionPipeline
import torch

pipeline = DiffusionPipeline.from_pretrained(
-    "stable-diffusion-v1-5/stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True,
+    "stable-diffusion-v1-5/stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True, device_map="balanced"
)
image = pipeline("a dog").images[0]
image

您还可以传递一个字典来强制限制每个设备上可以使用的最大 GPU 内存

from diffusers import DiffusionPipeline
import torch

max_memory = {0:"1GB", 1:"1GB"}
pipeline = DiffusionPipeline.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-v1-5",
    torch_dtype=torch.float16,
    use_safetensors=True,
    device_map="balanced",
+   max_memory=max_memory
)
image = pipeline("a dog").images[0]
image

如果 max_memory 中不存在某个设备,则该设备将被完全忽略,并且不会参与设备放置。

默认情况下,Diffusers 使用所有设备的最大内存。如果模型无法装入 GPU,则会将它们卸载到 CPU。如果 CPU 没有足够的内存,您可能会看到错误。在这种情况下,您可以考虑使用 enable_sequential_cpu_offload()enable_model_cpu_offload()

调用 reset_device_map() 以重置 pipeline 的 device_map。如果您想在已设备映射的 pipeline 上使用 to()enable_sequential_cpu_offload()enable_model_cpu_offload() 等方法,这也是必要的。

pipeline.reset_device_map()

一旦 pipeline 被设备映射,您还可以通过 hf_device_map 访问其设备映射

print(pipeline.hf_device_map)

设备映射示例可能如下所示

{'unet': 1, 'vae': 1, 'safety_checker': 0, 'text_encoder': 0}
< > 在 GitHub 上更新