Buckets:

mishig's picture
|
download
raw
19.3 kB

Acelerar a inferência

Os modelos de difusão são lentos na inferência porque a geração é um processo iterativo no qual o ruído é gradualmente refinado em uma imagem ou vídeo ao longo de um determinado número de "etapas". Para acelerar esse processo, você pode tentar experimentar diferentes schedulers, reduzir a precisão dos pesos do modelo para cálculos mais rápidos, usar mecanismos de atenção mais eficientes em termos de memória, entre outras coisas.

Combine e utilize essas técnicas em conjunto para tornar a inferência mais rápida do que ao usar qualquer técnica isoladamente.

Este guia explicará como acelerar a inferência.

Tipo de dados do modelo

A precisão e o tipo de dados dos pesos do modelo afetam a velocidade de inferência, pois uma precisão maior exige mais memória para carregar e mais tempo para executar os cálculos. O PyTorch carrega os pesos do modelo em float32 ou precisão completa por padrão, portanto, alterar o tipo de dados é uma maneira simples de obter inferência mais rápida rapidamente.

bfloat16 é semelhante a float16, mas é mais robusto a erros numéricos. O suporte de hardware para bfloat16 varia, mas a maioria das GPUs modernas é capaz de suportar bfloat16.

import torch
from diffusers import StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, num_inference_steps=30).images[0]

float16 é semelhante a bfloat16, mas pode ser mais suscetível a erros numéricos.

import torch
from diffusers import StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, num_inference_steps=30).images[0]

O modo TensorFloat-32 (tf32) é suportado em GPUs NVIDIA Ampere e executa as operações de convolução e multiplicação de matrizes em tf32. O armazenamento e as demais operações permanecem em float32. Isso permite computações significativamente mais rápidas quando combinado com bfloat16 ou float16.

O PyTorch habilita o modo tf32 apenas para convoluções por padrão e você precisará habilitá-lo explicitamente para multiplicações de matrizes.

import torch
from diffusers import StableDiffusionXLPipeline

torch.backends.cuda.matmul.allow_tf32 = True

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, num_inference_steps=30).images[0]

Consulte a documentação de treinamento com precisão mista para mais detalhes.

Atenção de produto escalar

A atenção eficiente em memória otimiza a velocidade de inferência e o uso de memória!

Scaled dot product attention (SDPA) implementa vários backends de atenção, FlashAttention, xFormers e uma implementação nativa em C++. Ele seleciona automaticamente o backend mais otimizado para o seu hardware.

O SDPA é ativado por padrão se você estiver usando PyTorch >= 2.0 e nenhuma alteração adicional é necessária no seu código. No entanto, você pode experimentar outros backends de atenção, caso queira escolher o seu próprio. O exemplo abaixo usa o gerenciador de contexto torch.nn.attention.sdpa_kernel para habilitar a atenção eficiente.

from torch.nn.attention import SDPBackend, sdpa_kernel
import torch
from diffusers import StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"

with sdpa_kernel(SDPBackend.EFFICIENT_ATTENTION):
  image = pipeline(prompt, num_inference_steps=30).images[0]

torch.compile

torch.compile acelera a inferência compilando o código e as operações do PyTorch em kernels otimizados. O Diffusers normalmente compila os modelos mais intensivos em computação, como o UNet, o transformer ou o VAE.

Ative as seguintes configurações do compilador para máxima velocidade (consulte a lista completa para mais opções).

import torch
from diffusers import StableDiffusionXLPipeline

torch._inductor.config.conv_1x1_as_mm = True
torch._inductor.config.coordinate_descent_tuning = True
torch._inductor.config.epilogue_fusion = False
torch._inductor.config.coordinate_descent_check_all_directions = True

Carregue e compile o UNet e o VAE. Existem vários modos diferentes que você pode escolher, mas "max-autotune" otimiza para a velocidade mais rápida compilando para um grafo CUDA. Os grafos CUDA reduzem efetivamente o overhead ao lançar múltiplas operações de GPU por meio de uma única operação de CPU.

Com o PyTorch 2.3.1, você pode controlar o comportamento de cache do torch.compile. Isso é particularmente benéfico para modos de compilação como "max-autotune", que executa uma busca em grade sobre várias flags de compilação para encontrar a configuração ideal. Saiba mais no tutorial Compile Time Caching in torch.compile.

Alterar o layout de memória para channels_last também otimiza a memória e a velocidade de inferência.

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.unet.to(memory_format=torch.channels_last)
pipeline.vae.to(memory_format=torch.channels_last)
pipeline.unet = torch.compile(
    pipeline.unet, mode="max-autotune", fullgraph=True
)
pipeline.vae.decode = torch.compile(
    pipeline.vae.decode,
    mode="max-autotune",
    fullgraph=True
)

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, num_inference_steps=30).images[0]

A compilação é lenta na primeira vez, mas, uma vez compilado, o processo é significativamente mais rápido. Tente usar o pipeline compilado apenas para o mesmo tipo de operações de inferência. Chamar o pipeline compilado com um tamanho de imagem diferente reativa a compilação, o que é lento e ineficiente.

Compilação de formas dinâmicas

Certifique-se de sempre usar a versão nightly do PyTorch para um melhor suporte.

torch.compile acompanha as formas e condições de entrada e, se estas forem diferentes, recompila o modelo. Por exemplo, se um modelo for compilado em uma imagem de resolução 1024x1024 e usado em uma imagem com resolução diferente, isso aciona a recompilação.

Para evitar recompilação, adicione dynamic=True para tentar gerar um kernel mais dinâmico e evitar recompilação quando as condições mudarem.

+ torch.fx.experimental._config.use_duck_shape = False
+ pipeline.unet = torch.compile(
    pipeline.unet, fullgraph=True, dynamic=True
)

Especificar use_duck_shape=False instrui o compilador se ele deve usar a mesma variável simbólica para representar tamanhos de entrada que são iguais. Para mais detalhes, confira este comentário.

Nem todos os modelos podem se beneficiar da compilação dinâmica imediatamente e podem exigir alterações. Consulte este PR que melhorou a implementação do AuraFlowPipeline para se beneficiar da compilação dinâmica.

Sinta-se à vontade para abrir uma issue se a compilação dinâmica não funcionar como esperado para um modelo do Diffusers.

Compilação regional

Compilação regional reduz a latência de inicialização ao compilar apenas o bloco pequeno e repetido com frequência de um modelo — tipicamente uma camada de transformer — e permite reutilizar os artefatos compilados para cada ocorrência subsequente. Para muitas arquiteturas de difusão, isso entrega as mesmas acelerações de tempo de execução que a compilação de grafo completo e reduz o tempo de compilação em 8 a 10 vezes.

Use o método compile_repeated_blocks(), um auxiliar que encapsula o torch.compile, em qualquer componente, como o modelo transformer, conforme mostrado abaixo.

# pip install -U diffusers
import torch
from diffusers import StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16,
).to("cuda")  # or "mps", "xpu", "cpu"

# compile only the repeated transformer layers inside the UNet
pipeline.unet.compile_repeated_blocks(fullgraph=True)

Para habilitar a compilação regional para um novo modelo, adicione um atributo _repeated_blocks a uma classe de modelo contendo os nomes das classes (como strings) dos blocos que você deseja compilar.

class MyUNet(ModelMixin):
    _repeated_blocks = ("Transformer2DModel",)  # ← compiled by default

Para mais exemplos de compilação regional, consulte a referência PR.

Há também um método compile_regions no Accelerate que seleciona automaticamente os blocos candidatos em um modelo para compilação. O restante do grafo é compilado separadamente. Isso é útil para experimentos rápidos, pois há menos opções para você definir quais blocos compilar ou ajustar as flags de compilação.

# pip install -U accelerate
import torch
from diffusers import StableDiffusionXLPipeline
from accelerate.utils import compile_regions

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.unet = compile_regions(pipeline.unet, mode="reduce-overhead", fullgraph=True)

compile_repeated_blocks() é intencionalmente explícito. Liste os blocos a repetir em _repeated_blocks e o helper compila apenas esses blocos. Ele oferece um comportamento previsível e facilita o raciocínio sobre a reutilização do cache em uma linha de código.

Quebras de gráfico

É importante especificar fullgraph=True no torch.compile para garantir que não haja quebras de grafo no modelo subjacente. Isso permite aproveitar o torch.compile sem qualquer degradação de desempenho. Para o UNet e o VAE, isso altera a forma como você acessa as variáveis de retorno.

- latents = unet(
-   latents, timestep=timestep, encoder_hidden_states=prompt_embeds
-).sample

+ latents = unet(
+   latents, timestep=timestep, encoder_hidden_states=prompt_embeds, return_dict=False
+)[0]

Sincronização de GPU

A função step() é chamada no scheduler a cada vez que o denoiser faz uma previsão, e a variável sigmas é indexada. Quando colocada na GPU, ela introduz latência devido à sincronização de comunicação entre a CPU e a GPU. Isso se torna mais evidente quando o denoiser já foi compilado.

Em geral, os sigmas devem permanecer na CPU para evitar a sincronização de comunicação e a latência.

Consulte o post no blog torch.compile and Diffusers: A Hands-On Guide to Peak Performance para maximizar o desempenho com torch.compile em modelos de difusão.

Benchmarks

Consulte o conjunto de dados diffusers/benchmarks para ver dados de latência de inferência e uso de memória para pipelines compilados.

O repositório diffusers-torchao também contém resultados de benchmarking para as versões compiladas do Flux e do CogVideoX.

Kernels

Kernels é uma biblioteca para construir, distribuir e carregar kernels de computação otimizados no Hub. Ela suporta kernels de atenção e kernels CUDA personalizados para operações como RMSNorm, GEGLU, RoPE e AdaLN.

O guia de Integração com o Pipeline do Diffusers mostra como integrar um kernel com a habilidade add cuda-kernels. Essa habilidade permite que um agente, como Claude ou Codex, escreva kernels personalizados voltados para um modelo específico e para o seu hardware.

Instale a habilidade add cuda-kernels para ensinar um agente a escrever um kernel. O post no blog Custom kernels for all from Codex and Claude aborda isso em mais detalhes.

Por exemplo, um kernel RMSNorm personalizado (gerado pela skill add cuda-kernels) com torch.compile acelera a geração do LTX-Video em 1,43x em um H100.

Xet Storage Details

Size:
19.3 kB
·
Xet hash:
8f529594dc26dc66cfbbd134442b2c5b6eaf14c25cd89afc811aab5ed9149735

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.