إنتقل إلى المحتوى الرئيسي

الفصل 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 × 2dk × 2 (مجمدة)
التدرجاتdk × 22rk × 2
المحسنdk × 82rk × 8
الإجمالي12dk2dk + 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 للتطبيق العملي.


تحقق سريع​

قبل المتابعة، تأكد من أنك تستطيع الإجابة:

  1. ما هو عامل المقياس الفعال لـ r=16، alpha=32؟
  2. كم عدد المعاملات التي يضيفها LoRA لمصفوفة 4096×4096 مع r=8؟
  3. لماذا يتم تهيئة B إلى الصفر بدلاً من A؟