الدرس 11 من 24

LoRA و QLoRA عملياً

QLoRA: الضبط الدقيق 4-bit

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

QLoRA يجمع LoRA مع تكميم 4-bit، مما يتيح ضبط النماذج الكبيرة على GPUs المستهلك. دعنا نفهم كيف يعمل وكيف نُعده.

كيف يعمل QLoRA

QLoRA لديه ثلاثة ابتكارات رئيسية:

  1. 4-bit NormalFloat (NF4) - صيغة تكميم محسّنة لأوزان الشبكات العصبية
  2. التكميم المزدوج - يُكمّم ثوابت التكميم لتوفير ذاكرة إضافي
  3. المحسّنات المُرقّمة - تستخدم ذاكرة CPU لحالات المحسّن عندما تنفد GPU
نموذج 7B قياسي:    ~14GB VRAM (fp16)
مع QLoRA:          ~4-6GB VRAM
التوفير:           60-70% أقل VRAM

BitsAndBytesConfig

التكوين الأساسي لتكميم 4-bit:

from transformers import BitsAndBytesConfig
import torch

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,                      # فعّل تحميل 4-bit
    bnb_4bit_quant_type="nf4",              # تكميم NormalFloat4
    bnb_4bit_compute_dtype=torch.bfloat16,  # دقة الحساب
    bnb_4bit_use_double_quant=True          # التكميم المزدوج
)

تعمّق في المعاملات

load_in_4bit

يفعّل تكميم 4-bit. أوزان النموذج تُخزن في 4-bit لكن تُحسب بدقة أعلى.

bnb_4bit_quant_type

النوع الوصف الأفضل لـ
"nf4" NormalFloat4 - محسّن للأوزان الموزعة طبيعياً معظم الحالات
"fp4" float 4-bit قياسي التوافق
# NF4 (موصى)
bnb_config = BitsAndBytesConfig(bnb_4bit_quant_type="nf4")

bnb_4bit_compute_dtype

نوع البيانات المستخدم للحساب أثناء التمرير الأمامي/الخلفي:

# BFloat16 (موصى لـ GPUs الحديثة)
bnb_config = BitsAndBytesConfig(bnb_4bit_compute_dtype=torch.bfloat16)

# Float16 (لـ GPUs الأقدم أو التوافق)
bnb_config = BitsAndBytesConfig(bnb_4bit_compute_dtype=torch.float16)

bnb_4bit_use_double_quant

يُكمّم ثوابت التكميم، يوفر ~0.4 bit لكل معامل:

# فعّل التكميم المزدوج (موصى)
bnb_config = BitsAndBytesConfig(bnb_4bit_use_double_quant=True)

تحميل نموذج مع QLoRA

from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
import torch

# 1. كوّن التكميم
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True
)

# 2. حمّل النموذج المُكمّم
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.2-8B-Instruct",
    quantization_config=bnb_config,
    device_map="auto",
    torch_dtype=torch.bfloat16
)

# 3. جهّز للتدريب
model = prepare_model_for_kbit_training(model)

# 4. أضف محولات LoRA
lora_config = LoraConfig(
    r=16,
    lora_alpha=16,
    target_modules="all-linear",
    lora_dropout=0.0,
    bias="none",
    task_type="CAUSAL_LM"
)

model = get_peft_model(model, lora_config)

# 5. حمّل المُرمّز
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-8B-Instruct")
tokenizer.pad_token = tokenizer.eos_token

مقارنة الذاكرة

النموذج ضبط كامل LoRA (fp16) QLoRA (4-bit)
Llama 3.2 1B 8GB 4GB 2GB
Llama 3.2 3B 24GB 12GB 4GB
Llama 3.2 8B 64GB 18GB 6GB
Mistral 7B 56GB 16GB 5GB
Llama 3.3 70B 560GB 160GB 24GB

Gradient Checkpointing

قلل الذاكرة أكثر بإعادة حساب التنشيطات أثناء التمرير الخلفي:

# فعّل gradient checkpointing
model.gradient_checkpointing_enable()
model.enable_input_require_grads()

# أو عبر معاملات التدريب
training_args = TrainingArguments(
    gradient_checkpointing=True,
    ...
)

إعداد QLoRA الكامل

import torch
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from trl import SFTTrainer
from datasets import load_dataset

# تكوين النموذج
model_name = "meta-llama/Llama-3.2-3B-Instruct"

# تكوين التكميم
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True
)

# حمّل النموذج
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto"
)

# جهّز لتدريب k-bit
model = prepare_model_for_kbit_training(model)
model.gradient_checkpointing_enable()

# تكوين LoRA
lora_config = LoraConfig(
    r=16,
    lora_alpha=16,
    target_modules="all-linear",
    lora_dropout=0.0,
    bias="none",
    task_type="CAUSAL_LM"
)

# طبّق LoRA
model = get_peft_model(model, lora_config)

# تحقق من المعاملات القابلة للتدريب
model.print_trainable_parameters()

# حمّل المُرمّز
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

المشاكل الشائعة والحلول

خسارة NaN

# استخدم قص التدرج
training_args = TrainingArguments(
    max_grad_norm=0.3,  # أقل من الافتراضي 1.0
    ...
)

تدريب بطيء

# فعّل Flash Attention 2
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    attn_implementation="flash_attention_2",
    ...
)

نفاد الذاكرة

# قلل حجم الدفعة واستخدم تراكم التدرج
training_args = TrainingArguments(
    per_device_train_batch_size=1,
    gradient_accumulation_steps=8,
    ...
)

نصيحة: QLoRA هو الخيار الأمثل لـ GPUs المستهلك. ابدأ هنا، وانتقل لـ LoRA الكامل فقط إذا كان لديك VRAM واحتجت تحسين الجودة الطفيف.

بعد ذلك، سنجمع كل شيء معاً ونشغّل مهمة ضبط دقيق كاملة. :::

اختبار

الوحدة 3: LoRA و QLoRA عملياً

خذ الاختبار