# استكشاف الأخطاء وإصلاحها

عادةً ما تنقسم مشاكل التدريب الموزّع إلى إحدى الفئات التالية: مشاكل رقمية، وأعطال في الاتصال، وأخطاء وقت التشغيل، وأخطاء البناء.

## كشف عن نقص التدفق وفائض التدفق

تحدث حالات النقص والزيادة في التدفق عندما تصل التفعيلات أو الأوزان إلى `inf` أو `nan`، أو عندما يصبح `loss=NaN`. لاكتشاف هذه الحالات، فعّل وحدة `DebugUnderflowOverflow` داخل `TrainingArguments.debug()`، أو استوردها وأضفها إلى حلقة التدريب الخاصة بك.

```py
from transformers import TrainingArguments

args = TrainingArguments(
    debug="underflow_overflow",
    ...
)
```

```py
from transformers.debug_utils import DebugUnderflowOverflow

debug_overflow = DebugUnderflowOverflow(model)
```

تقوم [DebugUnderflowOverflow](/docs/transformers/main/ar/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow) بإدخال خطافات (hooks) في النموذج لاختبار متغيرات الإدخال والإخراج والأوزان النموذجية المقابلة بعد كل استدعاء للأمام. عند اكتشاف `inf` أو `nan` في عنصر واحد على الأقل من التفعيلات أو الأوزان، يطبع الوحدة تقريرًا مشابهًا للذي أدناه.

المثال أدناه مخصص لتدريب الدقة المختلطة fp16 باستخدام [google/mt5-small](https://huggingface.co/google/mt5-small).

```shell
Detected inf/nan during batch_number=0
Last 21 forward frames:
abs min  abs max  metadata
                  encoder.block.1.layer.1.DenseReluDense.dropout Dropout
0.00e+00 2.57e+02 input[0]
0.00e+00 2.85e+02 output
[...]
                  encoder.block.2.layer.0 T5LayerSelfAttention
6.78e-04 3.15e+03 input[0]
2.65e-04 3.42e+03 output[0]
             None output[1]
2.25e-01 1.00e+04 output[2]
                  encoder.block.2.layer.1.layer_norm T5LayerNorm
8.69e-02 4.18e-01 weight
2.65e-04 3.42e+03 input[0]
1.79e-06 4.65e+00 output
                  encoder.block.2.layer.1.DenseReluDense.wi_0 Linear
2.17e-07 4.50e+00 weight
1.79e-06 4.65e+00 input[0]
2.68e-06 3.70e+01 output
                  encoder.block.2.layer.1.DenseReluDense.wi_1 Linear
8.08e-07 2.66e+01 weight
1.79e-06 4.65e+00 input[0]
1.27e-04 2.37e+02 output
                  encoder.block.2.layer.1.DenseReluDense.dropout Dropout
0.00e+00 8.76e+03 input[0]
0.00e+00 9.74e+03 output
                  encoder.block.2.layer.1.DenseReluDense.wo Linear
1.01e-06 6.44e+00 weight
0.00e+00 9.74e+03 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.DenseReluDense T5DenseGatedGeluDense
1.79e-06 4.65e+00 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.dropout Dropout
3.18e-04 6.27e+04 input[0]
0.00e+00      inf output
```

تُظهر السطر الأول رقم الدفعة التي حدث فيها الخطأ. في هذه الحالة، حدث الخطأ في الدفعة 0.

يصف كل إطار الوحدة التي يُبلّغ عنها. على سبيل المثال، يُبلّغ الإطار أدناه عن `encoder.block.2.layer.1.layer_norm`، وهو طبقة التطبيع في الطبقة الأولى من الكتلة الثانية في المُشفِّر. أما استدعاءات الأمامية فتتوجه إلى `T5LayerNorm`.

```shell
                  encoder.block.2.layer.1.layer_norm T5LayerNorm
8.69e-02 4.18e-01 weight
2.65e-04 3.42e+03 input[0]
1.79e-06 4.65e+00 output
```

تُبلّغ الإطار الأخير عن دالة `Dropout.forward`، التي تستدعي خاصية `dropout` داخل فئة `DenseReluDense`. حدثت حالة تجاوز النطاق (`inf`) في الكتلة الثانية من المُشفِّر عند الدفعة الأولى. وكان أكبر عنصر في المدخلات 6.27e+04.

```shell
                  encoder.block.2.layer.1.DenseReluDense T5DenseGatedGeluDense
1.79e-06 4.65e+00 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.dropout Dropout
3.18e-04 6.27e+04 input[0]
0.00e+00      inf output
```

بلغت قيم التفعيلات الناتجة عن `T5DenseGatedGeluDense.forward` حداً أقصى قدره 6.27e+04، وهو قريب من الحد الأقصى لنوع fp16 البالغ 6.4e+04. في الخطوة التالية، يعيد `Dropout` تطبيع الأوزان بعد تصفير بعض العناصر، مما يدفع الحد الأقصى إلى ما فوق 6.4e+04 ويسبب تجاوز السعة.

الآن وقد عرفت مكان حدوث الخطأ، تحقق من كود النمذجة في [modeling_t5.py](https://github.com/huggingface/transformers/blob/main/src/transformers/models/t5/modeling_t5.py).

```py
class T5DenseGatedGeluDense(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.wi_0 = nn.Linear(config.d_model, config.d_ff, bias=False)
        self.wi_1 = nn.Linear(config.d_model, config.d_ff, bias=False)
        self.wo = nn.Linear(config.d_ff, config.d_model, bias=False)
        self.dropout = nn.Dropout(config.dropout_rate)
        self.gelu_act = ACT2FN["gelu_new"]

    def forward(self, hidden_states):
        hidden_gelu = self.gelu_act(self.wi_0(hidden_states))
        hidden_linear = self.wi_1(hidden_states)
        hidden_states = hidden_gelu * hidden_linear
        hidden_states = self.dropout(hidden_states)
        hidden_states = self.wo(hidden_states)
        return hidden_states
```

إحدى الحلول هي التحويل إلى fp32 قبل بضع خطوات من أن تصبح القيم كبيرة جدًا، حتى لا تتجاوز الأرقام حدودها عند الضرب أو الجمع. خيار آخر هو تعطيل التدريب بدقة مختلطة (`amp`) مؤقتًا.

```py
import torch

def forward(self, hidden_states):
    device_type = hidden_states.device.type
    if torch.is_autocast_enabled(device_type):
        with torch.amp.autocast(device_type, enabled=False):
            return self._forward(hidden_states)
    else:
        return self._forward(hidden_states)
```

لا يغطي التقرير سوى المدخلات والمخرجات للإطارات الكاملة. لتحليل القيم الوسيطة داخل أي دالة `forward`، أضف `detect_overflow` بعد كل استدعاء للدالة الأمامية لتتبع `inf` أو `nan` في `forwarded_states`.

```py
from transformers.debug_utils import detect_overflow

class T5LayerFF(nn.Module):
    [...]

    def forward(self, hidden_states):
        forwarded_states = self.layer_norm(hidden_states)
        detect_overflow(forwarded_states, "after layer_norm")
        forwarded_states = self.DenseReluDense(forwarded_states)
        detect_overflow(forwarded_states, "after DenseReluDense")
        return hidden_states + self.dropout(forwarded_states)
```

اضبط عدد الإطارات التي يطبعها [DebugUnderflowOverflow](/docs/transformers/main/ar/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow).

```py
from transformers.debug_utils import DebugUnderflowOverflow

debug_overflow = DebugUnderflowOverflow(model, max_frames_to_save=100)
```

### تتبع الدفعة

يمكن لـ [DebugUnderflowOverflow](/docs/transformers/main/ar/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow) أيضًا تتبع الحد الأدنى المطلق والحد الأقصى المطلق للقيم في كل دفعة مع تعطيل كشف الانخفاض والتجاوز. يساعدك هذا في تحديد المكان الذي تبدأ فيه القيم بالانحراف في نموذجك.

يتتبع المثال أدناه الدفعات 1 و3 (تبدأ فهارس الدفعات من الصفر).

```py
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3])
```

```shell
                  *** Starting batch number=1 ***
abs min  abs max  metadata
                  shared Embedding
1.01e-06 7.92e+02 weight
0.00e+00 2.47e+04 input[0]
5.36e-05 7.92e+02 output
[...]
                  decoder.dropout Dropout
1.60e-07 2.27e+01 input[0]
0.00e+00 2.52e+01 output
                  decoder T5Stack
     not a tensor output
                  lm_head Linear
1.01e-06 7.92e+02 weight
0.00e+00 1.11e+00 input[0]
6.06e-02 8.39e+01 output
                   T5ForConditionalGeneration
     not a tensor output

                  *** Starting batch number=3 ***
abs min  abs max  metadata
                  shared Embedding
1.01e-06 7.92e+02 weight
0.00e+00 2.78e+04 input[0]
5.36e-05 7.92e+02 output
[...]
```

يقوم [DebugUnderflowOverflow](/docs/transformers/main/ar/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow) بإبلاغ عن العديد من الإطارات، مما يسهّل تحديد مكان انحراف القيم. إذا كنت تعرف أن المشكلة تقع حول الدفعة 150، فركّز التتبع على الدفعات 149 و150 لمقارنة المكان الذي تبدأ فيه الأرقام بالتباين.

يمكنك أيضًا إيقاف التتبع بعد رقم دفعة محدد، على سبيل المثال الدفعة 3.

```py
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3], abort_after_batch_num=3)
```

## التواصل

يتطلب التدريب الموزّع التواصل بين العمليات والعُقد، وهو مصدر شائع للأخطاء.

قم بتنزيل السكربت أدناه لتشخيص مشاكل الشبكة، ثم شغّله لاختبار تواصل وحدات معالجة الرسوميات. الأمر أدناه يختبر وحدتي معالجة رسوميات. عدّل `--nproc_per_node` و`--nnodes` بما يتناسب مع نظامك.

```bash
wget https://raw.githubusercontent.com/huggingface/transformers/main/scripts/distributed/torch-distributed-gpu-test.py
python -m torch.distributed.run --nproc_per_node 2 --nnodes 1 torch-distributed-gpu-test.py
```

يطبع السكربت `OK` إذا تواصلت بطاقتا GPU بنجاح وقامتا بتخصيص الذاكرة. راجع سكربت التشخيص لمزيد من التفاصيل وصفة لتشغيله في بيئة SLURM.

اضبط `NCCL_DEBUG=INFO` للحصول على مخرجات تفصيلية لتشخيص NCCL.

```bash
NCCL_DEBUG=INFO python -m torch.distributed.run --nproc_per_node 2 --nnodes 1 torch-distributed-gpu-test.py
```

## DeepSpeed

عند مواجهة خطأ، تحقق أولاً مما إذا كان DeepSpeed هو السبب. أعد المحاولة بإعدادك دون DeepSpeed، وإذا استمر الخطأ، فبلّغ عن المشكلة. أما للمشكلات غير المتعلقة بدمج Transformers، فافتح مشكلة في مستودع DeepSpeed [repository](https://github.com/microsoft/DeepSpeed).

بالنسبة إلى المشكلات المتعلقة بتكامل Transformers، يرجى تضمين المعلومات التالية.

* ملف إعدادات DeepSpeed الكامل.
* معاملات سطر الأوامر الخاصة بـ [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) أو [TrainingArguments](/docs/transformers/main/ar/main_classes/trainer#transformers.TrainingArguments) إذا كنت تقوم بكتابة سكربت إعداد [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) بنفسك (لا تُدرج كامل [TrainingArguments](/docs/transformers/main/ar/main_classes/trainer#transformers.TrainingArguments) الذي يحتوي على العديد من العناصر غير ذات الصلة).
* مخرجات هذه الأوامر.

    ```bash
    python -c 'import torch; print(f"torch: {torch.__version__}")'
    python -c 'import transformers; print(f"transformers: {transformers.__version__}")'
    python -c 'import deepspeed; print(f"deepspeed: {deepspeed.__version__}")'
    ```

* رابط إلى دفتر ملاحظات Google Colab لإعادة إنتاج المشكلة.
* مجموعة بيانات قياسية أو غير مخصصة، أو مثال موجود لإعادة إنتاج المشكلة.

### تم إنهاء العملية عند بدء التشغيل

إذا تم إنهاء عملية DeepSpeed أثناء الإقلاع دون ظهور أثر تتبع (traceback)، فقد حاول البرنامج تخصيص ذاكرة وحدة معالجة مركزية (CPU) أكبر من المتاح أو المسموح به. يقوم نواة نظام التشغيل بإنهاء العملية في كلتا الحالتين.

تحقق مما إذا كان ملف الإعداد الخاص بك يحتوي على `offload_optimizer` أو `offload_param` أو كلاهما مُهيأً لتفريغ الأحمال إلى وحدة المعالجة المركزية (CPU).

إذا كان لديك NVMe و ZeRO-3 مُعدّين، فحاول إجراء التفريغ إلى NVMe بدلاً من ذلك. [قدّر](https://deepspeed.readthedocs.io/en/latest/memory.html) متطلبات ذاوية النموذج الخاص بك أولاً.

### فقدان NaN

غالبًا ما يحدث فقدان بقيمة NaN عندما يكون النموذج مُدرَّبًا مسبقًا باستخدام bf16 ثم يُستخدم مع fp16 (وهو أمر شائع بشكل خاص مع النماذج المُدرَّبة على TPU). استخدم fp32 أو bf16 إذا كان جهازك يدعم ذلك (وحدات TPU، أو معالجات رسومية من نوع Ampere أو أحدث).

يمكن أن يتسبب fp16 أيضًا في تجاوز الحد الأقصى. إذا كان ملف الإعداد الخاص بك يبدو مثل الذي أدناه، فقد تظهر أخطاء تجاوز في السجلات.

```json
{
    "fp16": {
        "enabled": "auto",
        "loss_scale": 0,
        "loss_scale_window": 1000,
        "initial_scale_power": 16,
        "hysteresis": 2,
        "min_loss_scale": 1
    }
}
```

يعني خطأ `OVERFLOW!` أدناه أن مُقيِّم خسارة DeepSpeed لم يستطع إيجاد معامل تحجيم للتغلب على تجاوز الخسارة. جرّب قيمة أعلى لـ`initial_scale_power` (عادةً ما تعمل القيمة 32).

```bash
0%|                                                                                                                             | 0/189 [00:00<?, ?it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 262144, reducing to 262144
  1%|▌                                                                                                                    | 1/189 [00:00<01:26,  2.17it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 262144, reducing to 131072.0
  1%|█▏
 [...]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 14%|████████████████▌                                                                                                   | 27/189 [00:14<01:13,  2.21it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 15%|█████████████████▏                                                                                                  | 28/189 [00:14<01:13,  2.18it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 15%|█████████████████▊                                                                                                  | 29/189 [00:15<01:13,  2.18it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
[...]
```

## DeepSpeed CUDA

يقوم DeepSpeed بتجميع أكواد CUDA C++، وهو مصدر شائع لأخطاء البناء في امتدادات PyTorch التي تتطلب CUDA. وتعتمد هذه الأخطاء على طريقة تثبيت CUDA على نظامك.

```bash
pip install deepspeed
```

> [!TIP]
> بالنسبة لأي مشاكل تثبيت أخرى، [افتح مشكلة](https://github.com/microsoft/DeepSpeed/issues) مع فريق DeepSpeed.

### أدوات غير متطابقة

يأتي PyTorch مع مجموعة أدوات CUDA الخاصة به، لكن DeepSpeed يتطلب تثبيت إصدار CUDA مطابق على مستوى النظام. إذا قمت بتثبيت PyTorch باستخدام `cudatoolkit==10.2` في بيئة Python الخاصة بك، فستحتاج أيضاً إلى تثبيت CUDA 10.2 في كل مكان.

تختلف المساحة الدقيقة حسب النظام، لكن `/usr/local/cuda-10.2` هو المسار الأكثر شيوعًا على أنظمة Unix. بمجرد إعداد CUDA وإضافته إلى `PATH` الخاص بك، ابحث عن موقع التثبيت باستخدام هذا الأمر.

```bash
which nvcc
```

### عدة مجموعات أدوات

قد يكون لديك أكثر من مجموعة أدوات CUDA مثبتة على نظامك.

```text
/usr/local/cuda-10.2
/usr/local/cuda-11.0
```

عادةً ما تُعيّن مُثبِّتات الحزم المسارات إلى آخر إصدار مُثبَّت. إذا فشلت عملية البناء بسبب عدم العثور على إصدار CUDA الصحيح، فقم بإعداد `PATH` و`LD_LIBRARY_PATH` لتشير إلى المسار الصحيح.

تحقق من متغيرات البيئة هذه أولاً.

```bash
echo $PATH
echo $LD_LIBRARY_PATH
```

`PATH` يحدد مواقع الملفات القابلة للتنفيذ. `LD_LIBRARY_PATH` يحدد مواقع المكتبات المشتركة. تُمنح الأولوية للقيم المسجلة في البداية، ويفصل `:` بين القيم المتعددة. أضف مسار CUDA الصحيح في بداية القائمة لضمان أولويته.

```bash
# adjust the version and full path if needed
export PATH=/usr/local/cuda-10.2/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-10.2/lib64:$LD_LIBRARY_PATH
```

تحقق أيضًا من وجود المجلدات المُسندة. يحتوي المجلد الفرعي `lib64` على كائنات CUDA بصيغة `.so` مثل `libcudart.so`. تحقق من أسماء الملفات الفعلية وحدّث الإعدادات وفقًا لذلك.

### الإصدارات الأقدم

تتطلب إصدارات CUDA الأقدم أحيانًا إصدارات أقدم من المُجمِّع. على سبيل المثال، إذا كانت CUDA تتطلب `gcc-7` بينما يحتوي نظامك على `gcc-9` فقط، فإن عملية البناء ستفشل. قم بتثبيت المُجمِّع الأقدم المطلوب وإنشاء رابط رمزي حتى يتمكن نظام بناء CUDA من العثور عليه.

```bash
# adjust the path to your system
sudo ln -s /usr/bin/gcc-7  /usr/local/cuda-10.2/bin/gcc
sudo ln -s /usr/bin/g++-7  /usr/local/cuda-10.2/bin/g++
```

### البناء المسبق

إذا كنت لا تزال تواجه صعوبة في تثبيت DeepSpeed أو بناؤه وقت التشغيل، فقم ببناء وحدات DeepSpeed مسبقًا. نفّذ الأوامر أدناه للبناء محليًا.

```bash
git clone https://github.com/deepspeedai/DeepSpeed/
cd DeepSpeed
rm -rf build
TORCH_CUDA_ARCH_LIST="8.6" DS_BUILD_CPU_ADAM=1 DS_BUILD_UTILS=1 pip install . \
--global-option="build_ext" --global-option="-j8" --no-cache -v \
--disable-pip-version-check 2>&1 | tee build.log
```

> [!TIP]
> أضف `DS_BUILD_AIO=1` إلى أمر البناء لاستخدام NVMe offload. تأكد من تثبيت حزمة libaio-dev على مستوى النظام.

في الخطوة التالية، اضبط معمارية وحدة معالجة الرسوميات (GPU) الخاصة بك في `TORCH_CUDA_ARCH_LIST`. يمكنك العثور على قائمة كاملة بوحدات معالجة الرسوميات من NVIDIA ومعمارياتها في [صفحة CUDA GPUs](https://developer.nvidia.com/cuda-gpus). للتحقق من إصدار PyTorch الذي يتوافق مع معماريتك، نفّذ الأمر أدناه.

```bash
python -c "import torch; print(torch.cuda.get_arch_list())"
```

اعثر على بنية المعالج الرسومي باستخدام الأمر التالي.

```bash
CUDA_VISIBLE_DEVICES=0 python -c "import torch; print(torch.cuda.get_device_capability())"
```

نفّذ الأمر التالي للعثور على بنية GPU `0`. يُظهر المخرجات قيم `major` و`minor` التي تُشكّل معًا بنية GPU. يُظهر المثال أدناه البنية `8.6`.

```bash
CUDA_VISIBLE_DEVICES=0 python -c "import torch; \
print(torch.cuda.get_device_properties(torch.device('cuda')))
"_CudaDeviceProperties(name='GeForce RTX 3090', major=8, minor=6, total_memory=24268MB, multi_processor_count=82)"
```

لنتيجة `8, 6`، اضبط `TORCH_CUDA_ARCH_LIST="8.6"`. لعدة وحدات معالجة رسومية ذات معماريات مختلفة، اذكرها على النحو التالي `TORCH_CUDA_ARCH_LIST="6.1;8.6"`.

يمكنك حذف `TORCH_CUDA_ARCH_LIST` والسماح لبرنامج البناء باكتشاف بنية المعالج الرسومي تلقائيًا، لكن قد لا تتطابق مع المعالج الرسومي الفعلي على الجهاز المستهدف. إن تحديد البنية بشكل صريح يكون أكثر موثوقية.

لتدريب النموذج على عدة أجهزة ذات نفس الإعداد، قم ببناء عجلة ثنائية (binary wheel).

```bash
git clone https://github.com/deepspeedai/DeepSpeed/
cd DeepSpeed
rm -rf build
TORCH_CUDA_ARCH_LIST="8.6" DS_BUILD_CPU_ADAM=1 DS_BUILD_UTILS=1 \
python setup.py build_ext -j8 bdist_wheel
```

يولّد هذا الأمر عجلة ثنائية (binary wheel) على غرار `dist/deepspeed-0.3.13+8cd046f-cp38-cp38-linux_x86_64.whl`. ثبّتها محلياً أو على جهاز آخر.

```bash
pip install deepspeed-0.3.13+8cd046f-cp38-cp38-linux_x86_64.whl
```

