Buckets:

hf-doc-build/doc / diffusers /main /vi /optimization /context_parallelism.md
mishig's picture
|
download
raw
13.4 kB

Song song ngữ cảnh

Context parallelism chia chuỗi đầu vào giữa các GPU, và mỗi GPU xử lý phần riêng của nó. Điều này làm giảm bộ nhớ trên mỗi GPU, hữu ích nhất cho các chuỗi dài như ảnh độ phân giải cao và video. Để so sánh với các chiến lược khác, xem Distributed inference.

Context parallelism được thực thi bên trong attention backend, và chỉ một số backend hỗ trợ tính năng này. Nếu backend của bạn không hỗ trợ, enable_parallelism sẽ gây ra lỗi liệt kê các backend tương thích. Hãy chuyển đổi backend bằng set_attention_backend() và xem các backend khả dụng để có danh sách đầy đủ.

Cấu hình mọi biến thể bên dưới bằng ContextParallelConfig.

Biến thể Cấu hình Yêu cầu Sử dụng khi
Ring Attention ring_degree > 1 chiều dài chuỗi phải chia hết cho ring_degree các chuỗi dài và băng thông liên kết giữa các bộ xử lý bị hạn chế
Ulysses Attention ulysses_degree > 1 chiều dài chuỗi và số đầu chú ý phải chia hết cho ulysses_degree các GPU của bạn có kết nối nhanh
Unified Attention ring_degree > 1 và ulysses_degree > 1 ít nhất 4 GPU bạn có nhiều thiết bị hơn số thiết bị mà Ulysses Attention có thể sử dụng một mình
Ulysses Anything Attention ulysses_degree > 1, ulysses_anything=True ring_degree phải bằng 1 độ dài chuỗi hoặc số đầu không chia hết
Ring Anything Attention ring_degree > 1, ring_anything=True ulysses_degree phải là 1, chỉ suy luận, attn_mask phải là None chiều dài chuỗi không chia hết

Ring Attention

Ring Attention truyền các khối key (K) và value (V) giữa các GPU được sắp xếp thành một vòng. Mỗi GPU tính toán attention với khối K/V mà nó đang giữ, sau đó truyền khối đó sang GPU tiếp theo. Sau một vòng lặp hoàn chỉnh, mọi GPU đều đã xem qua K/V của mọi token, và không có GPU nào giữ toàn bộ chuỗi.

Đặt ring_degree bằng số lượng GPU và truyền cấu hình vào phương thức enable_parallelism() của transformer.

import os
import torch
from torch import distributed as dist
from diffusers import DiffusionPipeline, ContextParallelConfig

def setup_distributed():
    if not dist.is_initialized():
        dist.init_process_group(backend="nccl")
    # torchrun sets LOCAL_RANK to this process's GPU index on its node
    local_rank = int(os.environ["LOCAL_RANK"])
    device = torch.device(f"cuda:{local_rank}")
    torch.cuda.set_device(device)
    return device

def main():
    device = setup_distributed()
    world_size = dist.get_world_size()

    pipeline = DiffusionPipeline.from_pretrained(
        "black-forest-labs/FLUX.1-dev", dtype=torch.bfloat16
    ).to(device)
    pipeline.transformer.set_attention_backend("_native_cudnn")

    cp_config = ContextParallelConfig(ring_degree=world_size)
    pipeline.transformer.enable_parallelism(config=cp_config)

    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
    """

    # Must specify generator so all ranks start with same latents (or pass your own)
    generator = torch.Generator().manual_seed(42)
    image = pipeline(
        prompt,
        guidance_scale=3.5,
        num_inference_steps=50,
        generator=generator,
    ).images[0]

    if dist.get_rank() == 0:
        image.save("output.png")

    if dist.is_initialized():
        dist.destroy_process_group()

if __name__ == "__main__":
    main()

Lưu script dưới tên ring_attention.py và chạy bằng torchrun hoặc trình khởi chạy tương thích PyTorch khác. Đặt --nproc-per-node bằng số lượng GPU.

torchrun --nproc-per-node 2 ring_attention.py

Để thử các biến thể khác, hãy thay thế lời gọi enable_parallelism trong ring_attention.py bằng lời gọi từ phần tương ứng của chúng.

Ulysses Attention

Ulysses Attention sử dụng giao tiếp all-to-all, trong đó mỗi GPU trao đổi dữ liệu với mọi GPU khác. Thao tác all-to-all đầu tiên nhóm lại dữ liệu để mỗi GPU giữ mọi token cho một tập con các attention head. Mỗi GPU tính toán attention cho các head của nó, và thao tác all-to-all thứ hai nhóm lại kết quả theo token cho lớp tiếp theo.

Đặt ulysses_degree bằng số lượng GPU và truyền cấu hình vào enable_parallelism().

pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ulysses_degree=2))

Unified Attention

Unified Sequence Parallelism kết hợp Ring Attention và Ulysses Attention. Nó thực hiện all-to-all để phân phối lại heads và tokens, chạy Ring Attention trên kết quả, sau đó đảo ngược all-to-all để khôi phục bố cục ban đầu. Ulysses song song hóa trên các attention heads và Ring song song hóa trên sequence, do đó khi kết hợp chúng có thể sử dụng nhiều GPU hơn so với chỉ dùng Ulysses.

Sử dụng Unified Attention khi bạn có ít nhất 4 GPU. Đặt cả ulysses_degree và ring_degree lớn hơn 1. Các GPU tạo thành một lưới 2D gồm ulysses_degree * ring_degree thiết bị, và mỗi GPU thuộc về một nhóm Ulysses và một nhóm Ring.

pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ulysses_degree=2, ring_degree=2))

Bảng chuẩn dưới đây so sánh Ulysses, Ring và Unified Attention trên 4 GPU H100 bằng script này. Mỗi hình ảnh sử dụng 50 bước suy luận.

CP Backend Thời gian / Ảnh (ms) Bước / Giây Bộ nhớ đỉnh (GB)
ulysses 6670.789 7.50 33.85
ring 13076.492 3.82 56.02
unified_balanced 11068.705 4.52 33.85

Ulysses có thông lượng cao nhất, nhưng không thể sử dụng nhiều GPU hơn số đầu chú ý (attention heads) của mô hình. Unified Attention loại bỏ giới hạn đó.

Ulysses Anything Attention

Ulysses Attention yêu cầu độ dài chuỗi và số đầu attention phải chia hết cho ulysses_degree. Ulysses Anything Attention loại bỏ cả hai yêu cầu này. Bạn không thể kết hợp nó với Unified Attention, do đó ring_degree phải bằng 1.

Đặt ulysses_degree lớn hơn 1 và ulysses_anything=True.

pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ulysses_degree=2, ulysses_anything=True))

Thêm backend gloo cho tensor CPU vào init_process_group. Điều này tránh được các đồng bộ hóa CUDA bắt buộc do các thao tác chuyển dữ liệu từ host sang thiết bị và từ thiết bị sang host gây ra.

import torch.distributed as dist

dist.init_process_group(backend="cpu:gloo,cuda:nccl")

Bảng đánh giá dưới đây chạy FLUX.1-dev trên 4 GPU L20 bằng script này.

CP Backend Thời gian / Lặp (ms) Bước / Giây Bộ nhớ đỉnh (GB) Hình dạng (HxW)
ulysses 281.07 3.56 37.11 1024x1024
ring 351.34 2.85 37.01 1024x1024
unified_balanced 324.37 3.08 37.16 1024x1024
ulysses_anything 280.94 3.56 37.11 1024x1024
ulysses thất bại thất bại thất bại 1008x1008
ring thất bại thất bại thất bại 1008x1008
unified_balanced thất bại thất bại thất bại 1008x1008
ulysses_anything 278.40 3.59 36.99 1008x1008

Ở kích thước 1008x1008, khi độ dài chuỗi không chia hết, chỉ có Ulysses Anything Attention được thực thi. Ở kích thước 1024x1024, hiệu năng của nó tương đương với Ulysses Attention tiêu chuẩn.

Ring Anything Attention

Ring Attention yêu cầu độ dài chuỗi phải chia hết cho ring_degree. Ring Anything Attention hỗ trợ mọi độ dài chuỗi. Nó đệm K/V cục bộ của mỗi GPU lên độ dài cục bộ dài nhất, thực hiện all-gather trên K/V đã đệm, và cắt mỗi khối về độ dài thực trước khi tính toán attention.

Bạn không thể kết hợp nó với Unified Attention, do đó ulysses_degree phải bằng 1. Đặt ring_degree lớn hơn 1 và ring_anything=True.

pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ring_degree=2, ring_anything=True))

Ring Anything Attention cũng được hưởng lợi từ backend gloo được trình bày trong Ulysses Anything Attention.

Ring Anything Attention chỉ hỗ trợ suy luận, và attn_mask phải là None.

Bảng chuẩn (benchmark) bên dưới chạy FLUX.1-dev trên 4 GPU RTX 4090 (48GB).

CP Backend Thời gian / Lặp (ms) Bước / Giây Bộ nhớ đỉnh (GB) Hình dạng (HxW)
ulysses 259.07 3.86 33.83 1024x1024
ring 338.98 2.95 33.83 1024x1024
unified_balanced 321.54 3.11 33.83 1024x1024
ulysses_anything 259.07 3.86 33.83 1024x1024
ring_anything 340.14 2.94 33.83 1024x1024
ulysses thất bại thất bại thất bại 1008x1008
ring thất bại thất bại thất bại 1008x1008
unified_balanced thất bại thất bại thất bại 1008x1008
ulysses_anything 253.16 3.95 33.75 1008x1008
ring_anything 335.57 2.98 33.75 1008x1008

Ring Anything Attention chạy ở kích thước 1008x1008 và cho hiệu suất tương đương với Ring Attention tiêu chuẩn. Để tìm hiểu về động lực và các đánh đổi, hãy xem bình luận này.

Bật song song ngữ cảnh khi tải

Thay vì gọi enable_parallelism sau khi tải, hãy truyền ContextParallelConfig vào tham số parallel_config của from_pretrained(). Khởi tạo nhóm tiến trình trước, vì from_pretrained sẽ gây ra lỗi nếu torch.distributed chưa được khởi tạo. Chạy script bằng torchrun, như trong ví dụ Ring Attention.

import os
import torch
from torch import distributed as dist
from diffusers import AutoModel, ContextParallelConfig, DiffusionPipeline

CKPT_ID = "black-forest-labs/FLUX.1-dev"

dist.init_process_group(backend="nccl")
local_rank = int(os.environ["LOCAL_RANK"])
device = torch.device(f"cuda:{local_rank}")
torch.cuda.set_device(device)

cp_config = ContextParallelConfig(ring_degree=dist.get_world_size())
transformer = AutoModel.from_pretrained(
    CKPT_ID,
    subfolder="transformer",
    dtype=torch.bfloat16,
    parallel_config=cp_config
)

pipeline = DiffusionPipeline.from_pretrained(
    CKPT_ID, transformer=transformer, dtype=torch.bfloat16,
).to(device)

Xet Storage Details

Size:
13.4 kB
·
Xet hash:
5c3097d54c1b08b53d8cf2a4de8567f7a555db1e3cfa5375b0a33480ed570933

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