Diffusers 文件
ParaAttention
並獲得增強的文件體驗
開始使用
ParaAttention


大型影像和影片生成模型,例如 FLUX.1-dev 和 HunyuanVideo,由於其龐大的規模,對即時應用和部署來說可能是一個推理挑戰。
ParaAttention 是一個實現**上下文並行**和**首塊快取**的庫,可以與其他技術(torch.compile,fp8 動態量化)結合使用,以加速推理。
本指南將向您展示如何在 NVIDIA L20 GPU 上將 ParaAttention 應用於 FLUX.1-dev 和 HunyuanVideo。除了 HunyuanVideo 為避免記憶體不足錯誤而進行的最佳化外,我們的基準測試沒有應用任何最佳化。
我們的基準測試顯示,FLUX.1-dev 能夠在 28 步內生成 1024x1024 解析度的影像,耗時 26.36 秒;HunyuanVideo 能夠在 30 步內生成 129 幀 720p 解析度的影片,耗時 3675.71 秒。
要透過上下文並行實現更快的推理,請嘗試使用支援 NVLink 的 NVIDIA A100 或 H100 GPU(如果可用),尤其是在 GPU 數量較多的情況下。
首塊快取
快取模型中 Transformer 塊的輸出並在接下來的推理步驟中重複使用它們可以降低計算成本並加快推理速度。
然而,很難決定何時重用快取以確保生成影像或影片的質量。ParaAttention 直接使用**第一個 Transformer 塊輸出的殘差差異**來近似模型輸出之間的差異。當差異足夠小時,前一個推理步驟的殘差差異將被重用。換句話說,去噪步驟被跳過。
這在 FLUX.1-dev 和 HunyuanVideo 推理中實現了 2 倍的加速,同時保持了非常好的質量。

要在 FLUX.1-dev 上應用首塊快取,請按如下所示呼叫 `apply_cache_on_pipe`。0.08 是 FLUX 模型預設的殘差差異值。
import time
import torch
from diffusers import FluxPipeline
pipe = FluxPipeline.from_pretrained(
"black-forest-labs/FLUX.1-dev",
torch_dtype=torch.bfloat16,
).to("cuda")
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")
最佳化 | 原始 | FBCache rdt=0.06 | FBCache rdt=0.08 | FBCache rdt=0.10 | FBCache rdt=0.12 |
---|---|---|---|---|---|
預覽 | ![]() | ![]() | ![]() | ![]() | ![]() |
牆鍾時間 (秒) | 26.36 | 21.83 | 17.01 | 16.00 | 13.78 |
與基準線相比,首塊快取將推理速度降低到 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
torch.compile 使用 `mode="max-autotune-no-cudagraphs"` 或 `mode="max-autotune"` 選擇最佳核心以獲得性能。如果模型是第一次呼叫,編譯可能需要很長時間,但一旦模型編譯完成,這是值得的。
此示例僅量化 Transformer 模型,但您也可以量化文字編碼器以進一步減少記憶體使用。
動態量化會顯著改變模型輸出的分佈,因此您需要將 `residual_diff_threshold` 更改為更大的值才能使其生效。
import time
import torch
from diffusers import FluxPipeline
pipe = FluxPipeline.from_pretrained(
"black-forest-labs/FLUX.1-dev",
torch_dtype=torch.bfloat16,
).to("cuda")
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 倍。
上下文並行
上下文並行可並行化推理並隨多個 GPU 擴充套件。ParaAttention 的組合設計允許您將上下文並行與首塊快取和動態量化相結合。
請參閱 ParaAttention 倉庫,瞭解如何使用多個 GPU 擴充套件推理的詳細說明和示例。
如果推理過程需要持久且可服務,建議使用 torch.multiprocessing 編寫自己的推理處理器。這可以消除啟動程序以及載入和重新編譯模型的開銷。
以下程式碼示例結合了首塊快取、fp8 動態量化、torch.compile 和上下文並行,以實現最快的推理速度。
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",
torch_dtype=torch.bfloat16,
).to("cuda")
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 GPU 上,推理速度為 3.90 秒,即快 6.75 倍。
基準測試
GPU 型別 | GPU 數量 | 最佳化 | 牆鍾時間 (秒) | 加速比 |
---|---|---|---|---|
NVIDIA L20 | 1 | 基線 | 26.36 | 1.00倍 |
NVIDIA L20 | 1 | FBCache (rdt=0.08) | 17.01 | 1.55倍 |
NVIDIA L20 | 1 | FP8 DQ | 13.40 | 1.96倍 |
NVIDIA L20 | 1 | FBCache (rdt=0.12) + FP8 DQ | 7.56 | 3.48倍 |
NVIDIA L20 | 2 | FBCache (rdt=0.12) + FP8 DQ + CP | 4.92 | 5.35倍 |
NVIDIA L20 | 4 | FBCache (rdt=0.12) + FP8 DQ + CP | 3.90 | 6.75倍 |