الفصل 1: مقدمة في PEFT
جعل التخصيص الدقيق متاحًا
التخصيص الدقيق الفعال من حيث المعلمات (PEFT) هو مجموعة من التقنيات التي تمكن من تخصيص النماذج اللغوية الكبيرة باستخدام جزء بسيط من الموارد المطلوبة للتخصيص الكامل.
المشكلة التي يحلها PEFT
تحديات التخصيص الكامل
- نموذج 7B: يحتاج إلى ~60-80GB VRAM للتخصيص الكامل
- نموذج 13B: يحتاج إلى ~110-160GB VRAM
- نموذج 70B: يحتاج إلى ~600GB+ VRAM
هذا يمنع معظم الممارسين من تخصيص النماذج الأكبر.
حل PEFT
الفكرة الرئيسية
معظم معرفة النموذج موجودة في الأوزان المدربة مسبقًا. نحن بحاجة فقط إلى إجراء تعديلات صغيرة للمهام الجديدة.
كيف يعمل
بدلاً من تحديث جميع المعلمات:
# التخصيص الكامل (جميع المعلمات)
for param in model.parameters():
param.requires_grad = True # تحديث كل شيء
# PEFT (فقط المحولات)
for name, param in model.named_parameters():
param.requires_grad = False # تجميد النموذج الأساسي
# إضافة محول صغير قابل للتدريب
adapter = LoRAAdapter(rank=16) # ~0.1% من المعلمات
فوائد PEFT
1. تقليل الذاكرة بشكل كبير
| النموذج | التخصيص الكامل | PEFT | التوفير |
|---|---|---|---|
| 7B | ~80GB | ~6-14GB | 82-92% |
| 13B | ~160GB | ~10-26GB | 84-94% |
| 70B | ~600GB | ~48-140GB | 77-92% |
2. تدريب أسرع
التخصيص الكامل: 100% من المعلمات محدثة في كل خطوة
PEFT: أقل من 1% من المعلمات محدثة في كل خطوة
→ حساب تدرج أسرع
→ تحديث أوزان أسرع
→ عرض نطاق ذاكرة أقل
3. لا يوجد نسيان كارثي
# النموذج الأساسي مجمد = المعرفة الأصلية محفوظة
base_weights = model.get_base_weights() # لم يتم تعديلها أبدًا
adapter_weights = model.get_adapter_weights() # فقط هذه تتغير
4. محولات متعددة
# نموذج أساسي واحد، محولات متعددة لمهام مختلفة
model.load_adapter("customer-support") # المهمة 1
model.load_adapter("code-generation") # المهمة 2
model.load_adapter("summarization") # المهمة 3
# فقط قم بتبديل المحولات أثناء التشغيل!
المفاهيم الأساسية
المعلمات المجمدة
المعلمات التي لا تتلقى تحديثات تدرج أثناء التدريب.
# تجميد معلمة
param.requires_grad = False
# تحقق من المعلمات القابلة للتدريب
for name, param in model.named_parameters():
if param.requires_grad:
print(f"Trainable: {name}")
المعلمات القابلة للتدريب
فقط المجموعة الصغيرة من المعلمات التي يتم تحديثها أثناء تدريب PEFT.
المحولات
وحدات إضافية تضاف إلى النموذج المجمد وتحتوي على المعلمات القابلة للتدريب.
مكتبة PEFT
توفر مكتبة PEFT من Hugging Face وصولًا موحدًا إلى جميع طرق PEFT الرئيسية:
from peft import (
LoraConfig,
PrefixTuningConfig,
PromptTuningConfig,
IA3Config,
AdaLoraConfig,
get_peft_model,
PeftModel,
)
# تكوين PEFT
peft_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
)
# تطبيق على النموذج
model = get_peft_model(base_model, peft_config)
# تحقق من المعلمات القابلة للتدريب
model.print_trainable_parameters()
# Output: trainable params: 4,194,304 || all params: 6,742,609,920 || trainable%: 0.0622
الوظائف الرئيسية
| الوظيفة | الغرض |
|---|---|
get_peft_model() | تغليف النموذج الأساسي مع PEFT |
print_trainable_parameters() | عرض عدد القابلة للتدريب |
save_pretrained() | حفظ المحول فقط |
merge_and_unload() | دمج المحول في الأساسي |
load_adapter() | تحميل محول محفوظ |
مقارنة المعلمات
from transformers import AutoModelForCausalLM
from peft import LoraConfig, get_peft_model
# تحميل النموذج الأساسي
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B")
# حساب المعلمات الأساسية
total_params = sum(p.numel() for p in model.parameters())
print(f"Total parameters: {total_params:,}") # ~8,000,000,000
# تطبيق LoRA
lora_config = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"])
model = get_peft_model(model, lora_config)
# حساب القابلة للتدريب
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"Trainable: {trainable:,}") # ~4,000,000
print(f"Percentage: {100 * trainable / total_params:.4f}%") # ~0.05%
فوائد التخزين
حفظ المحول فقط
# النموذج الكامل: ~15GB (نموذج 7B في fp16)
model.save_pretrained("./full-model")
# محول LoRA فقط: ~10-50MB
model.save_pretrained("./lora-adapter", save_embedding_layers=False)
محولات متعددة = تخزين ضئيل
النموذج الأساسي: 15GB (مشترك)
المحول A: 20MB (المهمة 1)
المحول B: 20MB (المهمة 2)
المحول C: 20MB (المهمة 3)
الإجمالي: 15.06GB لثلاثة نماذج متخصصة
مقابل التخصيص الكامل: 45GB لثلاثة نماذج منفصلة
مثال سريع
إكمال تدريب PEFT في أقل من 50 سطرًا:
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model
from trl import SFTTrainer
from datasets import load_dataset
# تحميل النموذج والمحول
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B")
tokenizer.pad_token = tokenizer.eos_token
# تكوين LoRA
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
task_type="CAUSAL_LM",
)
# تطبيق PEFT
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# تحميل مجموعة البيانات
dataset = load_dataset("databricks/dolly-15k", split="train[:1000]")
# تدريب
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=512,
)
trainer.train()
# حفظ المحول
model.save_pretrained("./my-lora-adapter")
النقاط الرئيسية
✅ يدرب PEFT أقل من 1% من المعلمات مع الحفاظ على الجودة
✅ توفير في الذاكرة بنسبة 80-95% مقارنة بالتخصيص الكامل
✅ تبقى أوزان النموذج الأساسي مجمدة، مما يمنع النسيان
✅ يمكن لمحولات متعددة مشاركة نموذج أساسي واحد
✅ توفر مكتبة PEFT وصولًا موحدًا إلى جميع الطرق
✅ ملفات المحولات صغيرة (~10-50MB) مقابل النماذج الكاملة (~15GB+)
الخطوات التالية
تابع إلى الفصل 2: PEFT مقابل التخصيص الكامل لفهم التنازلات بالتفصيل.
تحقق سريع
قبل المتابعة، تأكد من أنك تستطيع الإجابة على:
- ما هي النسبة المئوية للمعلمات التي يدربها PEFT عادةً؟
- لماذا لا يسبب PEFT نسيانًا كارثيًا؟
- ما هي الوظيفة الرئيسية لإنشاء نموذج PEFT؟