Diffusers documentation

ParaAttention

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

ParaAttention

FLUX.1-dev や HunyuanVideo などの大規模な画像・動画生成モデルは、そのサイズゆえに、リアルタイムアプリケーションやデプロイにおける推論の課題となり得ます。

ParaAttention は、context parallelism と first block cache を実装したライブラリであり、他の技術(torch.compile、fp8 動的量子化)と組み合わせることで、推論を高速化できます。

このガイドでは、NVIDIA L20 GPU上でFLUX.1-devとHunyuanVideoにParaAttentionを適用する方法を示します。 ベースラインベンチマークには最適化は適用されていませんが、HunyuanVideoについてはメモリ不足エラーを回避するために例外として適用しています。

ベースラインのベンチマークによると、FLUX.1-dev は 28 ステップで 1024x1024 解像度の画像を 26.36 秒で生成でき、HunyuanVideo は 30 ステップで 720p 解像度の 129 フレームを 3675.71 秒で生成できます。

コンテキスト並列性によるさらに高速な推論を実現するには、特にGPUの台数が多い場合、NVLink対応のNVIDIA A100またはH100 GPU(利用可能な場合)の使用をお試しください。

最初のブロックキャッシュ

モデル内のtransformersブロックの出力をキャッシュし、次の推論ステップで再利用することで、計算コストを削減し、推論を高速化できます。

ただし、生成される画像や動画の品質を確保するために、キャッシュを再利用するタイミングを判断するのは容易ではありません。ParaAttention は、最初の Transformer ブロックの出力の残差差を直接使用して、モデル出力間の差を近似します。差が十分に小さい場合、以前の推論ステップの残差差が再利用されます。言い換えれば、デノイジングステップがスキップされるということです。

これにより、FLUX.1-dev および HunyuanVideo の推論で 2 倍の高速化を実現し、非常に高い品質を維持します。

Cache in Diffusion Transformer
AdaCacheの仕組み、First Block Cacheはそのバリアントです
FLUX-1.dev
HunyuanVideo

FLUX.1-dev に first block cache を適用するには、以下に示すように apply_cache_on_pipe を呼び出します。0.08 は FLUX モデルのデフォルトの residual difference 値です。

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")
最適化OriginalFBCache rdt=0.06FBCache rdt=0.08FBCache rdt=0.10FBCache rdt=0.12
プレビューOriginalFBCache rdt=0.06FBCache rdt=0.08FBCache rdt=0.10FBCache rdt=0.12
実時間 (秒)26.3621.8317.0116.0013.78

First Block Cache は、ベースラインと比較して推論速度を 17.01 秒に短縮し、1.55 倍の高速化を実現しながら、品質の低下をほぼゼロに抑えました。

fp8量子化

動的量子化を伴うfp8は、推論の高速化とメモリ使用量の削減をさらに実現します。8ビットのNVIDIA Tensor Coresを使用するには、アクティベーションと重みの両方を量子化する必要があります。

float8_weight_onlyとfloat8_dynamic_activation_float8_weightを使用して、テキストエンコーダーとtransformerモデルを量子化します。

デフォルトの量子化手法はテンソルごとの量子化ですが、GPUが行ごとの量子化をサポートしている場合は、より高い精度を得るためにこれを試すこともできます。

以下のコマンドでtorchaoをインストールします。

pip3 install -U torch torchao

mode="max-autotune-no-cudagraphs" または mode="max-autotune" を指定した torch.compile は、パフォーマンスを最大化する最適なカーネルを選択します。モデルが初めて呼び出される場合、コンパイルに長時間かかることがありますが、一度コンパイルが完了すればその価値は十分にあります。

この例では、transformerモデルのみを量子化していますが、テキストエンコーダーも量子化することで、メモリ使用量をさらに削減できます。

動的量子化はモデルの出力分布を大きく変える可能性があるため、residual_diff_thresholdをより大きな値に変更する必要があります。

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 動的量子化と torch.compile を使用することで、ベースラインと比較して推論速度が 7.56 秒に短縮され、3.48 倍高速化されました。

コンテキスト並列

Context Parallelism は推論を並列化し、複数の GPU によってスケーリングします。ParaAttention の構成要素としての設計により、Context Parallelism を First Block Cache および動的量子化と組み合わせることができます。

複数のGPUで推論をスケーリングする方法の詳細な手順や例については、ParaAttentionリポジトリを参照してください。

推論プロセスを永続的かつサービス可能にする必要がある場合は、torch.multiprocessing を使用して独自の推論プロセッサを作成することをお勧めします。これにより、プロセスの起動、モデルの読み込み、再コンパイルに伴うオーバーヘッドを排除できます。

FLUX-1.dev
HunyuanVideo

以下のコード例では、First Block Cache、fp8 動的量子化、torch.compile、および Context Parallelism を組み合わせることで、最速の推論速度を実現します。

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 に保存し、torchrun で起動します。

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

2基のNVIDIA L20 GPUを使用した場合、推論速度はベースラインと比較して8.20秒に短縮され、3.21倍高速化されます。4基のL20を使用した場合、推論速度は3.90秒となり、6.75倍高速化されます。

ベンチマーク

FLUX-1.dev
HunyuanVideo
GPU タイプGPUの数最適化実時間 (秒)高速化
NVIDIA L201ベースライン26.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