المحاذاة مع DPO
دمج SFT + DPO
3 دقيقة للقراءة
النهج الأكثر فعالية للضبط الدقيق يجمع SFT (لتعليم المهارات) مع DPO (لتحسين الجودة). لنبني خط أنابيب كامل من مرحلتين.
خط الأنابيب من مرحلتين
المرحلة 1: SFT
├── المدخل: نموذج أساسي + مجموعة بيانات تعليمات
├── المخرج: نموذج يستطيع اتباع التعليمات
└── الهدف: تعليم مهارات خاصة بالمهمة
المرحلة 2: DPO
├── المدخل: نموذج SFT + مجموعة بيانات تفضيل
├── المخرج: نموذج مُحاذى باستجابات أفضل
└── الهدف: تحسين جودة الاستجابة
سكريبت خط الأنابيب الكامل
from unsloth import FastLanguageModel
from trl import SFTTrainer, DPOTrainer, DPOConfig
from transformers import TrainingArguments
from datasets import load_dataset
import torch
# ============================================
# التكوين
# ============================================
model_name = "unsloth/Llama-3.2-3B-Instruct"
max_seq_length = 2048
# ============================================
# المرحلة 1: الضبط الدقيق المُشرف عليه
# ============================================
print("=" * 50)
print("المرحلة 1: تدريب SFT")
print("=" * 50)
# حمّل النموذج لـ SFT
model, tokenizer = FastLanguageModel.from_pretrained(
model_name=model_name,
max_seq_length=max_seq_length,
load_in_4bit=True,
)
model = FastLanguageModel.get_peft_model(
model,
r=16,
lora_alpha=16,
lora_dropout=0,
target_modules="all-linear",
use_gradient_checkpointing="unsloth",
)
# حمّل مجموعة بيانات SFT
sft_dataset = load_dataset("tatsu-lab/alpaca", split="train")
sft_dataset = sft_dataset.select(range(5000)) # مجموعة فرعية
def format_sft(example):
if example.get("input", ""):
text = f"""### التعليمة:
{example['instruction']}
### المدخل:
{example['input']}
### الاستجابة:
{example['output']}<|eot_id|>"""
else:
text = f"""### التعليمة:
{example['instruction']}
### الاستجابة:
{example['output']}<|eot_id|>"""
return {"text": text}
sft_dataset = sft_dataset.map(format_sft)
# تدريب SFT
sft_args = TrainingArguments(
output_dir="./outputs/stage1-sft",
num_train_epochs=1,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
warmup_ratio=0.03,
logging_steps=20,
save_steps=500,
bf16=True,
optim="adamw_8bit",
)
sft_trainer = SFTTrainer(
model=model,
args=sft_args,
train_dataset=sft_dataset,
processing_class=tokenizer,
max_seq_length=max_seq_length,
dataset_text_field="text",
)
sft_trainer.train()
sft_trainer.save_model("./outputs/stage1-sft/final")
print("اكتمل تدريب SFT!")
# ============================================
# المرحلة 2: محاذاة DPO
# ============================================
print("=" * 50)
print("المرحلة 2: تدريب DPO")
print("=" * 50)
# أعد تحميل نموذج SFT لـ DPO
# (التحميل الجديد يضمن التعامل الصحيح مع النموذج المرجعي)
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="./outputs/stage1-sft/final",
max_seq_length=max_seq_length,
load_in_4bit=True,
)
model = FastLanguageModel.get_peft_model(
model,
r=16,
lora_alpha=16,
lora_dropout=0,
target_modules="all-linear",
use_gradient_checkpointing="unsloth",
)
# حمّل مجموعة بيانات DPO
dpo_dataset = load_dataset("HuggingFaceH4/ultrafeedback_binarized", split="train_prefs")
dpo_dataset = dpo_dataset.select(range(2000)) # مجموعة فرعية
# تدريب DPO
dpo_config = DPOConfig(
output_dir="./outputs/stage2-dpo",
beta=0.1,
learning_rate=5e-6,
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
num_train_epochs=1,
warmup_ratio=0.1,
logging_steps=20,
save_steps=500,
bf16=True,
optim="adamw_8bit",
max_length=1024,
max_prompt_length=512,
)
dpo_trainer = DPOTrainer(
model=model,
args=dpo_config,
train_dataset=dpo_dataset,
processing_class=tokenizer,
)
dpo_trainer.train()
dpo_trainer.save_model("./outputs/stage2-dpo/final")
print("اكتمل تدريب DPO!")
print("اكتمل خط الأنابيب الكامل!")
أفضل الممارسات
المرحلة 1: SFT
# معدل تعلم أعلى لتعلم مهارات جديدة
learning_rate = 2e-4
# epochs أكثر إذا تعليم مهام معقدة
num_train_epochs = 1-3
# التركيز على اتباع التعليمات
dataset = "أزواج التعليمة-الاستجابة"
المرحلة 2: DPO
# معدل تعلم أقل بكثير (تنقيح دقيق)
learning_rate = 5e-6
# عادة 1 epoch يكفي
num_train_epochs = 1
# التركيز على تحسين الجودة
dataset = "أزواج التفضيل (مختار/مرفوض)"
بديل: التدريب بمرحلة واحدة
بعض الأساليب الحديثة تجمع SFT و DPO:
# ORPO (Odds Ratio Preference Optimization)
from trl import ORPOTrainer, ORPOConfig
orpo_config = ORPOConfig(
output_dir="./outputs/orpo",
beta=0.1,
learning_rate=8e-6,
per_device_train_batch_size=2,
num_train_epochs=1,
)
# ORPO يستخدم بيانات التفضيل لكن يتضمن خسارة SFT
trainer = ORPOTrainer(
model=model,
args=orpo_config,
train_dataset=preference_dataset,
processing_class=tokenizer,
)
مقارنة الأساليب
| الأسلوب | المراحل | البيانات المطلوبة | التعقيد |
|---|---|---|---|
| SFT فقط | 1 | أزواج التعليمات | منخفض |
| SFT + DPO | 2 | كلا النوعين | متوسط |
| ORPO | 1 | أزواج التفضيل | منخفض |
| SFT + RLHF | 3+ | كل الأنواع + نموذج مكافأة | عالي |
متى نستخدم كل مرحلة
SFT أولاً عندما:
- تعليم معرفة مجال جديدة
- النموذج لا يفهم مهمتك
- تحتاج صيغ إخراج محددة
- التدريب من نموذج أساسي
DPO بعدها عندما:
- نموذج SFT يعمل لكن الجودة متفاوتة
- تريد تقليل الاستجابات السيئة
- تحتاج اتباع تعليمات أفضل
- المحاذاة مع تفضيلات محددة
التقييم بين المراحل
تحقق من الجودة بعد كل مرحلة:
def evaluate_model(model, tokenizer, test_prompts):
"""تقييم سريع لاستجابات النموذج."""
model.eval()
results = []
for prompt in test_prompts:
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.7)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
results.append({"prompt": prompt, "response": response})
return results
# محثات الاختبار
test_prompts = [
"اشرح التعلم الآلي بمصطلحات بسيطة.",
"اكتب قصيدة قصيرة عن البرمجة.",
"ما هي فوائد الرياضة؟"
]
# قيّم بعد SFT
sft_results = evaluate_model(sft_model, tokenizer, test_prompts)
# قيّم بعد DPO
dpo_results = evaluate_model(dpo_model, tokenizer, test_prompts)
# قارن الاستجابات
for sft, dpo in zip(sft_results, dpo_results):
print(f"المحث: {sft['prompt']}")
print(f"SFT: {sft['response'][:200]}...")
print(f"DPO: {dpo['response'][:200]}...")
print("-" * 50)
نصيحة: خط أنابيب SFT+DPO هو المعيار الذهبي لـ 2025. SFT يعلّم القدرات، DPO ينقّح الجودة. قيّم دائماً بين المراحل للتأكد أن كلاً منها يحسّن النموذج.
في الوحدة التالية، سنتعلم كيفية تقييم ونشر نموذجك المضبوط. :::