الدرس 18 من 24

المحاذاة مع DPO

إعداد بيانات DPO

3 دقيقة للقراءة

DPO يتطلب بيانات تفضيل مع أزواج استجابة مختارة ومرفوضة. دعنا نتعلم كيفية هيكلة وإعداد هذه البيانات.

صيغة مجموعة بيانات DPO

الصيغة القياسية لـ DPO:

{
    "prompt": "ما هي عاصمة فرنسا؟",
    "chosen": "عاصمة فرنسا هي باريس. معروفة ببرج إيفل، متحف اللوفر، وتراثها الثقافي الغني.",
    "rejected": "مش عارف يمكن باريس أو شي"
}

تحميل مجموعات البيانات الموجودة

العديد من مجموعات بيانات التفضيل متاحة:

from datasets import load_dataset

# UltraFeedback - بيانات تفضيل عالية الجودة
dataset = load_dataset("HuggingFaceH4/ultrafeedback_binarized")

# Anthropic HH-RLHF
dataset = load_dataset("Anthropic/hh-rlhf")

# Intel Orca DPO Pairs
dataset = load_dataset("Intel/orca_dpo_pairs")

التنسيق لـ DPOTrainer

DPOTrainer من TRL يتوقع أسماء أعمدة محددة:

from datasets import load_dataset

dataset = load_dataset("HuggingFaceH4/ultrafeedback_binarized", split="train_prefs")

# تحقق من الأعمدة
print(dataset.column_names)
# يجب أن يكون: prompt، chosen، rejected

# أو تعيين للصيغة الصحيحة
def format_dpo(example):
    return {
        "prompt": example["question"],
        "chosen": example["response_a"],
        "rejected": example["response_b"]
    }

dataset = dataset.map(format_dpo)

استخدام قوالب المحادثة

لنماذج التعليمات، نسّق بقوالب المحادثة:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-3B-Instruct")

def format_with_template(example):
    # نسّق المحث
    prompt_messages = [
        {"role": "user", "content": example["prompt"]}
    ]

    # نسّق الاستجابة المختارة
    chosen_messages = [
        {"role": "user", "content": example["prompt"]},
        {"role": "assistant", "content": example["chosen"]}
    ]

    # نسّق الاستجابة المرفوضة
    rejected_messages = [
        {"role": "user", "content": example["prompt"]},
        {"role": "assistant", "content": example["rejected"]}
    ]

    return {
        "prompt": tokenizer.apply_chat_template(prompt_messages, tokenize=False),
        "chosen": tokenizer.apply_chat_template(chosen_messages, tokenize=False),
        "rejected": tokenizer.apply_chat_template(rejected_messages, tokenize=False)
    }

dataset = dataset.map(format_with_template)

إنشاء بيانات تفضيل مخصصة

من مخرجات النموذج

ولّد استجابات متعددة ورتّبها:

from transformers import pipeline
import random

generator = pipeline("text-generation", model="meta-llama/Llama-3.2-3B-Instruct")

def create_preference_pair(prompt):
    # ولّد استجابات متعددة
    responses = []
    for temp in [0.3, 0.7, 1.0, 1.2]:
        response = generator(prompt, temperature=temp, max_new_tokens=200)
        responses.append(response[0]["generated_text"])

    # اطلب من البشر أو LLM ترتيبها
    # ثم اختر الأفضل والأسوأ
    chosen = responses[0]  # الأفضل ترتيباً
    rejected = responses[-1]  # الأسوأ ترتيباً

    return {
        "prompt": prompt,
        "chosen": chosen,
        "rejected": rejected
    }

من المحادثات الموجودة

حوّل بيانات الملاحظات لأزواج تفضيل:

def convert_feedback_to_dpo(feedback_data):
    """
    المدخل: {"prompt": ..., "response": ..., "rating": 1-5}
    المخرج: أزواج DPO
    """
    # جمّع حسب المحث
    prompt_groups = {}
    for item in feedback_data:
        prompt = item["prompt"]
        if prompt not in prompt_groups:
            prompt_groups[prompt] = []
        prompt_groups[prompt].append(item)

    # أنشئ أزواج من تقييمات مختلفة
    dpo_data = []
    for prompt, responses in prompt_groups.items():
        sorted_responses = sorted(responses, key=lambda x: x["rating"], reverse=True)

        if len(sorted_responses) >= 2:
            dpo_data.append({
                "prompt": prompt,
                "chosen": sorted_responses[0]["response"],
                "rejected": sorted_responses[-1]["response"]
            })

    return dpo_data

إرشادات جودة البيانات

أزواج تفضيل جيدة

# فرق جودة واضح
{
    "prompt": "اشرح التمثيل الضوئي",
    "chosen": "التمثيل الضوئي هو العملية التي تحول بها النباتات ضوء الشمس والماء وثاني أكسيد الكربون إلى جلوكوز وأكسجين. يحدث في البلاستيدات الخضراء وهو ضروري للحياة على الأرض.",
    "rejected": "النباتات تصنع طعام من الشمس."
}

تجنّب

# متشابهة جداً (النموذج لا يستطيع التعلم)
{
    "prompt": "ما هو 2+2؟",
    "chosen": "2+2 يساوي 4.",
    "rejected": "الجواب هو 4."
}

# نفس الجودة، أسلوب مختلف (ليس تفضيل)
{
    "prompt": "أخبرني عن الكلاب",
    "chosen": "الكلاب رفقاء مخلصون...",
    "rejected": "الكلبيات تم تدجينها..."
}

التحقق

تحقق من مجموعة بياناتك قبل التدريب:

def validate_dpo_dataset(dataset):
    issues = []

    for i, example in enumerate(dataset):
        # تحقق من الحقول المطلوبة
        if not example.get("prompt"):
            issues.append(f"مثال {i}: محث مفقود")
        if not example.get("chosen"):
            issues.append(f"مثال {i}: مختار مفقود")
        if not example.get("rejected"):
            issues.append(f"مثال {i}: مرفوض مفقود")

        # تحقق أنهم مختلفون
        if example.get("chosen") == example.get("rejected"):
            issues.append(f"مثال {i}: المختار يساوي المرفوض")

        # تحقق من الأطوال
        if len(example.get("chosen", "")) < 10:
            issues.append(f"مثال {i}: المختار قصير جداً")

    print(f"وُجد {len(issues)} مشاكل")
    return issues

issues = validate_dpo_dataset(dataset)

توصيات حجم مجموعة البيانات

حجم مجموعة البيانات النتائج المتوقعة
100-500 تأثير ضئيل
500-2,000 تحسن ملحوظ
2,000-10,000 محاذاة جيدة
10,000+ محاذاة قوية

نصيحة: الجودة أهم من الكمية. 1,000 زوج منسّق بعناية يتفوق على 10,000 زوج مشوش.

بعد ذلك، لننفذ تدريب DPO مع DPOTrainer من TRL. :::

اختبار

الوحدة 5: المحاذاة مع DPO

خذ الاختبار