Diffusers documentation

ParaAttention

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

and get access to the augmented documentation experience

to get started

ParaAttention

Large image and video generation models, such as FLUX.1-dev and HunyuanVideo, can be an inference challenge for real-time applications and deployment because of their size.

ParaAttention, bağlam paralelliği ve ilk blok önbelleği uygulayan ve çıkarımı hızlandırmak için diğer tekniklerle (torch.compile, fp8 dinamik kuantizasyonu) birleştirilebilen bir kütüphanedir.

Bu kılavuz, ParaAttention'ın NVIDIA L20 GPU'larında FLUX.1-dev ve HunyuanVideo'ya nasıl uygulanacağını gösterecektir. HunyuanVideo için bellek dışı (out-of-memory) hatalarını önlemek dışında, temel (baseline) kıyaslama ölçümümüz için herhangi bir optimizasyon uygulanmamıştır.

Temel performans ölçümümüz, FLUX.1-dev'in 28 adımda 1024x1024 çözünürlüğünde bir görüntüyü 26,36 saniyede, HunyuanVideo'nun ise 30 adımda 720p çözünürlüğünde 129 kareyi 3675,71 saniyede üretebildiğini göstermektedir.

Bağlam paralelliği ile daha hızlı çıkarım için, özellikle çok sayıda GPU varsa, NVLink desteğine sahip NVIDIA A100 veya H100 GPU'ları (müsaitse) kullanmayı deneyin.

İlk Blok Önbelleği

Modeldeki transformers bloklarının çıktılarını önbelleğe alıp sonraki çıkarım adımlarında yeniden kullanmak, hesaplama maliyetini azaltır ve çıkarımı hızlandırır.

Ancak, üretilen görüntü veya videoların kalitesini garanti altına almak için önbelleğin ne zaman yeniden kullanılacağına karar vermek zordur. ParaAttention, model çıktılar arasındaki farkı yaklaşık olarak hesaplamak için doğrudan ilk transformer bloğu çıktısının kalan farkını kullanır. Fark yeterince küçük olduğunda, önceki çıkarım adımlarının kalan farkı yeniden kullanılır. Başka bir deyişle, gürültü azaltma adımı atlanır.

Bu, FLUX.1-dev ve HunyuanVideo çıkarımında çok iyi kaliteyle 2x hızlanma sağlar.

Cache in Diffusion Transformer
AdaCache nasıl çalışır, First Block Cache bunun bir varyantıdır
FLUX-1.dev
HunyuanVideo

FLUX.1-dev üzerinde ilk blok önbelleğini uygulamak için aşağıda gösterildiği gibi apply_cache_on_pipe fonksiyonunu çağırın. 0.08, FLUX modelleri için varsayılan artıksal fark değeridir.

import time
import torch
from diffusers import FluxPipeline

pipe = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    dtype=torch.bfloat16,
).to("cuda")  # or "mps", "xpu", "cpu"

from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe

apply_cache_on_pipe(pipe, residual_diff_threshold=0.08)

# Enable memory savings
# pipe.enable_model_cpu_offload()
# pipe.enable_sequential_cpu_offload()

begin = time.time()
image = pipe(
    "A cat holding a sign that says hello world",
    num_inference_steps=28,
).images[0]
end = time.time()
print(f"Time: {end - begin:.2f}s")

print("Saving image to flux.png")
image.save("flux.png")
OptimizasyonlarOrijinalFBCache rdt=0.06FBCache rdt=0.08FBCache rdt=0.10FBCache rdt=0.12
ÖnizlemeOriginalFBCache rdt=0.06FBCache rdt=0.08FBCache rdt=0.10FBCache rdt=0.12
Duvar Saati (s)26.3621.8317.0116.0013.78

First Block Cache, neredeyse sıfır kalite kaybı ile birlikte, referans değerine kıyasla çıkarım hızını 17,01 saniyeye düşürerek 1,55 kat daha hızlı bir performans sağladı.

fp8 kuantizasyonu

Dinamik kuantizasyon ile fp8, çıkarımı daha da hızlandırır ve bellek kullanımını azaltır. 8-bit NVIDIA Tensor Cores'ün kullanılabilmesi için hem aktivasyonların hem de ağırlıkların kuantize edilmesi gerekir.

Metin kodlayıcı ve dönüştürücü modelini nicelleştirmek için float8_weight_only ve float8_dynamic_activation_float8_weight kullanın.

Varsayılan kuantizasyon yöntemi tensör başına kuantizasyondur, ancak GPU'nuz satır başına kuantizasyonu destekliyorsa daha iyi doğruluk için bunu da deneyebilirsiniz.

Aşağıdaki komutla torchao paketini yükleyin.

pip3 install -U torch torchao

torch.compile, mode="max-autotune-no-cudagraphs" veya mode="max-autotune" ile çağrıldığında performansı en iyi sağlayacak çekirdeği seçer. Model ilk kez çağrılıyorsa derleme işlemi uzun sürebilir ancak model derlendikten sonra bu süre kendini amorti eder.

Bu örnek yalnızca dönüştürücü modelini kuantize eder, ancak bellek kullanımını daha da azaltmak için metin kodlayıcısını da kuantize edebilirsiniz.

Dinamik kuantizasyon, model çıktısının dağılımını önemli ölçüde değiştirebilir; bu nedenle etkin olması için residual_diff_threshold değerini daha büyük bir değere değiştirmeniz gerekir.

FLUX-1.dev
HunyuanVideo
import time
import torch
from diffusers import FluxPipeline

pipe = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    dtype=torch.bfloat16,
).to("cuda")  # or "mps", "xpu", "cpu"

from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe

apply_cache_on_pipe(
    pipe,
    residual_diff_threshold=0.12,  # Use a larger value to make the cache take effect
)

from torchao.quantization import quantize_, float8_dynamic_activation_float8_weight, float8_weight_only

quantize_(pipe.text_encoder, float8_weight_only())
quantize_(pipe.transformer, float8_dynamic_activation_float8_weight())
pipe.transformer = torch.compile(
   pipe.transformer, mode="max-autotune-no-cudagraphs",
)

# Enable memory savings
# pipe.enable_model_cpu_offload()
# pipe.enable_sequential_cpu_offload()

for i in range(2):
    begin = time.time()
    image = pipe(
        "A cat holding a sign that says hello world",
        num_inference_steps=28,
    ).images[0]
    end = time.time()
    if i == 0:
        print(f"Warm up time: {end - begin:.2f}s")
    else:
        print(f"Time: {end - begin:.2f}s")

print("Saving image to flux.png")
image.save("flux.png")

fp8 dinamik kuantizasyonu ve torch.compile, referans değerine kıyasla çıkarım süresini 7,56 saniyeye düşürerek 3,48 kat daha hızlı hale getirdi.

Bağlam Paralelliği

Context Parallelism, çıkarımı paralelleştirir ve birden çok GPU ile ölçeklenir. ParaAttention bileşen tasarımı, Context Parallelism'i First Block Cache ve dinamik kuantizasyonla birleştirmenizi sağlar.

Birden fazla GPU ile çıkarımı ölçeklendirmenin ayrıntılı talimatları ve örnekleri için ParaAttention deposuna başvurun.

Eğer çıkarım sürecinin kalıcı ve hizmet verebilir olması gerekiyorsa, kendi çıkarım işleyicinizi yazmak için torch.multiprocessing kullanmanız önerilir. Bu, süreci başlatma ve modeli yükleme ile yeniden derleme işlemlerinin getirdiği ek yükü ortadan kaldırabilir.

FLUX-1.dev
HunyuanVideo

Aşağıdaki kod örneği, en hızlı çıkarım hızı için First Block Cache, fp8 dinamik kuantizasyonu, torch.compile ve Context Parallelism'i birleştirir.

import time
import torch
import torch.distributed as dist
from diffusers import FluxPipeline

dist.init_process_group()

torch.cuda.set_device(dist.get_rank())

pipe = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    dtype=torch.bfloat16,
).to("cuda")  # or "mps", "xpu", "cpu"

from para_attn.context_parallel import init_context_parallel_mesh
from para_attn.context_parallel.diffusers_adapters import parallelize_pipe
from para_attn.parallel_vae.diffusers_adapters import parallelize_vae

mesh = init_context_parallel_mesh(
    pipe.device.type,
    max_ring_dim_size=2,
)
parallelize_pipe(
    pipe,
    mesh=mesh,
)
parallelize_vae(pipe.vae, mesh=mesh._flatten())

from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe

apply_cache_on_pipe(
    pipe,
    residual_diff_threshold=0.12,  # Use a larger value to make the cache take effect
)

from torchao.quantization import quantize_, float8_dynamic_activation_float8_weight, float8_weight_only

quantize_(pipe.text_encoder, float8_weight_only())
quantize_(pipe.transformer, float8_dynamic_activation_float8_weight())
torch._inductor.config.reorder_for_compute_comm_overlap = True
pipe.transformer = torch.compile(
   pipe.transformer, mode="max-autotune-no-cudagraphs",
)

# Enable memory savings
# pipe.enable_model_cpu_offload(gpu_id=dist.get_rank())
# pipe.enable_sequential_cpu_offload(gpu_id=dist.get_rank())

for i in range(2):
    begin = time.time()
    image = pipe(
        "A cat holding a sign that says hello world",
        num_inference_steps=28,
        output_type="pil" if dist.get_rank() == 0 else "pt",
    ).images[0]
    end = time.time()
    if dist.get_rank() == 0:
        if i == 0:
            print(f"Warm up time: {end - begin:.2f}s")
        else:
            print(f"Time: {end - begin:.2f}s")

if dist.get_rank() == 0:
    print("Saving image to flux.png")
    image.save("flux.png")

dist.destroy_process_group()

run_flux.py dosyasına kaydedin ve torchrun ile başlatın.

# Use --nproc_per_node to specify the number of GPUs
torchrun --nproc_per_node=2 run_flux.py

2 NVIDIA L20 GPU ile çıkarım hızı, baz çizgiye kıyasla 8,20 saniyeye düşer; bu da 3,21 kat daha hızlı demektir. 4 L20 ile çıkarım hızı 3,90 saniyedir, yani 6,75 kat daha hızlıdır.

Benchmarks

FLUX-1.dev
HunyuanVideo
GPU TürüGPU sayısıOptimizasyonlarDuvar Saati (s)Hızlanma
NVIDIA L201Taban26.361.00x
NVIDIA L201FBCache (rdt=0.08)17.011.55x
NVIDIA L201FP8 DQ13.401.96x
NVIDIA L201FBCache (rdt=0.12) + FP8 DQ7.563.48x
NVIDIA L202FBCache (rdt=0.12) + FP8 DQ + CP4.925.35x
NVIDIA L204FBCache (rdt=0.12) + FP8 DQ + CP3.906.75x
Update on GitHub