# Dağıtık çıkarım

Dağıtık çıkarım, iş yükünü birden fazla GPU arasında paylaştırır. Daha büyük modellerin belleğe sığdırılması için faydalı bir tekniktir ve daha yüksek bir aktarım hızı için birden çok istemciyi işleyebilir.

Bu kılavuz, dağıtık çıkarım için [Accelerate](https://huggingface.co/docs/accelerate/index) ve [PyTorch Distributed](https://pytorch.org/tutorials/beginner/dist_overview.html) kullanmayı gösterecektir.

## Accelerate

Accelerate, kurulumu ele alarak kullanıcıların PyTorch kodlarına odaklanmasını sağlamak üzere, birden fazla hızlandırıcıda çıkarım ve eğitimi basitleştirmek için tasarlanmış bir kütüphanedir.

Aşağıdaki komutla Accelerate'i yükleyin.

```bash
uv pip install accelerate
```

Dağıtık bir ortam oluşturmak için bir Python dosyasında [accelerate.PartialState](https://huggingface.co/docs/accelerate/main/en/package_reference/state#accelerate.PartialState) sınıfını başlatın. [accelerate.PartialState](https://huggingface.co/docs/accelerate/main/en/package_reference/state#accelerate.PartialState) sınıfı, süreç yönetimi, cihaz kontrolü ve dağıtımı ile süreç koordinasyonunu yönetir.

Her bir işlem için bir GPU atamak üzere [DiffusionPipeline](/docs/diffusers/main/tr/api/pipelines/overview#diffusers.DiffusionPipeline) nesnesini `accelerate.PartialState.device` konumuna taşıyın.

```py
import torch
from accelerate import PartialState
from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained(
    "Qwen/Qwen-Image", dtype=torch.float16
)
distributed_state = PartialState()
pipeline.to(distributed_state.device)
```

İstemcileri işlem sayısı arasında otomatik olarak dağıtmak için [split_between_processes](https://huggingface.co/docs/accelerate/main/en/package_reference/state#accelerate.PartialState.split_between_processes) yardımcısını bir bağlam yöneticisi olarak kullanın.

```py
with distributed_state.split_between_processes(["a dog", "a cat"]) as prompt:
    result = pipeline(prompt).images[0]
    result.save(f"result_{distributed_state.process_index}.png")
```

Betik çalıştırmak için `accelerate launch` komutunu çağırın ve kullanılacak GPU sayısını belirlemek için `--num_processes` argümanını kullanın.

```bash
accelerate launch run_distributed.py --num_processes=2
```

> [!TIP]
> Birden fazla GPU üzerinde çıkarım (inference) çalıştırmak için bu minimal [script](https://gist.github.com/sayakpaul/cfaebd221820d7b43fae638b4dfa01ba) örneğine bakın. Daha fazla bilgi edinmek için [Distributed Inference with 🤗 Accelerate](https://huggingface.co/docs/accelerate/en/usage_guides/distributed_inference#distributed-inference-with-accelerate) kılavuzuna göz atın.

## PyTorch Dağıtık

PyTorch [DistributedDataParallel](https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html), aynı modeli her cihazda çoğaltarak farklı veri topluluklarını paralel olarak işlemek için [data parallelism](https://huggingface.co/spaces/nanotron/ultrascale-playbook?section=data_parallelism) (veri paralelliği) sağlar.

Dağıtık işlem grubunu oluşturmak ve her GPU için çıkarım süreçlerini başlatmak üzere bir Python dosyasına `torch.distributed` ve `torch.multiprocessing` içe aktarın.

```py
import torch
import torch.distributed as dist
import torch.multiprocessing as mp

from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained(
    "Qwen/Qwen-Image", dtype=torch.float16,
)
```

[init_process_group](https://pytorch.org/docs/stable/distributed.html?highlight=init_process_group#torch.distributed.init_process_group) ile çıkarım için bir fonksiyon oluşturun. Bu yöntem, backend türü, mevcut sürecin `rank` değeri ve katılan süreç sayısı olan `world_size` (örneğin, 2 GPU için `world_size=2`) ile dağıtık bir ortam oluşturur.

Hattı `rank`'a taşıyın ve her bir sürece bir GPU atamak için `get_rank` kullanın. Her süreç farklı bir istemciyi işler.

```py
def run_inference(rank, world_size):
    dist.init_process_group("nccl", rank=rank, world_size=world_size)

    pipeline.to(rank)

    if torch.distributed.get_rank() == 0:
        prompt = "a dog"
    elif torch.distributed.get_rank() == 1:
        prompt = "a cat"

    image = sd(prompt).images[0]
    image.save(f"./{'_'.join(prompt)}.png")
```

`world_size` içinde tanımlanan süreç sayısını oluşturmak için [mp.spawn](https://pytorch.org/docs/stable/multiprocessing.html#torch.multiprocessing.spawn) kullanın.

```py
def main():
    world_size = 2
    mp.spawn(run_inference, args=(world_size,), nprocs=world_size, join=True)

if __name__ == "__main__":
    main()
```

Çıkarım betiğini çalıştırmak için `torchrun` çağırın ve kullanılacak GPU sayısını ayarlamak için `--nproc_per_node` argümanını kullanın.

```bash
torchrun --nproc_per_node=2 run_distributed.py
```

## device_map

`device_map` argümanı, model bileşenlerini ayrı GPU'lara otomatik olarak yerleştirerek dağıtık çıkarımı (inference) etkinleştirir. Bu, bir modelin tek bir GPU'ya sığmadığı durumlarda özellikle faydalıdır. Aşağıdaki örnekte gösterildiği gibi (iki GPU'nun mevcut olduğu varsayılır), belirli bir aşamada gerekli model bileşenlerini seçici olarak yüklemek ve boşaltmak için `device_map` kullanabilirsiniz.

`device_map="balanced"` değerini ayarlayarak metin kodlayıcılarını (text encoders) tüm kullanılabilir GPU'lara eşit şekilde dağıtabilirsiniz. Her bir metin kodlayıcı için tahsis edilecek maksimum bellek miktarını belirtmek için `max_memory` argümanını kullanabilirsiniz. Bellek kullanımını önlemek için başka hiçbir pipeline bileşeni yüklemeyin.

```py
from diffusers import FluxPipeline
import torch

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

pipeline = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    transformer=None,
    vae=None,
    device_map="balanced",
    max_memory={0: "16GB", 1: "16GB"},
    dtype=torch.bfloat16
)
with torch.no_grad():
    print("Encoding prompts.")
    prompt_embeds, pooled_prompt_embeds, text_ids = pipeline.encode_prompt(
        prompt=prompt, prompt_2=None, max_sequence_length=512
    )
```

Metin gömlemleri hesaplandıktan sonra, difüzyon dönüştürücüsü için yer açmak üzere GPU'dan kaldırın.

```py
import gc 

def flush():
    gc.collect()
    torch.cuda.empty_cache()
    torch.cuda.reset_max_memory_allocated()
    torch.cuda.reset_peak_memory_stats()

del pipeline.text_encoder
del pipeline.text_encoder_2
del pipeline.tokenizer
del pipeline.tokenizer_2
del pipeline

flush()
```

`device_map="auto"` değerini ayarlayarak modelin iki GPU üzerinde otomatik olarak dağıtılmasını sağlayın. Bu strateji, modeli önce en hızlı cihaza yerleştirir; gerekirse modeli CPU veya sabit disk gibi daha yavaş bir cihaza yerleştirir. Model parametrelerini daha yavaş cihazlarda saklamanın dezavantajı, daha yavaş çıkarım gecikmesidir.

```py
from diffusers import AutoModel
import torch 

transformer = AutoModel.from_pretrained(
    "black-forest-labs/FLUX.1-dev", 
    subfolder="transformer",
    device_map="auto",
    dtype=torch.bfloat16
)
```

> [!TIP]
> Farklı modellerin cihazlar arasında nasıl dağıtıldığını görmek için `pipeline.hf_device_map` çalıştırın. Bu, model cihaz yerleşimini izlemek için kullanışlıdır. Ayrıca, dağılımını görmek için dönüştürücü model üzerinde `hf_device_map` çağrısını da yapabilirsiniz.

Dönüştürücü modelini hattın (pipeline) içine ekleyin ve latentleri üretmek için `output_type="latent"` değerini ayarlayın.

```py
pipeline = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    text_encoder=None,
    text_encoder_2=None,
    tokenizer=None,
    tokenizer_2=None,
    vae=None,
    transformer=transformer,
    dtype=torch.bfloat16
)

print("Running denoising.")
height, width = 768, 1360
latents = pipeline(
    prompt_embeds=prompt_embeds,
    pooled_prompt_embeds=pooled_prompt_embeds,
    num_inference_steps=50,
    guidance_scale=3.5,
    height=height,
    width=width,
    output_type="latent",
).images
```

Hafızadan pipeline ve transformer'ı kaldırın ve latents'leri çözmek için bir VAE yükleyin. VAE genellikle tek bir cihaza yüklenebilecek kadar küçüktür.

```py
import torch
from diffusers import AutoencoderKL
from diffusers.image_processor import VaeImageProcessor

vae = AutoencoderKL.from_pretrained(ckpt_id, subfolder="vae", dtype=torch.bfloat16).to("cuda")  # or "mps", "xpu", "cpu"
vae_scale_factor = 2 ** (len(vae.config.block_out_channels) - 1)
image_processor = VaeImageProcessor(vae_scale_factor=vae_scale_factor)

with torch.no_grad():
    print("Running decoding.")
    latents = FluxPipeline._unpack_latents(latents, height, width, vae_scale_factor)
    latents = (latents / vae.config.scaling_factor) + vae.config.shift_factor

    image = vae.decode(latents, return_dict=False)[0]
    image = image_processor.postprocess(image, output_type="pil")
    image[0].save("split_transformer.png")
```

Belirli bir aşamada ihtiyaç duyduğunuz modelleri seçici olarak yükleyip boşaltarak ve en büyük modelleri birden çok GPU arasında paylaştırarak, tüketici GPU'larında büyük modellerle çıkarım (inference) çalıştırılabilir.

## Bağlam paralelliği

[Bağlam paralelliği](https://huggingface.co/spaces/nanotron/ultrascale-playbook?section=context_parallelism), bellek kullanımını azaltmak için giriş dizilerini birden çok GPU arasında böler. Her GPU, dizinin kendi dilimini işler.

Daha optimize bir dikkat (attention) arka ucuna geçmek için [set_attention_backend()](/docs/diffusers/main/tr/api/models/overview#diffusers.ModelMixin.set_attention_backend) kullanın. Kullanılabilir arka uçların tam listesi için bu [tabloya](../optimization/attention_backends#available-backends) bakın.

Çoğu dikkat (attention) arka ucu, bağlam paralelliğiyle uyumludur. Bir arka uç uyumlu değilse bir [issue](https://github.com/huggingface/diffusers/issues/new) açın.

### Halka Dikkat

Key (K) ve değer (V) temsilleri, cihazlar arasında [Ring Attention](https://huggingface.co/papers/2310.01889) kullanarak iletişim kurar. Bu, her bölünmenin diğer tüm token'ların K/V'lerini görmesini sağlar. Her GPU, yerel K/V'si için dikkat hesaplamasını yapar ve bunu halkadaki bir sonraki GPU'ya iletir. Tek bir GPU tam diziyi barındırmaz; bu da iletişim gecikmesini azaltır.

Dönüştürücü modelinin `parallel_config` argümanına bir [ContextParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.ContextParallelConfig) geçirin. Yapılandırma, Ring Attention için kaç cihazın kullanılacağını belirleyen `ring_degree` argümanını destekler.

```py
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")
    rank = dist.get_rank()
    device = torch.device(f"cuda:{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(f"output.png")

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

if __name__ == "__main__":
    main()
```

Yukarıdaki betik, PyTorch ile uyumlu [torchrun](https://docs.pytorch.org/docs/stable/elastic/run.html) gibi dağıtık bir başlatıcı ile çalıştırılmalıdır. `--nproc-per-node`, kullanılabilir GPU sayısına ayarlanır.

```shell
torchrun --nproc-per-node 2 above_script.py
```

### Ulysses Dikkat

[Ulysses Attention](https://huggingface.co/papers/2309.14509), bir diziyi GPU'lar arasında böler ve *all-to-all* iletişimi gerçekleştirir (her cihaz, veri göndermek ve almak için her diğer cihazla etkileşime girer). Her GPU, yalnızca dikkat başlıklarının bir alt kümesi için tüm token'lara sahip olur. Her GPU, kendi başlığı için tüm token'lar üzerinde dikkati yerel olarak hesaplar, ardından bir sonraki katman için sonuçları token'lara göre yeniden gruplamak üzere başka bir all-to-all işlemi gerçekleştirir.

[ContextParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.ContextParallelConfig), `ulysses_degree` argümanı aracılığıyla Ulysses Attention'ı destekler. Bu, Ulysses Attention için kaç cihazın kullanılacağını belirler.

[ContextParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.ContextParallelConfig) nesnesini `enable_parallelism()` fonksiyonuna iletin.

```py
# Depending on the number of GPUs available.
pipeline.transformer.enable_parallelism(config=ContextParallelConfig(ulysses_degree=2))
```

### Birleşik Dikkat

[Unified Sequence Parallelism](https://huggingface.co/papers/2405.07719), uzun dizi işleme için verimli bir yaklaşım olarak Ring Attention ve Ulysses Attention'ı tek bir yöntemde birleştirir. Önce başlıkları ve dizi jetonlarını yeniden dağıtmak için Ulysses'in *all-to-all* iletişimini uygular, ardından yeniden dağıtılan verileri işlemek için Ring Attention kullanır ve son olarak orijinal düzeni geri getirmek için *all-to-all* işlemini tersine çevirir.

Bu hibrit yaklaşım, her iki yöntemin güçlü yönlerinden faydalanır:
- **Ulysses Attention**, dikkat başlıkları arasında verimli bir şekilde paralelleştirme sağlar
- **Ring Attention**, çok uzun dizileri minimal bellek maliyetiyle işler
- Birlikte, hem başlar hem de dizi boyutları boyunca 2B paralelleştirmeyi mümkün kılarlar

[ContextParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.ContextParallelConfig), hem `ulysses_degree` hem de `ring_degree` parametrelerini belirleyerek Unified Attention'ı destekler. Kullanılan toplam cihaz sayısı `ulysses_degree * ring_degree`'tür ve Ulysses ile Ring gruplarının dik (keseşmeyen) olduğu 2B bir ızgarada düzenlenir. `ulysses_degree` ve `ring_degree` değerlerinin her ikisi de 1'den büyük olacak şekilde ayarlanmış [ContextParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.ContextParallelConfig) nesnesini `enable_parallelism()` fonksiyonuna iletin.

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

> [!TIP]
> Unified Attention, 2D bir ızgara oluşturmak için yeterli sayıda cihaz olduğunda (en az 4 cihaz) kullanılmalıdır.

Ulysess, Ring ve Unified Attention için [bu betiği](https://github.com/huggingface/diffusers/pull/12693#issuecomment-3694727532) kullanarak 4 H100 GPU'lu bir düğümde bir benchmark çalıştırdık. Sonuçlar aşağıda özetlenmiştir:

| CP Arka Ucu         | Zaman / İterasyon (ms) | Adım / Saniye | Zirve Bellek (GB) |
|--------------------|------------------|-------------|------------------|
| ulysses            | 6670.789         | 7.50        | 33.85            |
| halka               | 13076.492        | 3.82        | 56.02            |
| unified_balanced   | 11068.705        | 4.52        | 33.85            |

Yukarıdaki tablodan, Ulysses'in daha iyi bir veri akışı sağladığı açıkça görülüyor; ancak kullanabileceği cihaz sayısı, dikkat başlarının sayısıyla sınırlı kalmaya devam ediyor ve bu kısıtlama birleşik dikkat (unified attention) ile çözümleniyor.

### Ulysses Anything Dikkat

Varsayılan Ulysses Attention mekanizması, gizli durumların (hidden states) dizi uzunluğunun cihaz sayısına tam bölünmesini gerektirir. Bu durum, Ulysses Attention'ın pratik kullanımına önemli kısıtlamalar getirmektedir. [Ulysses Anything Attention](https://github.com/huggingface/diffusers/pull/12996), Ulysses Attention'ın bir varyantı olup keyfi dizi uzunluklarını ve keyfi dikkat başı (attention head) sayılarını destekleyerek, Ulysses Attention'ın pratik kullanımdaki çok yönlülüğünü artırır.

[ContextParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.ContextParallelConfig), `ulysses_degree` ve `ulysses_anything` parametrelerini birlikte belirleyerek Ulysses Anything Attention desteği sunar. Ulysses Anything Attention'ın şu anda Unified Attention tarafından desteklenmediğini lütfen unutmayın. `ulysses_degree` değerini 1'den büyük olacak şekilde ve `ulysses_anything=True` olarak ayarlanmış [ContextParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.ContextParallelConfig) nesnesini `enable_parallelism()` fonksiyonuna iletin.

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

> [!TIP] H2D ve D2H aktarımlarından kaynaklanan birden fazla zorunlu CUDA senkronizasyonunu önlemek için `init_process_group` içinde **gloo** arka ucunu ekleyin. Bu, iletişim gecikmesini önemli ölçüde azaltacaktır.

FLUX.1-dev için Ulysses, Ring, Unified Attention ve Ulysses Anything Attention ile [bu betiği](https://github.com/huggingface/diffusers/pull/12996#issuecomment-3797695999) kullanarak 4 L20 GPU'lu bir düğümde bir benchmark çalıştırdık. Sonuçlar aşağıda özetlenmiştir:

| CP Arka Ucu         | Zaman / İterasyon (ms) | Adım / Saniye | Zirve Bellek (GB) | Biçim (HxW)|
|--------------------|------------------|-------------|------------------|------------|
| ulysses            |   281.07         |    3.56     |     37.11        | 1024x1024  |
| halka               |   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            |   başarısız         |    başarısız   |     başarısız       | 1008x1008  |
| halka               |   başarısız         |    başarısız   |     başarısız       | 1008x1008  |
| unified_balanced   |   başarısız         |    başarısız   |     başarısız       | 1008x1008  |
| ulysses_anything   |   278.40         |    3.59     |     36.99        | 1008x1008  |

Yukarıdaki tablodan, Ulysses Anything Attention'ın standart Ulysses Attention ile aynı performansı korurken keyfi dizi uzunluklarıyla daha iyi uyumluluk sunduğu açıkça görülmektedir.

### Ring Anything Attention

Varsayılan [Ring Attention](https://huggingface.co/papers/2310.01889), gizli durumların (hidden states) dizi uzunluğunun halka derecesine (ring degree) eşit olarak bölünebilmesini gerektirir. [Ring Anything Attention](https://github.com/huggingface/diffusers/pull/13545#issuecomment-4302195582), Ring Attention'ın keyfi (eşit olarak bölünemeyen) dizi uzunluklarını destekleyen bir varyantıdır. Her sıranın (rank) yerel KV'sini küresel maksimum dizi uzunluğuna kadar doldurur (padding), doldurulmuş KV tamponunu tüm toplar (all-gather) ve dikkat (attention) çalıştırılmadan önce her sıranın gerçek uzunluğuna geri keser.

Ring Anything Attention, Unified Attention tarafından desteklenmez. Ring Anything Attention'i etkinleştirmek için `ring_degree > 1` ve `ring_anything=True` değerlerini ayarlayın.

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

> [!TIP]
> Birden fazla zorunlu CUDA senkronizasyonunun H2D ve D2H aktarımlarından kaynaklanmasını önlemek için [init_process_group](https://docs.pytorch.org/docs/stable/distributed.html#torch.distributed.init_process_group) fonksiyonuna `gloo` arka ucunu ekleyin.

```py
import torch.distributed as dist

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

> [!NOTE]
> Ring Anything Attention şu anda yalnızca çıkarımı destekler ve `None` olmayan dikkat maskeleri desteklenmez. `attn_mask` `None` olmalıdır.

4 RTX 4090 (48GB) GPU'lu bir düğümde FLUX.1-dev referans değerleri aşağıda görülebilir.

| CP Arka Ucu         | Zaman / İterasyon (ms) | Adım / Saniye | Zirve Bellek (GB) | Biçim (HxW)|
|--------------------|------------------|-------------|------------------|------------|
| ulysses            |   259.07         |    3.86     |     33.83        | 1024x1024  |
| halka               |   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            |   başarısız         |    başarısız   |     başarısız       | 1008x1008  |
| halka               |   başarısız         |    başarısız   |     başarısız       | 1008x1008  |
| unified_balanced   |   başarısız         |    başarısız   |     başarısız       | 1008x1008  |
| ulysses_anything   |   253.16         |    3.95     |     33.75        | 1008x1008  |
| ring_anything      |   335.57         |    2.98     |     33.75        | 1008x1008  |

Yukarıdaki tablodan, Ring Anything Attention'ın standart Ring Attention ile karşılaştırılabilir performansını korurken keyfi dizi uzunluklarıyla uyumlu olduğunu görüyoruz. Ring Anything Attention'ın motivasyonu ve ödünleşimleri hakkında daha fazla ayrıntı için [bu yorumu](https://github.com/huggingface/diffusers/pull/13545#issuecomment-4304104462) inceleyin.

### parallel_config

Model başlatma sırasında bağlam paralelliğini etkinleştirmek için `parallel_config` parametresini geçirin.

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

cp_config = ContextParallelConfig(ring_degree=2)
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)
```

## Tensor paralelliği

[Tensor parallelism](https://huggingface.co/spaces/nanotron/ultrascale-playbook?section=tensor_parallelism), bir modelin ağırlık matrislerini cihazlar arasında parçalar. Her cihaz, her katman için sütun bazlı (`"colwise"`) veya satır bazlı (`"rowwise"`) bir dilim tutar, kısmi bir sonuç hesaplar ve katman sınırındaki `AllReduce`/`AllGather` işlemi tam çıktıyı yeniden oluşturur. Bağlam paralelliğinin aksine, cihaz başına *ağırlık* belleğini azaltır; bu da tek bir cihaza sığmayan modeller için faydalıdır.

Modelin [from_pretrained()](/docs/diffusers/main/tr/api/models/overview#diffusers.ModelMixin.from_pretrained) metodunun `parallel_config` argümanına bir [TensorParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.TensorParallelConfig) geçirin. `tp_degree`, parçalanacak cihaz sayısıdır ve modelin dikkat başı sayısına tam bölünmelidir. Model, bir `_tp_plan` (modül adı glob'larından `"colwise"`/`"rowwise"` stiline düz bir eşleme) tanımlamalıdır.

Bu yöntemle yüklenirken kontrol noktası *okunurken* parçalanır: her rank, parçalanan her ağırlığın yalnızca kendi dilimini okur ve onu doğrudan kendi cihazına yerleştirir. Tam boyutlu hiçbir şey asla maddi olarak oluşturulmadığından, rank başına bellek `tp_degree` arttıkça düşer.

`enable_parallelism()` çağrısı yapılarak tam modeli yüklemeye kıyasla, bu yöntem daha hızlı yüklenir ve rank başına daha az CPU belleği kullanır; `tp_degree` arttıkça bu fark büyür. Aşağıdaki sayılar, 4x A10G (23GB) üzerinde [black-forest-labs/FLUX.2-dev](https://huggingface.co/black-forest-labs/FLUX.2-dev) (yalnızca transformer, 32B parametre, bf16) için verilmiştir.

| tp_degree | yöntem | yüklenme süresi | zirve CPU/rank |
|---|---|---|---|
| 4 | `from_pretrained(parallel_config=...)` | 12.5s | 6,8 GB |
| 4 | `from_pretrained` + `enable_parallelism` | 30.4s | 64,1 GB |

```py
import torch
from torch import distributed as dist
from diffusers import DiffusionPipeline, Flux2Transformer2DModel, TensorParallelConfig

def main():
    dist.init_process_group(backend="nccl")
    rank, world_size = dist.get_rank(), dist.get_world_size()
    device = torch.device(f"cuda:{rank}")
    torch.cuda.set_device(device)

    # Each rank reads only its own shard of every planned weight, straight onto `cuda:rank`.
    transformer = Flux2Transformer2DModel.from_pretrained(
        "black-forest-labs/FLUX.2-dev",
        subfolder="transformer",
        torch_dtype=torch.bfloat16,
        parallel_config=TensorParallelConfig(tp_degree=world_size),
    )

    pipeline = DiffusionPipeline.from_pretrained(
        "black-forest-labs/FLUX.2-dev", transformer=transformer, torch_dtype=torch.bfloat16
    )
    # The transformer is already on its device; move the remaining components individually. Do not call
    # `pipeline.to(device)` — that would move every rank's shards onto the same device.
    pipeline.text_encoder.to(device)
    pipeline.vae.to(device)

    generator = torch.Generator().manual_seed(42)
    image = pipeline(prompt="a cat holding a sign that says hello", generator=generator).images[0]
    if rank == 0:
        image.save("output.png")
    dist.destroy_process_group()

if __name__ == "__main__":
    main()
```

```shell
torchrun --nproc-per-node 4 tensor_parallel_flux.py
```

`tp_degree`, yukarıdaki `world_size` değerinden alınır; bu nedenle `--nproc-per-node 4` komutu, transformer'ı 4 cihaz arasında paylaştırır.

> [!CAUTION]
> `parallel_config` ile tensor-parallel yükleme, henüz `device_map`, `quantization_config`, `low_cpu_mem_usage=False`, `use_flashpack=True` veya safetensors olmayan ağırlıklarla desteklenmiyor; her biri sessizce tam checkpoint'i yüklemeye geri düşmek yerine hata fırlatıyor.
>
> Gergilik paralelliğinin (tensor parallelism) kuantizasyon, offloading veya LoRA adaptörleriyle birleştirilmesi henüz desteklenmediğinden, model nasıl bölünürse (shard) olsun bu durumlar yine de … hatası verir.
>
> Tensor-parallel modelleri bu şekilde yüklemenizi öneririz. Aynı yapılandırmayı kullanarak `enable_parallelism()` çağrısı yaparak bellekte zaten bulunan bir modeli dağitalayabilirsiniz (shard), ancak bu yöntem her şeyi önce yükleyip yeniden dağıtır, bu da her rank üzerinde tam checkpoint belleği maliyeti oluşturur.

Tensor-parallel bir modelin kaydedilmesi henüz desteklenmiyor ve [save_pretrained()](/docs/diffusers/main/tr/api/models/overview#diffusers.ModelMixin.save_pretrained) bu durumda bir hata fırlatır. Modeli parçalara ayırmadan önce kaydedin.

### Bir tensör paralelliği planı yazma

Tensor paralelliği, yalnızca modül ad kümeleme kalıplarını bir bölme stiline eşleyen düz bir sınıf özelliği olan `_tp_plan` tanımlayan modellerde çalışır. Bir tane yazmak, büyük ölçüde gizli boyutu *genişleten* her projeksiyonu, onu geri *daraltan* projeksiyonla eşleştirmekle ilgilidir.

Her anahtar en fazla bir **`*`** içerebilir ve bu karakterden önceki önek, tek bir girdinin tüm blokları kapsaması için bir [`nn.ModuleList`](https://pytorch.org/docs/stable/generated/torch.nn.ModuleList.html) olarak çözümlenmelidir. `*` içermeyen bir anahtar, modelin kendisine uygulanır. Yollar modele göre göreli olarak tanımlanır.

#### Colwise ve rowwise

| Stil | Parçalar | Her sıra | Kullanım alanları |
|---|---|---|---|
| `"colwise"` | çıkış özellikleri (`weight` boyutu 0) | çıkışın bir dilimini hesaplar | `to_q`, `to_k`, `to_v`, FFN iç projeksiyonu |
| `"rowwise"` | girdi özellikleri (`weight` boyutu 1) | kısmi bir toplam hesaplar | `to_out.0`, FFN çıkış projeksiyonu |
| `"rowwise_split_input"` | girdi özellikleri (`weight` boyutu 1) | girdinin ve ağırlıkların kendi dilimine kullanarak kısmi bir sonuç hesaplar; `AllReduce` bu sonuçları ekler ve böylece her sıra tüm çıktı özelliklerini alır | örneğin zaman adımı gömümünden bir AdaLN modülasyon projeksiyonu gibi, replike bir aktivasyonu okuyan bağımsız bir projeksiyon |

Her zaman bu sırayla eşleştirin. `"colwise"` projeksiyonu çıktısını parçalı (sharded) bırakır, ardından gelen `"rowwise"` projeksiyonu bu parçayı doğrudan tüketir ve blok sınırındaki tek bir `AllReduce` işlemi sonucu yeniden oluşturur. Çifti başka bir şekilde parçalamak, ortada bir toplama (gather) işlemi zorunlu kılar ve çok daha fazla iletişim gerektirir.

Dikkat mekanizması için bu, her sıranın (rank) başların (head) bir alt kümesine sahip olduğu anlamına gelir; bu nedenle `tp_degree`, baş sayısına tam bölünmelidir. Kodlayıcı akış kopyaları (`add_q_proj`, `to_add_out`, `ff_context`), görüntü akışı karşılıklarıyla aynı deseni izler.

#### Birleştirilmiş projeksiyonlar

Tek bir `Linear`, parçalanan boyut boyunca birden fazla mantıksal tensörü paketlediğinde, düz `"colwise"`/`"rowwise"` dilimleri birleştirmenin üzerinden doğrudan geçer ve parçaları hizasız bırakır. Bunun yerine, her paketlenmiş bloğu bağımsız olarak parçalayan `PackedColwiseParallel`/`PackedRowwiseParallel` kullanın.

```py
# in src/diffusers/models/transformers/your_model.py
from ...hooks.tensor_parallel import PackedColwiseParallel, PackedRowwiseParallel
```

`blocks`, toplamı paketlenmiş boyutu bölen oransal tam sayılardan oluşan bir listedir — eşit yarımlara sahip bir SwiGLU gate+up projeksiyonu için `[1, 1]` veya `mlp_ratio=3` ile birleşik Q+K+V+gate+up projeksiyonu için `[1, 1, 1, 3, 3]`.

```py
"transformer_blocks.*.ff.linear_in": PackedColwiseParallel([1, 1]),
```

Blok boyutları yalnızca yapılandırmadan biliniyorsa, argümanı atlayın ve bunun yerine mutlak boyutları `__init__` sırasında `Linear` üzerinde `_tp_packed_col_blocks` veya `_tp_packed_row_blocks` olarak saklayın.

```py
# in the attention module's __init__
self.to_out._tp_packed_row_blocks = [self.inner_dim, self.mlp_hidden_dim]
```

```py
# in the model's _tp_plan
"single_transformer_blocks.*.attn.to_out": PackedRowwiseParallel(),
```

Her iki biçim de [`transformer_flux2.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_flux2.py) içindeki tek bir planda görünür: çift akışlı SwiGLU çifti `blocks` değerini olduğu gibi geçirirken, birleştirilmiş tek akışlı QKV+MLP projeksiyonu, boyutları `mlp_ratio` ve `mlp_mult_factor` değerlerine bağlı olduğu için bu boyutları `Flux2ParallelSelfAttention.__init__` içindeki `Linear` sınıfında saklar.

#### Dışarıda bırakılacaklar

Planın dışında kalan her şey, her sırada kopyalanır; bu, normalizasyon katmanları, AdaLN modülasyonu (`img_mod`/`txt_mod`), yama ve metin gömme değerleri ile son `norm_out`/`proj_out` için doğru seçimdir. Bunlar küçük olduğundan, bunları bölümlere ayırmak az miktarda bellek tasarrufu sağlar ancak iletişim yükü ekler.

#### Kısıtlamalar ve doğrulama

- `tp_degree`, `config.num_attention_heads`'i tam olarak bölmelidir.
- Her paketlenmiş blok, yalnızca toplamları değil, *tek tek* `tp_degree` değerine bölünebilir olmalıdır.

Her ikisi de herhangi bir ağırlık yüklenmeden veya bölünmeden önce [from_pretrained()](/docs/diffusers/main/tr/api/models/overview#diffusers.ModelMixin.from_pretrained) ve `enable_parallelism()` tarafından doğrulanır.

Yeni bir planı gözle değil, sayısal olarak doğrulayın: sabit bir tohumla tek bir cihazda üretim yapın, ardından tensor paralelliği altında tekrar üretin ve çıktıları karşılaştırın. Yanlış yerleştirilmiş bir `"colwise"`/`"rowwise"` genellikle yine de çalışır ve makul görünen ancak yanlış bir görüntü üretir.

> [!TIP]
> Benzer bir mimariye sahip mevcut bir plandan başlayın. [QwenImageTransformer2DModel](/docs/diffusers/main/tr/api/models/qwenimage_transformer2d#diffusers.QwenImageTransformer2DModel) tamamen füzyon yapılmamış olup her giriş düz `"colwise"`/`"rowwise"` şeklindedir, [FluxTransformer2DModel](/docs/diffusers/main/tr/api/models/flux_transformer#diffusers.FluxTransformer2DModel) tek bir paketli satır içi projeksiyon ekler ve [Flux2Transformer2DModel](/docs/diffusers/main/tr/api/models/flux2_transformer#diffusers.Flux2Transformer2DModel) her iki paketli stili de kapsar — sırasıyla [`transformer_qwenimage.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_qwenimage.py), [`transformer_flux.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_flux.py) ve [`transformer_flux2.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_flux2.py) dosyalarına bakın.

## Strateji seçimi

Yukarıdaki stratejiler farklı sorunları çözer ve faydalı soru soyut olarak hangisinin en hızlı olduğu değil, neyin tükendiğidir.

| Strateji | Bölümler | Azaltır | Tek bir istem için gecikme | En iyi olduğu durum |
|---|---|---|---|---|
| [Accelerate](#accelerate) / [DDP](#pytorch-distributed) | kopyalar arasında istemler | hiçbiri — her cihaz tam bir kopya tutar | değişmedi | model zaten sığıyor ve birçok isteminiz var |
| [`device_map`](#devicemap) | cihazlar arası bileşenler | ağırlık belleği | biraz daha kötü | model sığmıyor ve bağlantı yavaş |
| [Bağlam paralelliği](#context-parallelism) | giriş dizisi | aktivasyon belleği | düşük | diziler uzun — yüksek çözünürlüklü veya video |
| [Tensor paralelliği](#tensor-parallelism) | ağırlık matrisleri | ağırlık belleği | düşük | bir bileşenin ağırlıkları sığmıyor ve bağlantı hızlı |

Bazı pratik yönergeler:

- **Çoklu isteklerde (prompts) yüksek verimlilik, model zaten sığabiliyor.** Veri paralelliği kullanın. Modeli değiştirmeden verimliliği doğrusal olarak ölçeklendiren tek strateji budur ve tek istek gecikmesini (latency) etkilemez.
- **Bir bileşenin ağırlıkları sığmıyor.** Hem belleği hem de gecikmeyi düşürdüğü için önce tensör paralelliğine yönelin. Her blok sınırında iletişim kurduğu için NVLink gibi hızlı bir bağlantı ister; PCIe üzerinde katman başına bu trafik, kazandırdığı hesaplama gücünü aşabilir ve bu durumda `device_map` daha iyi bir seçim olur. `device_map`, bileşenlerin tek başına uygun olmasına rağmen toplu olarak çok büyük olduğu durumu da ele alır.
- **Problem ağırlıklarda değil, aktivasyonlarda.** Bu, uzun dizi rejimidir — büyük görüntüler, çok sayıda kare — ve bağlam paralelliği doğrudan cevaptır. Yukarıdaki [Ulysses/Ring kıyaslamalarından](#ulysses-attention) bir arka uç seçin.
- **Hem ağırlıklar hem de dizi çok büyük.** Tensor ve bağlam paralelliğini birleştirin. [TensorParallelConfig](/docs/diffusers/main/tr/api/parallel#diffusers.TensorParallelConfig), her ikisinin de tek bir cihaz ızgarasını paylaşabilmesi için `mesh` argümanını kabul eder. Bu kombinasyon henüz doğrulanmadığından deneysel olarak ele alınmalıdır.

