Diffusers documentation

使用 device_map 将模型组件拆分到多个 GPU

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.43.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

使用 device_map 将模型组件拆分到多个 GPU

device_map 参数用于将模型的各个组件放置在不同的 GPU 上。当模型无法装入单个 GPU,且你的 GPU 之间没有高速互连时,请使用该参数。请参阅 分布式推理 以将其与其他策略进行比较,并参阅 设备放置 了解 device_map 的更多选项。

以下示例仅在需要该组件的阶段加载它,并使用 device_map 将较大的组件分布在两块 GPU 上。

编码提示

将 device_map="balanced" 设置为将文本编码器放置在单独的 GPU 上。使用 max_memory 来限制每个 GPU 使用的内存量。将 transformer 和 vae 设置为 None,以便仅加载文本编码器。

from diffusers import FluxPipeline
import torch

prompt = """
cinematic film still of a cat sipping a margarita in a pool in Palm Springs, California
highly detailed, high budget hollywood movie, cinemascope, moody, epic, gorgeous, film grain
"""

pipeline = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    transformer=None,
    vae=None,
    device_map="balanced",
    max_memory={0: "16GB", 1: "16GB"},
    dtype=torch.bfloat16
)
with torch.no_grad():
    print("Encoding prompts.")
    prompt_embeds, pooled_prompt_embeds, text_ids = pipeline.encode_prompt(
        prompt=prompt, prompt_2=None, max_sequence_length=512
    )

获取文本嵌入后,将文本编码器从 GPU 上移除,以为 transformer 腾出空间。

import gc

def flush():
    gc.collect()
    torch.cuda.empty_cache()
    torch.cuda.reset_max_memory_allocated()
    torch.cuda.reset_peak_memory_stats()

del pipeline.text_encoder
del pipeline.text_encoder_2
del pipeline.tokenizer
del pipeline.tokenizer_2
del pipeline

flush()

使用分片 Transformer 进行去噪

设置 device_map="auto" 以让 Accelerate 放置 transformer。它会先填满第一块 GPU,然后再移动到下一块,并且仅在 GPU 内存耗尽时才使用 CPU 或磁盘。在两块较小的 GPU 上,transformer 最终会被拆分到两块 GPU 上。位于 CPU 或磁盘上的权重会拖慢推理速度。

from diffusers import AutoModel
import torch

transformer = AutoModel.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    subfolder="transformer",
    device_map="auto",
    dtype=torch.bfloat16
)

检查 transformer.hf_device_map 以查看 transformer 在设备上的分布情况。使用 device_map 加载的 pipeline 也具有 hf_device_map 属性,用于显示每个组件的放置位置。

加载仅包含 transformer 的 pipeline,并设置 output_type="latent",使其返回潜变量而非图像。

pipeline = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    text_encoder=None,
    text_encoder_2=None,
    tokenizer=None,
    tokenizer_2=None,
    vae=None,
    transformer=transformer,
    dtype=torch.bfloat16
)

print("Running denoising.")
height, width = 768, 1360
latents = pipeline(
    prompt_embeds=prompt_embeds,
    pooled_prompt_embeds=pooled_prompt_embeds,
    num_inference_steps=50,
    guidance_scale=3.5,
    height=height,
    width=width,
    output_type="latent",
).images

解码潜在变量

从内存中移除 pipeline 和 transformer,并加载 VAE 以解码潜变量。VAE 足够小,可以放入单个 GPU 中。

import torch
from diffusers import AutoencoderKL
from diffusers.image_processor import VaeImageProcessor

del pipeline.transformer
del pipeline
del transformer

flush()

vae = AutoencoderKL.from_pretrained(
    "black-forest-labs/FLUX.1-dev", subfolder="vae", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"
vae_scale_factor = 2 ** (len(vae.config.block_out_channels) - 1)
image_processor = VaeImageProcessor(vae_scale_factor=vae_scale_factor)

with torch.no_grad():
    print("Running decoding.")
    latents = FluxPipeline._unpack_latents(latents, height, width, vae_scale_factor)
    latents = (latents / vae.config.scaling_factor) + vae.config.shift_factor

    image = vae.decode(latents, return_dict=False)[0]
    image = image_processor.postprocess(image, output_type="pil")
    image[0].save("split_transformer.png")

通过选择性地加载和卸载在特定阶段所需的模型,并将最大的模型分片到多个 GPU 上,您可以在消费级 GPU 上运行大模型的推理。

Update on GitHub