Transformers documentation
استكشاف الأخطاء وإصلاحها
استكشاف الأخطاء وإصلاحها
عادةً ما تنقسم مشاكل التدريب الموزّع إلى إحدى الفئات التالية: مشاكل رقمية، وأعطال في الاتصال، وأخطاء وقت التشغيل، وأخطاء البناء.
كشف عن نقص التدفق وفائض التدفق
تحدث حالات النقص والزيادة في التدفق عندما تصل التفعيلات أو الأوزان إلى inf أو nan، أو عندما يصبح loss=NaN. لاكتشاف هذه الحالات، فعّل وحدة DebugUnderflowOverflow داخل TrainingArguments.debug()، أو استوردها وأضفها إلى حلقة التدريب الخاصة بك.
from transformers import TrainingArguments
args = TrainingArguments(
debug="underflow_overflow",
...
)from transformers.debug_utils import DebugUnderflowOverflow
debug_overflow = DebugUnderflowOverflow(model)تقوم DebugUnderflowOverflow بإدخال خطافات (hooks) في النموذج لاختبار متغيرات الإدخال والإخراج والأوزان النموذجية المقابلة بعد كل استدعاء للأمام. عند اكتشاف inf أو nan في عنصر واحد على الأقل من التفعيلات أو الأوزان، يطبع الوحدة تقريرًا مشابهًا للذي أدناه.
المثال أدناه مخصص لتدريب الدقة المختلطة fp16 باستخدام google/mt5-small.
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.
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.
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.
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) مؤقتًا.
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.
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.
from transformers.debug_utils import DebugUnderflowOverflow
debug_overflow = DebugUnderflowOverflow(model, max_frames_to_save=100)تتبع الدفعة
يمكن لـ DebugUnderflowOverflow أيضًا تتبع الحد الأدنى المطلق والحد الأقصى المطلق للقيم في كل دفعة مع تعطيل كشف الانخفاض والتجاوز. يساعدك هذا في تحديد المكان الذي تبدأ فيه القيم بالانحراف في نموذجك.
يتتبع المثال أدناه الدفعات 1 و3 (تبدأ فهارس الدفعات من الصفر).
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3]) *** 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 بإبلاغ عن العديد من الإطارات، مما يسهّل تحديد مكان انحراف القيم. إذا كنت تعرف أن المشكلة تقع حول الدفعة 150، فركّز التتبع على الدفعات 149 و150 لمقارنة المكان الذي تبدأ فيه الأرقام بالتباين.
يمكنك أيضًا إيقاف التتبع بعد رقم دفعة محدد، على سبيل المثال الدفعة 3.
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3], abort_after_batch_num=3)التواصل
يتطلب التدريب الموزّع التواصل بين العمليات والعُقد، وهو مصدر شائع للأخطاء.
قم بتنزيل السكربت أدناه لتشخيص مشاكل الشبكة، ثم شغّله لاختبار تواصل وحدات معالجة الرسوميات. الأمر أدناه يختبر وحدتي معالجة رسوميات. عدّل --nproc_per_node و--nnodes بما يتناسب مع نظامك.
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.
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.
بالنسبة إلى المشكلات المتعلقة بتكامل Transformers، يرجى تضمين المعلومات التالية.
ملف إعدادات DeepSpeed الكامل.
حجج سطر الأوامر الخاصة بـ Trainer أو TrainingArguments إذا كنت تقوم بكتابة سكربت لإعداد Trainer بنفسك (لا تُدرج كل محتوى TrainingArguments الذي يحتوي على العديد من العناصر غير ذات الصلة).
مخرجات هذه الأوامر.
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 بدلاً من ذلك. قدّر متطلبات ذاوية النموذج الخاص بك أولاً.
فقدان NaN
غالبًا ما يحدث فقدان بقيمة NaN عندما يكون النموذج مُدرَّبًا مسبقًا باستخدام bf16 ثم يُستخدم مع fp16 (وهو أمر شائع بشكل خاص مع النماذج المُدرَّبة على TPU). استخدم fp32 أو bf16 إذا كان جهازك يدعم ذلك (وحدات TPU، أو معالجات رسومية من نوع Ampere أو أحدث).
يمكن أن يتسبب fp16 أيضًا في تجاوز الحد الأقصى. إذا كان ملف الإعداد الخاص بك يبدو مثل الذي أدناه، فقد تظهر أخطاء تجاوز في السجلات.
{
"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).
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 على نظامك.
pip install deepspeed
بالنسبة لأي مشاكل تثبيت أخرى، افتح مشكلة مع فريق DeepSpeed.
أدوات غير متطابقة
يأتي PyTorch مع مجموعة أدوات CUDA الخاصة به، لكن DeepSpeed يتطلب تثبيت إصدار CUDA مطابق على مستوى النظام. إذا قمت بتثبيت PyTorch باستخدام cudatoolkit==10.2 في بيئة Python الخاصة بك، فستحتاج أيضاً إلى تثبيت CUDA 10.2 في كل مكان.
تختلف المساحة الدقيقة حسب النظام، لكن /usr/local/cuda-10.2 هو المسار الأكثر شيوعًا على أنظمة Unix. بمجرد إعداد CUDA وإضافته إلى PATH الخاص بك، ابحث عن موقع التثبيت باستخدام هذا الأمر.
which nvccعدة مجموعات أدوات
قد يكون لديك أكثر من مجموعة أدوات CUDA مثبتة على نظامك.
/usr/local/cuda-10.2 /usr/local/cuda-11.0
عادةً ما تُعيّن مُثبِّتات الحزم المسارات إلى آخر إصدار مُثبَّت. إذا فشل البناء بسبب عدم العثور على إصدار CUDA الصحيح، فقم بإعداد PATH وLD_LIBRARY_PATH لتشير إلى المسار الصحيح.
تحقق من متغيرات البيئة هذه أولاً.
echo $PATH
echo $LD_LIBRARY_PATHPATH يحدد مواقع الملفات القابلة للتنفيذ. LD_LIBRARY_PATH يحدد مواقع المكتبات المشتركة. تُمنح الأولوية للقيم المسجلة في البداية، ويفصل : بين القيم المتعددة. أضف مسار CUDA الصحيح في بداية القائمة لضمان أولويته.
# 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 من العثور عليه.
# 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 مسبقًا. نفّذ الأوامر أدناه للبناء المحلي.
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أضف
DS_BUILD_AIO=1إلى أمر البناء لاستخدام NVMe offload. تأكد من تثبيت حزمة libaio-dev على مستوى النظام.
في الخطوة التالية، اضبط معمارية وحدة معالجة الرسوميات (GPU) الخاصة بك في TORCH_CUDA_ARCH_LIST. يمكنك العثور على قائمة كاملة بوحدات معالجة الرسوميات من NVIDIA ومعمارياتها في صفحة CUDA GPUs. للتحقق من إصدار PyTorch الذي يتوافق مع معماريتك، نفّذ الأمر أدناه.
python -c "import torch; print(torch.cuda.get_arch_list())"اعثر على بنية المعالج الرسومي باستخدام الأمر التالي.
CUDA_VISIBLE_DEVICES=0 python -c "import torch; print(torch.cuda.get_device_capability())"نفّذ الأمر التالي للعثور على بنية GPU 0. يُظهر المخرجات قيم major وminor التي تُشكّل معًا بنية GPU. يُظهر المثال أدناه البنية 8.6.
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).
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. ثبّتها محلياً أو على جهاز آخر.
pip install deepspeed-0.3.13+8cd046f-cp38-cp38-linux_x86_64.whl