انتقل إلى المحتوى الرئيسي

الوحدة 9 — مكتبة Transformers عمليًا

استعملنا هذه المكتبة في الوحدات الأخيرة دون تعمّق. الآن نجمع كلّ ما رأيناه في صورة واحدة، ونضيف ما يلزم لاستعمالها في مسار إنتاج: الفروق بين pipeline وAutoTokenizer وAutoModel، والتدفّق الكامل من الـHub إلى الاستدلال، وكيف يُختار نموذج من بين عشرات النماذج المتوفّرة لكلّ مهمّة، وكيف تُقاس الكلفة الحقيقيّة قبل التبنّي.

طبقات المكتبة الثلاث

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

الطبقة الأولى: pipeline. واجهة سطر واحد لمهامّ شائعة. تُخفي كلّ شيء: تحميل النموذج، تحميل المقطّع، معالجة قبليّة، استدلال، فكّ ترميز. مثالية للاستكشاف والنماذج الأوّلية.

from transformers import pipeline

qanat = pipeline("sentiment-analysis", model="CAMeL-Lab/bert-base-arabic-camelbert-mix-sentiment")
print(qanat("المنتج ممتاز وأنصح به"))
# [{'label': 'positive', 'score': 0.994}]

الحدّ: pipeline بطيء نسبيًّا لأنّه يعالج مثالًا واحدًا في كلّ استدعاء، ولا يستغلّ الدفعات (batching) تلقائيًّا. في الإنتاج، نعطيه دفعات صريحة أو ننزل طبقةً.

الطبقة الثانية: AutoTokenizer وAutoModel. التحكّم الكامل في المقطّع والنموذج بشكل صريح، مع بقاء التجريد على مستوى المكتبة (لا نحتاج معرفة إن كان النموذج BERT أو RoBERTa أو ELECTRA).

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch

ism = "CAMeL-Lab/bert-base-arabic-camelbert-mix-sentiment"
tok = AutoTokenizer.from_pretrained(ism)
model = AutoModelForSequenceClassification.from_pretrained(ism)

nusus = ["ممتاز جدا", "مخيّب للأمل", "عادي، لا شيء يذكر"]
mad = tok(nusus, return_tensors="pt", padding=True, truncation=True, max_length=128)

with torch.no_grad():
out = model(**mad)
ihtimalat = out.logits.softmax(dim=-1)
prints_tanabbu = ihtimalat.argmax(dim=-1)

هذه الطبقة هي الاختيار الافتراضي في مسار إنتاج جادّ. نتحكّم في حجم الدفعة، والدقّة (float16 أو bfloat16)، والجهاز، ونستطيع تسخين النموذج مرّة والاحتفاظ به في الذاكرة.

الطبقة الثالثة: PreTrainedModel المباشر. الطبقة المخصّصة (BertModel, RobertaModel)، للتحكّم الدقيق في المعمارية وإضافة طبقات مخصّصة. تستعمل حين نحتاج بنية غير قياسية (رأس تصنيف مخصّص، سلسلة نماذج، فقدان مركّب).

الـHub وترتيب البحث

مركز Hugging Face يحوي عشرات الآلاف من النماذج. البحث الكفء يوفّر ساعات.

  • حسب المهمّة: Text Classification, Token Classification, Question Answering, Summarization. كلّ مهمّة تعرض نماذج مُهيّأة لها بواجهة AutoModelFor... المناسبة.
  • حسب اللغة: فلتر Languages: Arabic. يعطي عادة بين 500 و1000 نموذج.
  • حسب المكتبة: PyTorch أو TensorFlow أو JAX، حسب البيئة.
  • حسب الترخيص: apache-2.0, mit, cc-by-4.0, أو تراخيص أشدّ (gemma-terms, llama2). الترخيص يحدّد إن كان الاستعمال التجاري مسموحًا.
  • حسب الحجم: عمود Downloads مقاس لثقة المجتمع؛ تاريخ التحديث الأخير مقاس للنشاط.

للعربية، أسرع طريق هو البدء بـaubmindlab/* (AraBERT وأخواته من AUB)، CAMeL-Lab/* (نماذج جامعية أميركية، مقسّمة حسب الفصحى والعامّية)، UBC-NLP/* (نماذج جامعة UBC). هذه العائلات موثّقة جيّدًا وترخيصها مرن.

قراءة بطاقة النموذج بجدّية

بطاقة النموذج (README.md في مستودعه) يجب أن تُقرأ قبل الاستعمال. العناصر الحرجة:

  • بيانات التدريب: أيّ جسم؟ حجمه؟ لغته؟ نموذج مدرَّب على العربية الفصحى فقط سيؤدّي رديئًا على الدارجة المغربية.
  • مهمّة التدريب: نمذجة لغويّة بإخفاء؟ ضبط دقيق على مشاعر؟ نموذج مُضبَط على المشاعر بالإنجليزية لن يعمل على العربية.
  • المقاييس المعلنة: على أيّ مجموعة اختبار؟ الأرقام الجميلة على مجموعة داخلية غير خاضعة للتحقّق مشكوك فيها.
  • القيود الموثّقة: تحيّزات معروفة، ضعف على لهجات معيّنة، حدود الطول.
  • الترخيص: للاستعمال التجاري، تأكّد. النماذج الجديدة أحيانًا تحمل تراخيص مقيَّدة.

Trainer مقابل حلقة تدريب مخصّصة

Trainer من Transformers يُدير كلّ ما يلزم للتدريب: توزيع، دفعات، تسجيل، إيقاف مبكّر، حفظ. للحالات القياسية، لا داعي لكتابة حلقة تدريب.

from transformers import TrainingArguments, Trainer

args = TrainingArguments(
output_dir="./out",
num_train_epochs=3,
per_device_train_batch_size=16,
per_device_eval_batch_size=32,
learning_rate=2e-5,
warmup_ratio=0.1,
weight_decay=0.01,
logging_steps=50,
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
fp16=True, # نصف الدقة، أسرع على GPU حديث
gradient_accumulation_steps=2, # لدفعة فعلية 32 على GPU بذاكرة أقل
)

mudarrib = Trainer(
model=model,
args=args,
train_dataset=ds_tr,
eval_dataset=ds_val,
tokenizer=tok,
compute_metrics=maqayis,
)
mudarrib.train()
mudarrib.save_model("./nihai")
tok.save_pretrained("./nihai") # دائمًا مع النموذج!

متى نغادر Trainer؟ حين نحتاج حلقة تدريب غير قياسية (تعلّم متعدّد المهامّ، تدريب معاكس، حلقة تقييم مخصّصة تعتمد على استدلال طويل). في هذه الحالات، torch.nn.Module مباشر مع حلقة PyTorch كلاسيكية أوضح من محاولة تحوير Trainer.

الاستدلال الفعّال في الإنتاج

pipeline وTrainer مصمَّمان للتطوير، لا للإنتاج بمعدل مرتفع. للنشر الفعلي، خيارات أفضل:

  • الدفعات الصريحة: تجميع الطلبات القادمة في نافذة قصيرة (بضع ملّي‌ثوانٍ) في دفعة واحدة. يزيد الإنتاجية 5 إلى 10 أضعاف.
  • الدقّة النصف (float16 أو bfloat16): يخفّض الذاكرة إلى النصف ويسرّع الاستدلال، مع فقدان دقّة يكاد لا يُلحَظ.
  • التقطير (distillation): تدريب نموذج أصغر (DistilBERT، MiniLM) على تقليد نموذج كبير. يعطي غالبًا 95 بالمئة من الجودة بنصف الحجم وضِعف السرعة.
  • التكميم (quantization): تحويل الأوزان إلى int8. يخفّض الحجم إلى الربع، مع فقدان دقّة محدود على معظم المهامّ.
  • خوادم متخصّصة: Text Generation Inference (TGI) من Hugging Face، vLLM، Triton Inference Server. مصمَّمة لتحميل النموذج مرّة واحدة والخدمة عبر HTTP بأداء عالٍ.

اختيار النموذج: الأبعاد الأربعة

في كلّ مهمّة، الاختيار بين نموذج وآخر يوزّع على أربعة أبعاد:

  • الجودة: F1 على مجموعة اختبار تمثّل بياناتك، لا على مجموعة عامّة.
  • الحجم: عدد المعاملات (100 مليون، 500 مليون، ملياران) يحدّد الذاكرة والسرعة.
  • الكلفة: زمن استدلال ملّي‌ثانوي × عدد الطلبات × سعر GPU-ساعة. احسبها قبل النشر.
  • الترخيص: يحدّد إن كنت تستطيع النشر التجاري، وإن كنت مضطرًّا إلى الإفصاح عن استعمالك.

قد يكون النموذج الأكبر أفضل بنقطة F1، لكنّه يكلّف عشرة أضعاف. للتصنيف الجماعي على مليوني مراجعة يوميًّا، الفارق قد يعني عشرات آلاف الدولارات شهريًّا. للنماذج الأوّلية والاستكشاف، الأكبر دائمًا أسهل. للإنتاج، الأصغر الذي يفي بالحاجة هو الأصحّ.

احفظ المقطّع مع أوزان النموذج دائمًا

mudarrib.save_model("./nihai") يحفظ الأوزان فقط. عليك أن تُتبعه بـtok.save_pretrained("./nihai") صراحة، وإلّا حمَلت في الإنتاج المقطّع الافتراضي بدل مقطّع التدريب، فتأتي الجُذاذات مختلفة صمتًا. الأداء ينهار دون أيّ رسالة خطأ. راجع هذا في نصّ نشرك، فهو الخطأ الأشيع.

قِس زمن الاستدلال قبل التبنّي
import time
n = 100
nusus_test = ["مراجعة عربية نموذجية"] * n

# تسخين
_ = qanat(nusus_test[:4])

# قياس
t0 = time.time()
_ = qanat(nusus_test)
zaman = (time.time() - t0) / n
print(f"{zaman * 1000:.1f} مللي ثانية لكل مثال")

هذا القياس على GPU-ك ولا على «مواصفات النموذج المعلنة». الفرق بين نموذجين بالحجم نفسه قد يبلغ ضعفين تبعًا للمعمارية.

في الخلاصة

  • مكتبة Transformers مبنيّة بطبقات من pipeline (تجريد كامل) إلى AutoModel (تحكّم صريح مع تجريد المعمارية) إلى الفئات المتخصّصة؛ لكلّ طبقة استعمالها المناسب.
  • الـHub يُبحث حسب المهمّة واللغة والترخيص، وبطاقة النموذج تُقرأ بجدّية قبل الاستعمال: بيانات التدريب، والمقاييس، والقيود، والترخيص عناصر حاسمة.
  • Trainer كافٍ للحالات القياسية؛ نغادره فقط لحلقات تدريب غير قياسية، ونحفظ المقطّع مع أوزان النموذج دائمًا وإلّا يفشل النشر صمتًا.
  • الاستدلال في الإنتاج يعتمد على الدفعات الصريحة، والدقّة النصف، والتقطير، والتكميم، وخوادم متخصّصة؛ الاختيار الأمثل بين النماذج يوازن الجودة والحجم والكلفة والترخيص.

الوحدة التالية: المشروع الكامل، حيث نجمع كلّ ما رأيناه في مصنّف مستندات بالعربية، ونقارن رسميًّا بين المقاربات الثلاث على المهمّة نفسها.