Diffusers documentation

Distribua os componentes do modelo entre GPUs com device_map

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

Distribua os componentes do modelo entre GPUs com device_map

O argumento device_map posiciona os componentes do modelo em GPUs separadas. Use-o quando um modelo não couber em uma única GPU e suas GPUs não possuírem um interconector rápido. Consulte Inferência distribuída para compará-lo com as outras estratégias e Posicionamento de dispositivo para mais opções de device_map.

O exemplo abaixo carrega cada componente apenas para o estágio que precisa dele e usa device_map para distribuir os maiores entre duas GPUs.

Codifique o prompt

Defina device_map="balanced" para posicionar os codificadores de texto em GPUs separadas. Use max_memory para limitar a quantidade de memória que cada GPU utiliza. Defina transformer e vae como None para que apenas os codificadores de texto sejam carregados.

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
    )

Depois de obter os embeddings de texto, remova os codificadores de texto da GPU para liberar espaço para o 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()

Desruído com o transformer fragmentado

Defina device_map="auto" para permitir que o Accelerate posicione o transformer. Ele preenche a primeira GPU antes de passar para a próxima e usa a CPU ou o disco apenas quando as GPUs ficam sem memória. Em duas GPUs menores, o transformer acaba dividido entre ambas. Pesos na CPU ou no disco deixam a inferência mais lenta.

from diffusers import AutoModel
import torch

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

Verifique transformer.hf_device_map para ver como o transformer é distribuído entre os dispositivos. Um pipeline carregado com device_map também possui um atributo hf_device_map que mostra onde cada componente é colocado.

Carregue um pipeline com apenas o transformer e defina output_type="latent" para que ele retorne latentes em vez de imagens.

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

Decodificar os latentes

Remova o pipeline e o transformer da memória e carregue um VAE para decodificar os latentes. O VAE é pequeno o suficiente para caber em uma única 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")

Ao carregar e descarregar seletivamente os modelos necessários em cada etapa e dividir os maiores modelos entre várias GPUs, é possível executar inferência com modelos grandes em GPUs de consumo.

Update on GitHub