مشروع: نموذج مضبوط
درس 6 من 6
6مشروع: نموذج مضبوط
الصورة الكبيرةفي هذا المشروع تضبط نموذجًا صغيرًا على مهمة عربية واحدة، وتثبت برقم أنه تفوّق على أفضل ما يستطيعه القالب وحده، أو تكتشف أنه لم يتفوّق، وهذه نتيجة صحيحة أيضًا. المقارنة العادلة هي مع القالب مع الأمثلة، لا مع القالب الفقير.
ماذا تشاهد في الرسم: الخط الأساس هو العمود الأوسط، القالب مع ثمانية أمثلة، لا العمود الأول الضعيف. المضبوط تجاوزه بفاصل كله فوق الصفر، لكنه خسر عشر نقاط على المجموعة العامة، وهذا يدخل التقرير مع القرار.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
تختار مهمة عربية واحدة محددة، مثل تصنيف شكاوى العملاء، وتضبط نموذجًا صغيرًا عليها. ثم تثبت بالأرقام أنه صار أفضل من مجرد كتابة تعليمات جيدة لنموذج غير مضبوط. وإن لم يكن أفضل، فهذه أيضًا نتيجة مهمة تكتبها بصراحة.
بيانات JSONL منظفة ومقسمة، وضبط LoRA بإعدادات مسجّلة، ومقارنة على مجموعة اختبار ثابتة مقابل أفضل أمر prompt مع أمثلة قليلة. التقرير يذكر الدقة والتكلفة وزمن الاستدلال وفحص النسيان.
قبل أن تبدأ
📋بيانات الضبط
تعرف كيف تبني ملفًا نظيفًا بتقسيم مجمّع واختبار مجمّد.
📏هل تحسّن فعلًا
تعرف فاصل الثقة للفرق، ومجموعة النسيان العامة.
الفكرة الجوهرية
في هذا المشروع تضبط نموذجًا صغيرًا على مهمة عربية واحدة، وتثبت برقم هل تفوّق على أفضل ما يقدّمه القالب. تختار المهمة، ثم تبني البيانات، ثم تشغّل الضبط، ثم تملأ جدول النتائج.
1اختيار المهمة
اختر مهمة ضيّقة إجاباتها قابلة للتحقّق آليًّا، ولها حجم يبرّر الضبط أو تحتاج نموذجًا صغيرًا لسبب واضح.
انظر إلى الرسم: ثلاث مهام تصلح لمشروع أول، وواحدة تتجنّبها لأن تقييمها أصعب بكثير.
2البيانات والاختبار المجمّد
تحتاج 500 إلى 2000 مثال نظيف، بدليل تسمية مكتوب، وتقسيم مجمّع. والاختبار يُجمَّد قبل أي تدريب.
انظر إلى الرسم: كل خطوة من درس البيانات، بالترتيب، قبل أن تشغّل أي تدريب.
3تشغيل الضبط
التشغيل LoRA على نموذج صغير مفتوح، بإعدادات مسجّلة في اسم المجلّد، ونقطة التفتيش عند أدنى خسارة تحقّق.
يحتاج بطاقة رسوميات وpip install transformers peft trl datasets، فهو للقراءة هنا. نموذج بنصف مليار إلى 1.5 مليار معامل يكفي لمهمة تصنيف، ويتدرّب في أقل من ساعة على بطاقة متوسطة أو مجانية.
from datasets import load_dataset
from peft import LoraConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import SFTConfig, SFTTrainer
BASE = "Qwen/Qwen2.5-0.5B-Instruct"
tok = AutoTokenizer.from_pretrained(BASE)
model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype="bfloat16")
data = load_dataset("json", data_files={"train": "train.jsonl", "val": "val.jsonl"})
peft = LoraConfig(r=16, lora_alpha=32, lora_dropout=0.05, target_modules="all-linear", task_type="CAUSAL_LM")
args = SFTConfig(
output_dir="runs/r16-lr2e-4-ep2",
per_device_train_batch_size=4, gradient_accumulation_steps=8,
learning_rate=2e-4, num_train_epochs=2, bf16=True,
eval_strategy="steps", eval_steps=50, save_steps=50, load_best_model_at_end=True,
completion_only_loss=True, # learn the assistant's answer, not the user's message
)
trainer = SFTTrainer(model=model, args=args, train_dataset=data["train"], eval_dataset=data["val"],
processing_class=tok, peft_config=peft)
trainer.train()
trainer.save_model("runs/r16-lr2e-4-ep2/best")
completion_only_loss=True يجعل الخسارة على إجابة المساعد فقط، فلا يضيّع النموذج سعته في تعلّم إعادة كتابة رسائل المستخدمين. وبعد التدريب، شغّل النموذج المضبوط والأصلي بالقالبين على الاختبار المجمّد والمجموعة العامة، واملأ الجدول بنفس الكود في الأعلى.
4جدول النتائج
الجدول يقارن ثلاثة أنظمة على نفس الأسئلة المجمّدة: القالب وحده، والقالب مع الأمثلة، والمضبوط. المقارنة العادلة ضد القالب مع الأمثلة، لا ضد قالب ضعيف.
ثلاثة أنظمة على نفس 300 سؤال مجمّد، ومجموعة عامة من 100 سؤال لكشف النسيان. الأرقام هنا مولّدة للتوضيح، وفي مشروعك تأتي من تشغيل حقيقي لكل نظام.
import numpy as np
rng = np.random.default_rng(7)
# per-question results on a frozen set of 300 (1 = correct); in your project these come from real runs
task = {"prompt only": rng.random(300) < 0.62,
"prompt + 8 examples": rng.random(300) < 0.74,
"LoRA tuned": rng.random(300) < 0.86}
general = {"prompt only": rng.random(100) < 0.80, # 100 unrelated questions: forgetting check
"prompt + 8 examples": rng.random(100) < 0.80,
"LoRA tuned": rng.random(100) < 0.74}
def interval(a, b, n=2000):
idx = rng.integers(0, len(a), (n, len(a)))
d = b[idx].mean(1) - a[idx].mean(1)
return np.percentile(d, [2.5, 97.5])
base = task["prompt + 8 examples"]
print(f"{'system':22} {'task':>6} {'vs few-shot (95%)':>22} {'general':>8}")
for name in task:
if task[name] is base:
cell = "baseline"
else:
lo, hi = interval(base, task[name])
cell = f"[{lo:+.1%}, {hi:+.1%}]"
print(f"{name:22} {task[name].mean():6.1%} {cell:>22} {general[name].mean():8.0%}")
خط الأساس هو القالب مع ثمانية أمثلة، لا القالب الفقير. النموذج المضبوط تفوّق عليه بفاصل كامل فوق الصفر، فالتحسّن حقيقي. لكنه خسر 10 نقاط في المجموعة العامة. إن كان النموذج سيُستخدم لهذه المهمة وحدها، فهذا مقبول ويُكتب في التقرير. وإن كان سيُستخدم لغيرها، فأعد التشغيل بخلط أمثلة عامة.
انظر إلى الرسم: هذا العمود الثاني من الجدول، المجموعة العامة. المضبوط تفوّق في المهمة، لكنه خسر هنا. وكلا الرقمين يدخل التوصية.
كيف يعمل، خطوة بخطوة: كيف تُجري المشروع من المهمة إلى التوصية
تختار مهمة عربية ضيّقة إجاباتها قابلة للفحص آليًّا، وتجمّد مجموعة اختبار من 200 سؤال على الأقل.
تقيس القالب وحده والقالب مع أمثلة على الاختبار والمجموعة العامة قبل أي تدريب.
تدرّب LoRA على نموذج صغير بإعدادات مسجّلة، وتأخذ نقطة التفتيش عند أدنى خسارة تحقّق.
تملأ جدول الأنظمة الثلاثة مع الفاصل مقابل القالب مع الأمثلة، وتكتب التوصية بأرقامها.
مثال محلول
اختر مهمة ضيّقة بإجابات قابلة للتحقّق: تصنيف رسائل إلى فئات، أو استخراج حقول من نص إلى JSON، أو تحويل العامية إلى فصحى بمعايير واضحة. تجنّب المهام المفتوحة كالكتابة الإبداعية، لأن تقييمها أصعب بكثير لمشروع أول.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| إن تفوّق المضبوط على القالب وحده فالضبط يستحق. | المقارنة العادلة مع أفضل بديل رخيص، أي القالب مع الأمثلة. الفرق أمام قالب ضعيف يضخّم الفائدة. |
| مشروع لم يتفوّق فيه الضبط مشروع فاشل. | نتيجة «لا تضبط» بأرقام وأسباب نتيجة صحيحة، وتوفّر على الفريق صيانة نموذج لا يحتاجه. |
| الأفضل أن يتعلّم النموذج المحادثة كلها. | completion_only_loss=True يجعل الخسارة على إجابة المساعد فقط، فلا تُصرف القدرة على تعلّم كتابة رسائل المستخدمين. |
جرّب بنفسك
النموذج المضبوط أفضل من القالب وحده بـ25 نقطة، ومن القالب مع الأمثلة بنقطة واحدة والفاصل يشمل الصفر. ما التوصية؟
الـ25 نقطة مقابل خط أساس ضعيف. أمام البديل الحقيقي لا فرق مثبت، فالقرار يعود للتكلفة والزمن، ويُكتب بأرقامهما.
ماذا يفعل completion_only_loss=True؟
هدفك أن يتعلّم النموذج كيف يجيب، لا كيف يكتب أسئلة المستخدمين. الخسارة على الإجابة وحدها تركّز التعلّم حيث تريده.
بطاقات مراجعة سريعة
تحقق من فهمك
لماذا خط الأساس هو «القالب مع ثمانية أمثلة» لا «القالب وحده»؟
لأن السؤال الحقيقي هو هل يستحق الضبط تكلفته مقارنة بأفضل بديل رخيص، والأمثلة في القالب بديل رخيص يحسّن كثيرًا. مقارنة المضبوط بالقالب الفقير تضخّم فائدته وتقود لقرار خاطئ.
النموذج المضبوط خسر عشر نقاط على المجموعة العامة. متى تقبل ذلك ومتى لا؟
تقبله إن كان النموذج سيخدم هذه المهمة وحدها، وتكتبه في التقرير. وإن كان سيخدم مهام أخرى، تعيد التدريب مع خلط أمثلة عامة.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.