# التوازي الخبير

[التوازي الخبير](https://huggingface.co/spaces/nanotron/ultrascale-playbook?section=expert_parallelism) هو استراتيجية توازي مخصصة لنماذج [مزيج الخبراء (MoE)](https://huggingface.co/blog/moe). توجد طبقة التغذية الأمامية لكل خبير على مُسرِّع عتاد مختلف. يقوم الموجِّه بتوجيه الرموز إلى الخبراء المناسبين وجمع النتائج. يتيح هذا النهج توسيع النماذج إلى أعداد أكبر بكثير من المعاملات دون زيادة تكلفة الحساب، لأن كل رمز يُفعِّل عدداً قليلاً فقط من الخبراء.

## DistributedConfig

فعّل توازي الخبراء باستخدام فئة [DistributedConfig](/docs/transformers/main/ar/expert_parallelism#transformers.DistributedConfig) والحجة `ep_size`. تعتمد معظم نماذج MoE افتراضيًا على [token dispatch](#token-dispatch)، لذا يمكن تعيين `ep_size` بشكل مستقل عن `tp_size`. لا تزال بعض النماذج، مثل Llama 4 و Gemma 4، تعتمد افتراضيًا على الأقنعة و all-reduce (`"ep_router"` و `"moe_tp_experts"` في `model.ep_plan`). كما يتوفر استخدام الأقنعة على أي نموذج مع تجاوز `ep_plan`، ويتطلب ذلك `ep_size=tp_size` حتى يتلقى كل رتبة في مجموعة الخبراء الرموز نفسها.

```py
import os

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.distributed.configuration_utils import DistributedConfig

distributed_config = DistributedConfig(
    tp_size=int(os.environ["WORLD_SIZE"]),
    ep_size=int(os.environ["WORLD_SIZE"]),
)

model = AutoModelForCausalLM.from_pretrained(
    "openai/gpt-oss-120b",
    distributed_config=distributed_config,
)
```

يحدد كل نموذج MoE خطتين في ملفه الإعدادي: `base_model_tp_plan` للوحدات الكثيفة و`base_model_ep_plan` للخبراء. وتُعرض هاتان الخطتان على النموذج المحمّل على شكل `model.tp_plan` و`model.ep_plan`. عند استخدام `tp_size > 1` و`ep_size > 1`، تُطبَّق الخطتان معًا: حيث يقوم خط [التوازي على مستوى المصفوفات](./perf_infer_gpu_multi) بتقسيم وحدات الانتباه ووحدات MLP الكثيفة، بينما يقوم خط التوازي على مستوى الخبراء بتقسيم الخبراء. ولأن قواعد EP لها الأولوية على قواعد TP بالنسبة لنفس الوحدات، فإن أوزان الخبراء تُقسَّم مرة واحدة فقط، وذلك وفق خط EP. وفي خط EP، يقوم نمط `GroupedGemmParallel` بتقسيم أوزان الخبراء على طول بُعد الخبراء بحيث يحمّل كل rank الخبراء المحليين لديه فقط، بينما يقوم `ep_router` بإخفاء الخبراء الموجودة على ranks أخرى قبل أن يجمع all-reduce مخرجات الخبراء.

يُطبَّق `tp_plan` فقط عندما يكون `tp_size > 1`، و`ep_plan` فقط عندما يكون `ep_size > 1`. عند تفعيل TP وتعطيل EP، يُطبَّق خطة TP الكاملة، بما في ذلك قواعد الخبراء.

تختص قاعدة الأمامي الخبير في `ep_plan` بكيفية وصول الرموز إلى الخبراء:

| قاعدة | آلية | تخطيط |
| :--- | :--- | :--- |
| `"moe_tp_experts"` مع `"ep_router"` على الموجّه | التخفي والجمع الشامل: يقوم كل رتبة بتشغيل خبرائها المحليين على الدفعة الكاملة، ويقوم الموجّه بتخفي الآخرين، ثم يجمع الجمع الشامل المخرجات | `ep_size=tp_size` |
| `"ep_dispatch_experts"` | [توزيع الرموز](#token-dispatch): يحتفظ كل رتبة برموزها الخاصة ولا يتبادل إلا أزواج (الرمز، الخبير) الموجَّهة عبر مجموعتين من عمليات all-to-all | `ep_size` مضاعف لـ `tp_size` يقسم `fsdp_size * tp_size` |

> [!TIP]
> استخدام `enable_expert_parallel=True` دون تحديد `ep_size` أصبح غير مدعوم، ويُصدر تحذيرًا من نوع `FutureWarning`. يقوم هذا الإعداد بتقسيم الخبراء فقط، تمامًا كما في `ep_size=tp_size, tp_plan={}`.

شغّل سكربت الاستدلال الخاص بك باستخدام [torchrun](https://pytorch.org/docs/stable/elastic/run.html). يجب أن يساوي عدد العمليات `tp_size * fsdp_size * pp_size`، وأن يقسم `ep_size` عدد الخبراء قسمة تامة.

```zsh
torchrun --nproc-per-node 8 your_script.py
```

### التجاوز على الخطط

مرّر `tp_plan={...}` أو `ep_plan={...}` إلى [DistributedConfig](/docs/transformers/main/ar/expert_parallelism#transformers.DistributedConfig) لتجاوز قواعد محددة في الخطط المحددة مسبقًا. تُحفظ القواعد غير المحددة، وتُخزَّن الخطط المدمجة على النموذج. يجب أن يطابق كل مفتاح وحدةً أو معاملًا أو عنصرًا موجودًا في الخطة؛ وإلا فسيُطلق التحميل `ValueError` قبل تقسيم أي شيء. استخدم المسار الكامل كما يظهر من النموذج المحمّل، لذا `model.layers.*` لنموذج LM السببي و`layers.*` لنموذه الأساسي.

```py
distributed_config = DistributedConfig(
    tp_size=4,
    ep_size=4,
    tp_plan={"model.layers.*.self_attn.q_proj": "colwise_rep"},
    ep_plan={"model.layers.*.mlp.experts.down_proj": "grouped_gemm"},
)
```

تقديم خطة لا يستنتج أحجام التوازي: اضبط `tp_size` و`ep_size` بشكل صريح.

تعتمد معظم نماذج MoE افتراضيًا على `"ep_dispatch_experts"`. لاستخدام الأقنعة (masking) و all-reduce بدلاً من ذلك، اضبط `ep_size=tp_size` وأعد كتابة كلٍّ من قواعد التوجيه (router) وقواعد الأمامية (forward) للخبراء (يعتمد اسم وحدة التوجيه على النموذج، مثل `mlp.router` في gpt-oss):

```py
distributed_config = DistributedConfig(
    tp_size=4,
    ep_size=4,
    ep_plan={
        "model.layers.*.mlp.gate": "ep_router",
        "model.layers.*.mlp.experts": "moe_tp_experts",
    },
)
```

وعلى العكس من ذلك، تجاوز قاعدة التقدّم الأمامي للخبراء في نموذج يستخدم الخطة التي تعتمد على الأقنعة، وذلك باستخدام `"ep_dispatch_experts"` لتوزيع الرموز. في هذه الحالة، تُتجاهل قاعدة الموجّه، لأن التوزيع يحتاج إلى معرفات الخبراء العالمية لتحديد مالك كل خبير.

## توزيع الرموز

With token dispatch, each rank trains on its own part of the batch. At every MoE layer, a rank routes its tokens, sends each (token, expert) pair to the rank that owns the expert with an all-to-all, runs its local experts on what it receives, gets the results back with a second all-to-all and combines them with the routing weights. Only the routed activations and expert outputs travel, and no rank computes experts for tokens it does not own.

```py
from transformers import AutoModelForCausalLM
from transformers.distributed import DistributedConfig

distributed_config = DistributedConfig(
    tp_size=1,
    fsdp_size=8,
    ep_size=4,
)
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-30B-A3B", distributed_config=distributed_config)
```

عند `tp_size=1`، يجب أن يكون `ep_size` قاسماً لـ `fsdp_size` وعدد الخبراء، ولا يكون الانتباه مقيداً بـ `num_key_value_heads`. أما بالنسبة لباقي النموذج:

- تُقسَّم المعاملات خارج الخبراء باستخدام [FSDP2](./fsdp) عبر `fsdp`، مما يقلل من تدرجاتها.
- تُقسَّم الخبراء عبر `ep`، وعندما يكون `efsdp_size = fsdp_size * tp_size // ep_size` أكبر من واحد، تُقسَّم أيضًا عبر `efsdp` باستخدام تقسيم FSDP. وهي دائمًا مُغلَّفة بـ FSDP، لذا فإن `fsdp_mixed_precision` و`fsdp_cpu_offload` تنطبق عليها أيضًا، و[save_pretrained()](/docs/transformers/main/ar/main_classes/model#transformers.PreTrainedModel.save_pretrained) تجمعها مثل أي معامل آخر.
- يحتوي مجموعة التوازي الخبير على `ep_size / tp_size` دفعات، لذا فإن تدرج الخبير هو مجموع على عدد الدفعات هذا. تقسم عملية الاختزال في `efsdp` على `fsdp_size` بدلاً من حجم المجموعة، مما يعطي متوسط FSDP2 لكل دفعة الذي تتبناه الوحدات الكثيفة.
- يعالج كل خبير محلي أيضًا صفًا واحدًا من الحشو الصفري في كل طبقة. أما المرتبة التي لم تتلقِ خبراؤها أي رموز، فإنها تشارك لا تزال في عملية all-to-all العكسية وتقليل تدرجات الخبراء.

### كيف يتم دمج الأحجام

- `tp_size * fsdp_size` هو عدد العمليات. `ep_size` لا يضيف شيئًا: فهو يعيد تجميع نفس الرتب لأوزان الخبراء فقط.
- يقسم `ep_size` قائمة الخبراء إلى `ep_size` كتل. يحسب كل رتبة `num_experts / ep_size` من الخبراء، وتحتفظ `ep_size` رتب متتالية بمجموعة كاملة واحدة. تلك المجموعة من الرتب هي المجموعة التي يعمل فيها all-to-all.
- `efsdp_size = fsdp_size * tp_size / ep_size` هو عدد النسخ الكاملة الموجودة من مجموعة الخبراء. تقوم الرتب الموجودة في نفس الموقع في النسخ المختلفة بتجزئة هؤلاء الخبراء من أجل الذاكرة، ثم تُوسط تدرجاتهم، تماماً كما تفعل FSDP مع الوحدات الكثيفة.
- يعتمد الدفعة التي يحتفظ بها كل رتبة على `fsdp` فقط. تتكوّن الرتب المتتالية من مجموعة TP وتحصل على نفس الدفعة؛ بينما تحصل مجموعات `fsdp_size` على دفعات مختلفة.

ثماني عمليات، `tp_size=2, fsdp_size=4`، ثمانية خبراء:

```text
rank         0      1       2      3       4      5       6      7
batch       [====B0====]   [====B1====]   [====B2====]   [====B3====]     one batch per TP pair
tp           0      1       0      1       0      1       0      1

ep_size=2   E0-3   E4-7    E0-3   E4-7    E0-3   E4-7    E0-3   E4-7     group = a TP pair,   efsdp_size=4
ep_size=4   E0E1   E2E3    E4E5   E6E7    E0E1   E2E3    E4E5   E6E7     group = two pairs,   efsdp_size=2
ep_size=8   E0     E1      E2     E3      E4     E5      E6     E7       group = all ranks,   efsdp_size=1
```

ينجم عن الصورة عددان:

- داخل مجموعة EP، يوجد كل رمز `tp_size` مرات، مرة واحدة لكل رتبة من الزوج الذي يحمل دفعته. ولا يعتمد هذا على `ep_size`.
- يحتوي مجموعة EP على `ep_size / tp_size` دفعات مختلفة. وهذا هو العدد الذي تُجمَّع عليه تدرجات خبير معين.

### مع توازي الموترات

اضبط `tp_size > 1` لتقسيم الوحدات الكثيفة وفق خطة TP بينما تستخدم الخبراء الإرسال. على ثمانية عمليات:

```py
distributed_config = DistributedConfig(
    tp_size=2,
    fsdp_size=4,
    ep_size=4,
)
```

تستقبل كل زوج من رتب TP نفس الدفعة، لأن توازي الموترات يكرر التفعيلات داخل الزوج. إذا أرسلت الرتبتان جميع رموزهما، فإن الرتبة التي تمتلك خبيرًا ما ستستقبل كل رمز مرتين، وتحسبه مرتين، مما يؤدي إلى مضاعفة تدرج أوزانها. نظرًا لأن الخبراء مكتملون على رتبة واحدة، لا يمكن تقسيم النسخة المكررة عبر الأوزان، وغالبًا ما تكون الرتبة المالكة رتبة أخرى، فلا يمكن حل المشكلة عبر الملكية كما تفعل عملية الإخفاء (masking). لذلك، يقسم الزوج الصفوف: حيث ترسل كل رتبة TP جزءًا منفصلًا من الرموز بحجم `1 / tp_size`، وتعود النتائج إلى الرتبة التي أرسلتها، ثم تعيد عملية all-reduce على الزوج، بعد ملء النصفين الصفرين، المخرجات المكررة التي تتوقعها الطبقة التالية. يتم التقسيم بناءً على `tp_size` وليس `ep_size`، لأن الرتب التي تحتفظ بالدفعة فقط هي التي يمكنها إرسالها. تمتد مجموعات الخبراء عبر أربع رتب، وكل خبير مقسّم عبر FSDP على `efsdp_size = 2` رتبة، بينما تمتد مجموعة FSDP للجذع عبر أربع رتب. لا تزال قيود TP المعتادة للنموذج، مثل قابلية تقسيم رؤوس الانتباه، سارية على الوحدات الكثيفة. قد تكون شرائح الرموز غير متساوية أو فارغة، بما في ذلك أثناء فك ترميز رمز واحد.

لا يمكن دمج توزيع الرموز (Token dispatch) مع توازي الخطوط الأنابيبية (pipeline parallelism) بعد؛ استخدم `pp_size=1` (لم يتم الاختبار بعد). تستخدم كل من هذه الإعدادات ثمانية وحدات معالجة رسومية (GPU):

| الإعداد | النتيجة |
| :--- | :--- |
| `DistributedConfig(tp_size=4, fsdp_size=2, ep_size=4)` | التوزيع عبر مجموعات TP من أربعة، حيث تقطع كل مجموعة دفعتها إلى أربعة (الخطة الافتراضية)؛ ما لم تحدد ep_plan لاستخدام EP المقنّع القديم |
| `DistributedConfig(tp_size=1, fsdp_size=8, ep_size=4)` | Dispatch مع دفعة مستقلة على كل رتبة، دون تقطيع، وخبراء مُقسَّمين عبر أزواج من الرتب باستخدام FSDP. |
| `DistributedConfig(tp_size=2, fsdp_size=4, ep_size=4)` | أرسل باستخدام زوج TP لكل دفعة، حيث يقسم كل زوج دفعته إلى نصفين؛ دفعتان لكل مجموعة خبراء. |
| `DistributedConfig(tp_size=8, ep_size=8)` | أرسل مع مشاركة كل رتبة دفعة واحدة، مقسّمة إلى ثمانية، أو استخدم الإخفاء (masking) وجميع الاختزال (all-reduce) لخطة مخفية. |

> [!WARNING]
> لا يأخذ [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) بعد في الحسبان إرسال الرموز: فعدّ الدفعات والرموز يفترض تخطيط all-reduce، حيث تتشارك رتب مجموعة TP دفعة واحدة وتوجد `fsdp_size` شظايا موازية للبيانات. تأتي دعم Trainer للإرسال في متابعة لاحقة.

## الدمج مع FSDP2

يقوم توازي الموترات وتوازي الخبراء بتقسيم الأوزان عبر `tp`، لكن حالة المُحسِّن (optimizer) والوحدات التي لا تخضع لقاعدة ما تبقى مكررة على كل رتبة ضمن المجموعة، مما يحد من حجم النموذج الذي يمكن تدريبه. أضف [FSDP2](./fsdp) على بُعد شبكة ثانٍ باستخدام `fsdp_size`. مع استخدام القناع (masking) و all-reduce، حافظ على `ep_size=tp_size` ومرر `ep_plan={"layers.*.mlp.gate": "ep_router", "layers.*.mlp.experts": "moe_tp_experts"}``.

```py
from transformers import AutoModelForCausalLM
from transformers.distributed import DistributedConfig

distributed_config = DistributedConfig(
    tp_size=4,
    ep_size=4,  # expert parallel size, must match tp_size with masking and all-reduce
    fsdp_size=2,  # data parallel shards
    ep_plan={
        "model.layers.*.mlp.gate": "ep_router",
        "model.layers.*.mlp.experts": "moe_tp_experts",
    },
)
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-30B-A3B", distributed_config=distributed_config)
```

حمّل النموذج كما هو معتاد، ثم درّبه باستخدام [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer). يقوم هذا بإيجاد معيار التدرج عبر كلا الشبكتين ومنح كل شبكة مجموعة معلمات خاصة بها في المُحسِّن. تجمع [save_model()](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer.save_model) الأوزان المجزأة في نقطة تحقق عادية. يتطلب هذا `accelerate>=1.12` حتى يتمكن `Trainer` من عكس `tp_size` و`fsdp_size` إلى `~Accelerate.ParallelismConfig`.

يقارن الجدول أدناه التدريب باستخدام EP فقط مع التدريب ثنائي الأبعاد EP+FSDP2 على 8 وحدات GPU من نوع H100. يتكون عبء العمل من الضبط الكامل لنموذج Qwen3-30B-A3 بدقة bf16 عند طول تسلسل يبلغ 2048. تقلل شظايا FSDP الإضافية من ذروة الذاكرة، وتنخفض سرعة المعالجة (رموز/ثانية) قليلاً لأن FSDP2 يقوم بجمع (all-gathers) وتشتيت (reduce-scatters) الخبراء عبر `fsdp`.

| الإعداد | رموز/ثانية/وحدة معالجة رسومية | ذروة الذاكرة/وحدة معالجة الرسوميات |
|---|---|---|
| `tp_size=8` | 3485 | 38.6 GB |
| `tp_size=4, fsdp_size=2` | 2900 | 34.2 GB |
| `tp_size=2, fsdp_size=4` | 2830 | 32.3 GB |

> [!WARNING]
> لا يُدعَم استئناف التدريب من نقطة تحقق بعد للنماذج التي تُقسَّم عند التحميل، لذا يقبل [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) فقط `save_only_model=True` أو `save_strategy="no"` لها.

## مرجع الواجهة البرمجية[[transformers.DistributedConfig]]

#### transformers.DistributedConfig[[transformers.DistributedConfig]]

```python
transformers.DistributedConfig(tp_size: int | None = None, tp_plan: typing.Union[dict[str, str], typing.Literal['auto'], NoneType] = None, enable_sequence_parallel: bool = False, enable_expert_parallel: bool = False, fsdp_size: int | None = None, fsdp_plan: dict[str, str] | None = None, fsdp_cpu_offload: bool = False, fsdp_mixed_precision: bool = False, pp_size: int | None = None, ep_size: int | None = None, ep_plan: dict[str, str] | None = None)
```

[المصدر](https://github.com/huggingface/transformers/blob/main/src/transformers/distributed/configuration_utils.py#L26)

**المعاملات:**

tp_size (`int`, *اختياري*) : عدد الأجهزة للتوازي على مستوى الموترات. إذا كانت `None` وتم تعيين `tp_plan`، فإن القيمة الافتراضية هي `WORLD_SIZE // (other_parallel_size)`. إذا كانت `None` ولم يتم تعيين `tp_plan`، فإن القيمة الافتراضية هي 1.

tp_plan (`dict[str, str]` أو `"auto"`، *اختياري*) : خطة تقسيم التوازي على مستوى الموترات. مرّر `"auto"`، أو اتركه `None` عند ضبط `tp_size`، لاستخدام `base_model_tp_plan` المُعرّف مسبقًا للنموذج. مرّر قاموسًا لتجاوز قواعد محددة في تلك الخطة؛ وتُحفظ القواعد غير المحددة. تطبيق قاموس فارغ يعني عدم تطبيق أي قاعدة لتوازي الموترات.

enable_sequence_parallel (`bool`، *اختياري*، القيمة الافتراضية `False`) : محجوز للتوازي التسلسلي. لم يتم ربطه بعد.

enable_expert_parallel (`bool`، *اختياري*، القيمة الافتراضية `False`) : تم إيقاف استخدامه، وأُزيل في الإصدار v5.21. في غياب `ep_size`، يكون الأمر مطابقًا لـ `ep_size=tp_size, tp_plan={}`: خطة التوازي بين الخبراء وحدها عبر `tp_size` من الرتب. القيمة الصريحة لـ `ep_size` لها الأولوية. لا يغيّر هذا العلم قيمة `tp_size` أو `fsdp_size`.

fsdp_size (`int`، *اختياري*) : عدد الأجهزة الخاصة بـ FSDP (التوازي في البيانات). إذا كانت `None` وتم تعيين `tp_size`، فإن القيمة الافتراضية هي 1.

fsdp_plan (`dict[str, str]`، *اختياري*) : خطة تجزئة FSDP2. اتركها `None` لاستخدام `base_model_fsdp_plan` المُعرَّفة مسبقًا في النموذج و`_fsdp_plan` على مستوى الفئة. مرِّر قاموسًا لتجاوز قواعد فردية؛ وتُحفظ القواعد غير المحددة. تُطبَّق فقط عندما يكون `fsdp_size > 1`.

fsdp_cpu_offload (`bool`، *اختياري*، القيمة الافتراضية `False`) : ما إذا كان سيتم تفعيل تفريغ المعالجة إلى وحدة المعالجة المركزية (CPU) لـ FSDP2.

fsdp_mixed_precision (`bool`، *اختياري*، القيمة الافتراضية `False`) : ما إذا كان سيتم تفعيل الدقة المختلطة لـ FSDP2.

pp_size (`int`، *اختياري*) : عدد الأجهزة للتوازي في خط الأنابيب. إذا كانت `None` وتم ضبط وضع توازٍ آخر، فإن القيمة الافتراضية هي 1.

ep_size (`int`، *اختياري*) : عدد الأجهزة التي تمتلك شظايا خبراء مميزة. القيمة الافتراضية هي 1. اضبطه صراحةً لتفعيل EP. يجب أن يكون مضاعفاً لـ`tp_size` وأن يقسم `fsdp_size * tp_size`. تتطلب خطط تقليل الخبراء الكلّية `ep_size=tp_size`؛ كما يسمح توزيع الرموز (`"ep_dispatch_experts"`) أيضاً بـ`ep_size > tp_size`.

ep_plan (`dict[str, str]`، *اختياري*) : خطة تجزئة التوازي بين الخبراء. اتركه `None` لاستخدام `base_model_ep_plan` المُعرّف مسبقًا في النموذج. مرّر قاموسًا لتجاوز قواعد محددة في تلك الخطة؛ وتُحفظ القواعد غير المحددة. يُطبَّق هذا الإعداد فقط عندما يكون `ep_size > 1`، وتتمتع قواعده بأولوية على قواعد `tp_plan` الخاصة بنفس الوحدات. تختار قاعدة `"ep_dispatch_experts"` إرسال الرموز من الكل إلى الكل بدلاً من قناع الموجّه والجمع الشامل.

إعدادات الاستدلال والتدريب الموزعين الأصليين باستخدام التوازي على مستوى الموترات أو خط الأنابيب أو FSDP2.

