مستوى المقال: متوسط. لو سبق لك إنك شغّلت نموذج لغوي أو درّبته ولو مرة، هتمشي مع المقال ده مرتاح. ولو لسه مبتدئ، المثال اللي في الأول هيوصّلك الفكرة كاملة قبل أول سطر كود.
LoRA: درّب نموذج 7B على كارت شاشة واحد بذاكرة 10 جيجا بدل 80
ضبط (fine-tuning) نموذج بـ 7 مليار باراميتر بالطريقة الكاملة بياكل حوالي 80 جيجابايت من ذاكرة الـ GPU. تقنية LoRA بتنزّل الرقم ده لأقل من 10 جيجا، وبتخلّي ناتج تدريبك ملف صغير أقل من 20 ميجابايت بدل نسخة كاملة من النموذج. النتيجة المباشرة: تقدر تدرّب على كارت شاشة واحد بدل ما تستأجر مزرعة كروت.
المشكلة باختصار
الـ fine-tuning الكامل بيحدّث كل وزن في النموذج. ودي مش مشكلة الأوزان نفسها بس. عشان تدرّب بـ optimizer زي Adam، محتاج تخزّن لكل باراميتر: الوزن، والـ gradient، وحالتين إضافيتين للـ optimizer. يعني نموذج 7B وزنه 14 جيجا بدقة 16-bit، بيطلب فعليًا قرابة 80 جيجا وقت التدريب لما تجمع كل ده.
الافتراض اللي بيمشي عليه المقال: إنك بتعمل fine-tuning لنموذج مفتوح المصدر حجمه 13B أو أقل، مش نموذج مغلق زي GPT-4 مش موجود عندك أصلاً. ولو النموذج أكبر من كده، LoRA بيبقى أهم مش أقل.
الفكرة بمثال: ورقة لاصقة بدل ما تعيد طبع الكتاب
تخيّل عندك كتاب مطبوع من 700 صفحة، ولقيت إنك عايز تظبط 20 معلومة بسيطة فيه. مش معقول تعيد طبع الكتاب كله عشان 20 تعديل. اللي بتعمله فعليًا: بتسيب الكتاب زي ما هو، وبتحط ورقة لاصقة صغيرة جنب كل معلومة عايز تعدّلها. القارئ لما يوصل الصفحة، بيقرأ الأصل زائد الورقة اللاصقة، فيطلع بالمعنى الجديد.
LoRA بيعمل بالظبط كده. الكتاب هو أوزان النموذج الأصلية، وبنجمّدها فمفيش تعديل عليها. الورق اللاصق هو مصفوفتين صغيرتين جديدتين بندرّبهم لوحدهم. النموذج وقت الاستخدام بيجمع الأصل زائد اللاصق، فيطلع بسلوك متخصص من غير ما نلمس ولا وزن من الأوزان الأصلية.
الشرح العلمي: التحليل منخفض الرتبة
دلوقتي بالتفصيل والدقة. لما تعمل fine-tuning، اللي بيتغيّر فعليًا هو مقدار التعديل على كل مصفوفة أوزان، ونسمّيه دلتا الأوزان. اكتشاف ورقة LoRA إن التعديل ده رتبته منخفضة فعليًا، يعني ممكن نمثّله بدقة كويسة كحاصل ضرب مصفوفتين صغيرتين بدل مصفوفة ضخمة.
بدل ما ندرّب مصفوفة تعديل مقاسها d في d وفيها ملايين القيم، بنكتبها كحاصل ضرب مصفوفتين: B مقاسها d في r، و A مقاسها r في d، والرتبة r رقم صغير جدًا زي 8. المعادلة في التمرير الأمامي بتبقى: الخرج يساوي الأوزان الأصلية في المدخل زائد B في A في المدخل. الأوزان الأصلية مجمّدة، وبندرّب B و A بس. وبما إن r أصغر بكتير من d، عدد القيم اللي بندرّبها بينهار لجزء بسيط.
خد الحساب على نموذج 7B عليه d يساوي 4096، ولو طبّقنا LoRA على طبقتي الانتباه q و v في 32 طبقة برتبة 8: كل مصفوفة لاصقة فيها حوالي 65 ألف قيمة، والإجمالي حوالي 4.2 مليون باراميتر قابل للتدريب. ده 0.06% بس من الـ 7 مليار. الباقي كله مجمّد.
مثال تنفيذي: LoRA في 15 سطر بـ PEFT
ده كود شغّال بمكتبة PEFT من Hugging Face. بيلفّ النموذج بإعداد LoRA ويطبع كام باراميتر بندرّبه فعلاً.
from transformers import AutoModelForCausalLM
from peft import LoraConfig, get_peft_model
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
model.print_trainable_parameters()
# trainable params: 4,194,304 || all params: 6,742,609,920 || trainable%: 0.0622بعد التدريب، بتحفظ الـ adapter لوحده بأمر model.save_pretrained، فيطلع مجلد صغير أقل من 20 ميجابايت. تقدر تحمّل عليه أكتر من adapter لمهام مختلفة على نفس النموذج الأساسي من غير ما تخزّن نسخة كاملة لكل مهمة.
الأرقام الحقيقية و trade-offs
ورقة LoRA الأصلية على GPT-3 بحجم 175B قلّلت الباراميتر القابل للتدريب بمقدار 10 آلاف مرة، وذاكرة الـ GPU بمقدار 3 مرات، من غير أي تأخير إضافي وقت الاستدلال لأنك تقدر تدمج المصفوفتين في الأوزان بعد التدريب. وتقنية QLoRA وسّعت الفكرة لدرجة إنها درّبت نموذج 65B على كارت واحد بذاكرة 48 جيجا.
الـ trade-off هنا صريح: بتكسب توفير ذاكرة هائل وسرعة تجربة أعلى وملفات adapter صغيرة، بس بتخسر شوية سقف في الجودة القصوى. الـ full fine-tuning لسه بيدّي أعلى دقة ممكنة لما البيانات كتير جدًا والمهمة بعيدة عن توزيع النموذج الأصلي. LoRA بيوصل لـ 95 إلى 99% من مكسب الـ full fine-tuning في أغلب المهام العملية، مش 100%.
متى لا تستخدم LoRA
متستخدموش لو محتاج تعلّم النموذج معرفة جديدة كليًا وواسعة زي لغة جديدة بالكامل أو دومين بعيد جدًا عن بيانات التدريب الأصلية، هنا الرتبة المنخفضة بتبقى ضيقة على حجم التغيير المطلوب، وممكن تحتاج ترفع r كتير لدرجة إن الميزة تختفي. وكمان لو عندك ذاكرة وميزانية كافية والفرق البسيط في الدقة بيفرق فلوس حقيقية في الإنتاج، الطريقة الكاملة تبقى مبرّرة.
الخطوة التالية
ثبّت peft و transformers، وشغّل الكود اللي فوق على أي نموذج مفتوح صغير عندك، وبُصّ في سطر print_trainable_parameters. لو النسبة طلعت أقل من 1%، يبقى LoRA اشتغل صح. جرّب تغيّر r من 8 لـ 16 وشوف تأثيرها على النسبة وعلى الجودة، وابدأ منها.
المصادر
- ورقة LoRA الأصلية — Hu et al., 2021: https://arxiv.org/abs/2106.09685
- ورقة QLoRA — Dettmers et al., 2023: https://arxiv.org/abs/2305.14314
- توثيق Hugging Face PEFT الرسمي: https://huggingface.co/docs/peft
- مستودع Microsoft LoRA على GitHub: https://github.com/microsoft/LoRA