الفصل 2: رياضيات LoRA
الأسس الرياضية للتكيف منخفض الرتبة
فهم الرياضيات وراء LoRA يساعدك في اتخاذ قرارات أفضل بشأن المعاملات الفائقة وفهم سبب عملها بفعالية كبيرة.
تحليل المصفوفات
المعادلة الأساسية
لمصفوفة الوزن المدربة مسبقًا W ∈ ℝ^(d×k):
W' = W + ΔW = W + BA
حيث:
- W هو الوزن المدرب مسبقًا والمجمد (d × k)
- B ∈ ℝ^(d×r) هي مصفوفة الإسقاط السفلي
- A ∈ ℝ^(r×k) هي مصفوفة الإسقاط العلوي
- r هي الرتبة (أصغر بكثير من d و k)
التمرير الأمامي
def forward(x, W, B, A, scaling):
# المسار المجمد
h_base = W @ x # (d × k) @ (k × batch) = (d × batch)
# مسار LoRA
h_lora = B @ A @ x # (d × r) @ (r × k) @ (k × batch) = (d × batch)
# المجموع
return h_base + scaling * h_lora
الرتبة والسعة
ما هي الرتبة؟
الرتبة r تحدد الحد الأقصى لـ "التعبير" لتحديث LoRA.
Matrix ΔW = BA has rank at most r
إذا كانت r صغيرة، يمكن لـ ΔW فقط تمثيل التحديثات في فضاء منخفض الأبعاد.
عدد المعاملات
def lora_params(d, k, r):
"""حساب معاملات LoRA لمصفوفة الوزن."""
return r * d + r * k # مصفوفات B و A
# مثال: مصفوفة 4096 × 4096 مع r=16
params = lora_params(4096, 4096, 16)
print(f"LoRA params: {params:,}") # 131,072
# مقارنة بالمصفوفة الكاملة
full = 4096 * 4096
print(f"Full params: {full:,}") # 16,777,216
print(f"Ratio: {params/full:.4%}") # 0.78%
اختيار الرتبة
| الرتبة | السعة | حالة الاستخدام |
|---|---|---|
| 4 | منخفضة | مهام بسيطة، تغييرات طفيفة |
| 8 | منخفضة-متوسطة | ضبط دقيق قياسي |
| 16 | متوسطة | التوصية الافتراضية |
| 32 | متوسطة-عالية | مهام معقدة |
| 64 | عالية | أقصى تعبير |
| 128+ | عالية جدًا | نادرًا ما تكون مطلوبة |
مقياس ألفا
عامل المقياس
يتضمن LoRA عامل مقياس α (ألفا):
ΔW = (α/r) × BA
لماذا المقياس؟
بدون المقياس، تغيير r يغير حجم التحديثات:
- r أعلى → المزيد من المعاملات → تحديثات أكبر
- r أقل → معاملات أقل → تحديثات أصغر
مع مقياس α/r، يمكنك ضبط r دون تغيير الحجم.
التكوينات الشائعة
# الأنماط الشائعة
config1 = {"r": 16, "alpha": 16} # Scale = 1
config2 = {"r": 16, "alpha": 32} # Scale = 2
config3 = {"r": 32, "alpha": 64} # Scale = 2
# القاعدة العامة: alpha = 2 × r شائع
تأثير ألفا
def effective_scaling(alpha, r):
return alpha / r
# أمثلة
print(effective_scaling(16, 16)) # 1.0
print(effective_scaling(32, 16)) # 2.0
print(effective_scaling(64, 32)) # 2.0
أعلى α/r → مساهمة أكبر من LoRA → تكيف أسرع ولكن مع خطر أكبر لعدم الاستقرار.
التهيئة
مصفوفة A: تهيئة Kaiming/He
# قيم عشوائية صغيرة
A = torch.randn(r, k) * math.sqrt(2 / k)
# أو ببساطة:
A = torch.randn(r, k) * 0.01
مصفوفة B: تهيئة صفرية
B = torch.zeros(d, r)
التفكير الرياضي
عند التهيئة:
ΔW = BA = B × A = 0 × A = 0
h_total = Wx + (α/r) × BAx = Wx + 0 = Wx
يبدأ النموذج تمامًا كنموذج مدرب مسبقًا - بدون أي اضطراب ابتدائي.
تدفق التدرج
حساب التدرجات
لخسارة L، نحتاج إلى تدرجات لـ B و A:
∂L/∂B = (α/r) × (∂L/∂h) × x^T × A^T
∂L/∂A = (α/r) × B^T × (∂L/∂h) × x^T
خاصية رئيسية
التدرجات تتدفق فقط عبر مسار LoRA أثناء التدريب:
- W مجمدة (بدون تدرجات)
- B و A تستقبل وتحدث التدرجات
# في الكود
for name, param in model.named_parameters():
if "lora" in name.lower():
param.requires_grad = True
else:
param.requires_grad = False
تحليل الذاكرة
مكونات ذاكرة التدريب
لمصفوفة الوزن W ∈ ℝ^(d×k):
| المكون | FT الكامل | LoRA |
|---|---|---|
| الأوزان | dk × 2 | dk × 2 (مجمدة) |
| التدرجات | dk × 2 | 2rk × 2 |
| المحسن | dk × 8 | 2rk × 8 |
| الإجمالي | 12dk | 2dk + 20rk |
def memory_comparison(d, k, r, bytes_per_param=2):
"""مقارنة استخدام الذاكرة."""
# FT الكامل: الأوزان + التدرجات + المحسن (2 حالات)
full_ft = d * k * (2 + 2 + 8) # 12 بايت لكل معامل
# LoRA: أوزان مجمدة + تدريب LoRA
frozen = d * k * 2 # فقط الأوزان، بدون تدرجات/محسن
lora = r * (d + k) * (2 + 2 + 8) # تدريب كامل
lora_total = frozen + lora
print(f"Full FT: {full_ft / 1e9:.2f} GB")
print(f"LoRA: {lora_total / 1e9:.2f} GB")
print(f"Savings: {(1 - lora_total/full_ft) * 100:.1f}%")
memory_comparison(4096, 4096, 16)
رياضيات اختيار الرتبة
النظرة المعلوماتية النظرية
الرتبة تحدد عدد "الاتجاهات" المستقلة التي يمكن للتحديث تعديلها:
- الرتبة r تسمح بـ r تعديلات مستقلة
- رتبة أعلى = أكثر تعبيرًا ولكن المزيد من المعاملات
تحليل القيمة المفردة
يمكن تحليل أي مصفوفة:
M = UΣV^T
إذا كان لـ M رتبة فعالة منخفضة (معظم القيم المفردة صغيرة)، فإن تقريب الرتبة المنخفضة يعمل بشكل جيد.
الاختيار العملي
def estimate_rank(task_complexity, model_size):
"""تقدير لاختيار الرتبة."""
base_rank = 8 # قيمة افتراضية جيدة
# التعديل للتعقيد
if task_complexity == "simple":
rank = max(4, base_rank // 2)
elif task_complexity == "moderate":
rank = base_rank
elif task_complexity == "complex":
rank = base_rank * 2
else:
rank = base_rank * 4
return rank
# أمثلة
print(estimate_rank("simple", "7B")) # 4
print(estimate_rank("moderate", "7B")) # 8
print(estimate_rank("complex", "7B")) # 16
رياضيات الدمج
عملية الدمج
عند الاستدلال، يمكننا دمج LoRA في الأوزان الأساسية:
def merge_lora(W, B, A, alpha, r):
"""دمج LoRA في الأوزان الأساسية."""
scaling = alpha / r
W_merged = W + scaling * (B @ A)
return W_merged
بعد الدمج
h = W_merged × x
ما يعادل:
h = (W + (α/r) × BA) × x
ولكن يتم حسابه بضرب مصفوفة واحدة - بدون أي عبء إضافي.
ملخص المعادلات الرئيسية
| المفهوم | المعادلة |
|---|---|
| التمرير الأمامي | h = Wx + (α/r)BAx |
| عدد المعاملات | (d + k) × r |
| عامل المقياس | α/r |
| الأوزان المدمجة | W' = W + (α/r)BA |
| عند التهيئة | BA = 0 (B صفرية) |
النقاط الرئيسية
✅ LoRA تقرب تحديثات الأوزان كمنتجات مصفوفة منخفضة الرتبة
✅ الرتبة r تتحكم في السعة؛ α تتحكم في الحجم
✅ المقياس الفعال = α/r؛ القيمة الشائعة هي 2
✅ التهيئة الصفرية لـ B تضمن بداية نظيفة
✅ توفير الذاكرة يأتي من عدم تخزين التدرجات/المحسن للأوزان المجمدة
✅ الدمج يجمع LoRA في الأوزان الأساسية بدون أي عبء إضافي
الخطوات التالية
تابع إلى الفصل 3: تنفيذ LoRA باستخدام PEFT للتطبيق العملي.
تحقق سريع
قبل المتابعة، تأكد من أنك تستطيع الإجابة:
- ما هو عامل المقياس الفعال لـ r=16، alpha=32؟
- كم عدد المعاملات التي يضيفها LoRA لمصفوفة 4096×4096 مع r=8؟
- لماذا يتم تهيئة B إلى الصفر بدلاً من A؟