Buckets:

hf-doc-build/doc / diffusers /main /es /optimization /context_parallelism.md
mishig's picture
|
download
raw
12.8 kB
# Paralelismo de contexto
[El paralelismo de contexto](https://huggingface.co/spaces/nanotron/ultrascale-playbook?section=context_parallelism) divide la secuencia de entrada entre las GPU y cada GPU procesa su propia porción. Esto reduce la memoria por GPU, lo cual resulta más útil para secuencias largas, como imágenes de alta resolución y vídeo. Para compararlo con las demás estrategias, consulte [Inferencia distribuida](../training/distributed_inference).
El paralelismo de contexto se ejecuta dentro del backend de atención, y solo algunos backends lo admiten. Si el suyo no lo hace, `enable_parallelism` lanza un error que enumera los backends compatibles. Cambie de backend con [set_attention_backend()](/docs/diffusers/main/es/api/models/overview#diffusers.ModelMixin.set_attention_backend) y consulte los [backends disponibles](./attention_backends#available-backends) para ver la lista completa.
Configure cada variante de abajo con [ContextParallelConfig](/docs/diffusers/main/es/api/parallel#diffusers.ContextParallelConfig).
| Variante | Config | Requisitos | Uso |
|---|---|---|---|
| [Ring Attention](#ring-attention) | `ring_degree > 1` | la longitud de la secuencia debe ser divisible exactamente por `ring_degree` | las secuencias son largas y el ancho de banda de interconexión es limitado |
| [Atención Ulysses](#ulysses-attention) | `ulysses_degree > 1` | la longitud de la secuencia y el número de cabezas de atención deben ser divisibles exactamente por `ulysses_degree` | sus GPUs tienen un interconexión rápida |
| [Atención unificada](#unified-attention) | `ring_degree > 1` y `ulysses_degree > 1` | al menos 4 GPUs | tienes más dispositivos de los que Ulysses Attention puede usar por sí solo |
| [Ulysses Anything Attention](#ulysses-anything-attention) | `ulysses_degree > 1`, `ulysses_anything=True` | `ring_degree` debe ser 1 | la longitud de la secuencia o el número de cabezas no se divide de forma uniforme |
| [Ring Anything Attention](#ring-anything-attention) | `ring_degree > 1`, `ring_anything=True` | `ulysses_degree` debe ser 1, solo inferencia, `attn_mask` debe ser `None` | la longitud de la secuencia no se divide exactamente |
## Ring Attention
[Ring Attention](https://huggingface.co/papers/2310.01889) transmite los bloques de claves (K) y valores (V) entre las GPU dispuestas en un anillo. Cada GPU calcula la atención contra el bloque K/V que posee y luego pasa ese bloque a la GPU siguiente. Después de un ciclo completo, cada GPU ha visto los K/V de cada token y ninguna GPU almacena la secuencia completa.
Establezca `ring_degree` en el número de GPUs y pase la configuración al método `enable_parallelism()` del transformer.
```py
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()
```
Guarde el script como `ring_attention.py` y ejecútelo con [torchrun](https://docs.pytorch.org/docs/stable/elastic/run.html) o cualquier otro lanzador compatible con PyTorch. Establezca `--nproc-per-node` en el número de GPUs.
```bash
torchrun --nproc-per-node 2 ring_attention.py
```
Para probar las otras variantes, reemplace la llamada a `enable_parallelism` en `ring_attention.py` con la de su sección.
## Ulysses Attention
[Ulysses Attention](https://huggingface.co/papers/2309.14509) utiliza comunicación *all-to-all*, donde cada GPU intercambia datos con todas las demás GPUs. El primer all-to-all reagrupa los datos para que cada GPU contenga todos los tokens de un subconjunto de cabezas de atención. Cada GPU calcula la atención para sus cabezas, y un segundo all-to-all reagrupa los resultados por token para la siguiente capa.
Establezca `ulysses_degree` en el número de GPUs y pase la configuración a `enable_parallelism()`.
```py
pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ulysses_degree=2))
```
## Atención unificada
[Unified Sequence Parallelism](https://huggingface.co/papers/2405.07719) combina Ring Attention y Ulysses Attention. Ejecuta un all-to-all para redistribuir las cabezas y los tokens, ejecuta Ring Attention sobre el resultado y luego invierte el all-to-all para restaurar el diseño original. Ulysses paraleliza entre las cabezas de atención y Ring paraleliza a lo largo de la secuencia, por lo que, juntos, pueden utilizar más GPUs que Ulysses por sí solo.
Use Unified Attention cuando tenga al menos 4 GPUs. Establezca tanto `ulysses_degree` como `ring_degree` en un valor mayor que 1. Las GPUs forman una cuadrícula 2D de `ulysses_degree * ring_degree` dispositivos, y cada GPU pertenece a un grupo Ulysses y a un grupo Ring.
```py
pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ulysses_degree=2, ring_degree=2))
```
El siguiente benchmark compara Ulysses, Ring y Unified Attention en 4 GPUs H100 con [este script](https://github.com/huggingface/diffusers/pull/12693#issuecomment-3694727532). Cada imagen utiliza 50 pasos de inferencia.
| Backend de CP | Tiempo / Imagen (ms) | Pasos / Seg | Memoria máxima (GB) |
|--------------------|------------------|-------------|------------------|
| ulysses | 6670.789 | 7.50 | 33.85 |
| ring | 13076.492 | 3.82 | 56.02 |
| unified_balanced | 11068.705 | 4.52 | 33.85 |
Ulysses tiene el mayor rendimiento, pero no puede usar más GPUs que las cabezas de atención que tiene el modelo. Unified Attention elimina ese límite.
## Ulysses Anything Attention
Ulysses Attention requiere que la longitud de la secuencia y el número de cabezas de atención sean divisibles exactamente por `ulysses_degree`. [Ulysses Anything Attention](https://github.com/huggingface/diffusers/pull/12996) elimina ambos requisitos. No se puede combinar con Unified Attention, por lo que `ring_degree` debe ser 1.
Establezca `ulysses_degree` en un valor mayor que 1 y `ulysses_anything=True`.
```py
pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ulysses_degree=2, ulysses_anything=True))
```
> [!TIP]
> Agregue un backend `gloo` para tensores de CPU a [init_process_group](https://docs.pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group). Esto evita las sincronizaciones forzadas de CUDA que provocan las transferencias de host a dispositivo y de dispositivo a host.
```py
import torch.distributed as dist
dist.init_process_group(backend="cpu:gloo,cuda:nccl")
```
El benchmark siguiente ejecuta FLUX.1-dev en 4 GPUs L20 con [este script](https://github.com/huggingface/diffusers/pull/12996#issuecomment-3797695999).
| Backend de CP | Tiempo / Iteración (ms) | Pasos / Seg | Memoria máxima (GB) | Forma (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 | fallido | fallido | fallido | 1008x1008 |
| ring | fallido | fallido | fallido | 1008x1008 |
| unified_balanced | fallido | fallido | fallido | 1008x1008 |
| ulysses_anything | 278.40 | 3.59 | 36.99 | 1008x1008 |
En 1008x1008, donde la longitud de la secuencia no se divide exactamente, solo se ejecuta Ulysses Anything Attention. En 1024x1024, su rendimiento es el mismo que el de Ulysses Attention estándar.
## Ring Anything Attention
Ring Attention requiere que la longitud de la secuencia sea divisible exactamente por `ring_degree`. [Ring Anything Attention](https://github.com/huggingface/diffusers/pull/13545#issuecomment-4302195582) admite cualquier longitud de secuencia. Rellena los K/V locales de cada GPU hasta la longitud local más larga, realiza un all-gather de los K/V rellenos y recorta cada bloque a su longitud real antes de calcular la atención.
No se puede combinar con Unified Attention, por lo que `ulysses_degree` debe ser 1. Establezca `ring_degree` en un valor mayor que 1 y `ring_anything=True`.
```py
pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ring_degree=2, ring_anything=True))
```
Ring Anything Attention también se beneficia del backend `gloo` mostrado en [Ulysses Anything Attention](#ulysses-anything-attention).
> [!NOTE]
> Ring Anything Attention solo admite inferencia y `attn_mask` debe ser `None`.
El benchmark siguiente ejecuta FLUX.1-dev en 4 GPUs RTX 4090 (48GB).
| Backend de CP | Tiempo / Iteración (ms) | Pasos / Seg | Memoria máxima (GB) | Forma (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 | fallido | fallido | fallido | 1008x1008 |
| ring | fallido | fallido | fallido | 1008x1008 |
| unified_balanced | fallido | fallido | fallido | 1008x1008 |
| ulysses_anything | 253.16 | 3.95 | 33.75 | 1008x1008 |
| ring_anything | 335.57 | 2.98 | 33.75 | 1008x1008 |
Ring Anything Attention se ejecuta a 1008x1008 y rinde aproximadamente igual que el Ring Attention estándar. Para conocer la motivación y las compensaciones, consulte [este comentario](https://github.com/huggingface/diffusers/pull/13545#issuecomment-4304104462).
## Activar el paralelismo de contexto durante la carga
En lugar de llamar a `enable_parallelism` después de cargar, pase el [ContextParallelConfig](/docs/diffusers/main/es/api/parallel#diffusers.ContextParallelConfig) al argumento `parallel_config` de [from_pretrained()](/docs/diffusers/main/es/api/models/overview#diffusers.ModelMixin.from_pretrained). Inicialice primero el grupo de procesos, ya que `from_pretrained` genera un error si `torch.distributed` no está inicializado. Ejecute el script con `torchrun`, como en el ejemplo de [Ring Attention](#ring-attention).
```py
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:
12.8 kB
·
Xet hash:
7f73e0eb20dcbae6421dd4ee223e210ceb24c7c7209c207f7fb6587efcbbb4ac

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