Fine-tuning & Model Customization
Trang chủ / Module 4
🔧 Module 4 / 14 · 1-2 tuần

LoRA & QLoRA — Lý thuyết và thực hành

Cơ chế low-rank decomposition, rank/alpha/target_modules, lượng tử hoá 4-bit trong QLoRA, code thực hành đầy đủ.

LoRAQLoRAPEFT
📌 Vì sao module này quan trọng

LoRA và biến thể QLoRA của nó là kỹ thuật fine-tuning được dùng nhiều nhất trong thực tế ngoài các phòng thí nghiệm lớn — nó là lý do một người với 1 GPU tiêu dùng (hoặc thuê GPU theo giờ với vài USD) có thể fine-tune một model 7B-13B ngay tại nhà. Đây là module thực hành trọng tâm của cả khoá học.

🎯 Mục tiêu học tập

  • Giải thích được cơ chế low-rank decomposition đằng sau LoRA
  • Cấu hình đúng các siêu tham số quan trọng: rank (r), alpha, target_modules, dropout
  • Hiểu QLoRA lượng tử hoá model gốc xuống 4-bit để tiết kiệm bộ nhớ ra sao mà vẫn giữ chất lượng huấn luyện
  • Viết được code fine-tune một model nhỏ bằng LoRA/QLoRA từ đầu đến cuối

Cơ chế LoRA: Low-Rank Decomposition

Với một weight matrix gốc W (kích thước d×k, đã đóng băng), LoRA không sửa trực tiếp W mà học thêm một "phần điều chỉnh" ΔW = B·A, trong đó A có kích thước r×k và B có kích thước d×r, với r (rank) rất nhỏ (thường 8-64) so với d và k (thường hàng nghìn). Output của layer trở thành:

h = W·x + (B·A)·x * (alpha / r)

Vì r nhỏ, số tham số cần huấn luyện trong AB nhỏ hơn W gốc hàng trăm đến hàng nghìn lần. Trực giác: giả định rằng "phần điều chỉnh cần thiết" để thích ứng model với tác vụ mới có hạng nội tại (intrinsic rank) thấp — tức là không cần thay đổi model theo mọi hướng có thể, chỉ cần thay đổi theo một số ít "hướng" quan trọng nhất — giả định này được nghiên cứu gốc của LoRA chứng minh thực nghiệm là hợp lý cho hầu hết tác vụ thích ứng (adaptation) thực tế.

Siêu tham số quan trọng

Tham sốÝ nghĩaGiá trị thường dùng
r (rank)Kích thước "cổ chai" — r càng lớn, model càng có khả năng học pattern phức tạp nhưng càng nhiều tham số cần train8-64 (bắt đầu từ 16, tăng nếu underfitting)
alphaHệ số scale cho ΔW (xem công thức trên) — thường đặt alpha = 2×r làm điểm khởi đầu16-32
target_modulesLayer nào được gắn LoRA — thường là các ma trận attention (q_proj, v_proj, đôi khi cả k_proj, o_proj), có thể mở rộng sang MLP để tăng khả năng họcTối thiểu q_proj + v_proj
lora_dropoutDropout riêng cho nhánh LoRA, chống overfitting khi dữ liệu ít0.05-0.1

QLoRA: Lượng tử hoá 4-bit + LoRA

QLoRA kết hợp LoRA với việc lượng tử hoá model gốc xuống 4-bit (dùng định dạng NF4 — NormalFloat 4-bit, tối ưu cho phân phối trọng số của model đã huấn luyện) trước khi gắn adapter LoRA lên trên. Đóng góp kỹ thuật quan trọng giúp không mất nhiều chất lượng: double quantization (lượng tử hoá cả hằng số lượng tử hoá để tiết kiệm thêm bộ nhớ) và paged optimizer (dùng cơ chế bộ nhớ CPU/GPU linh động của NVIDIA để tránh out-of-memory khi có đột biến bộ nhớ). Kết quả: một model 7B có thể fine-tune trên GPU chỉ 12-16GB VRAM (ví dụ RTX 4060 Ti/4070) thay vì cần 24GB+ như LoRA thường (không lượng tử hoá).

Code thực hành: QLoRA với Hugging Face

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

model_name = "Qwen/Qwen2.5-7B-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")
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = prepare_model_for_kbit_training(model)

lora_config = LoraConfig(
    r=16, lora_alpha=32, lora_dropout=0.05,
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# In ra: ví dụ "trainable params: 20M || all params: 7.6B || trainable%: 0.26%"
💡 Công cụ tăng tốc

Thư viện Unsloth viết lại các phép tính lõi bằng kernel tối ưu, cho tốc độ huấn luyện QLoRA nhanh hơn 2x và dùng ít VRAM hơn khoảng 60% so với cách viết thuần Hugging Face ở trên, với cùng chất lượng kết quả — đáng cân nhắc thay thế khi tài nguyên GPU hạn chế.

Sau khi train: Merge hay giữ tách rời?

Sau khi huấn luyện xong, bạn có 2 lựa chọn: merge (gộp B·A trực tiếp vào W gốc, tạo ra 1 model độc lập, đơn giản khi serving nhưng mất khả năng "tháo rời") hoặc giữ tách rời (giữ adapter LoRA như 1 file nhỏ riêng, load động cùng base model lúc chạy — cho phép chạy nhiều adapter khác nhau trên cùng 1 base model, tiết kiệm bộ nhớ đáng kể khi cần phục vụ nhiều "phiên bản chuyên biệt" — xem thêm ở Module 13).

🏋️ Bài tập thực hành

Fine-tune model đầu tiên bằng QLoRA

Trên 1 GPU thuê theo giờ (RunPod/Colab/Lambda) hoặc GPU cá nhân nếu đủ VRAM, fine-tune 1 model nhỏ (Qwen2.5-1.5B hoặc tương đương) bằng QLoRA trên 1 dataset instruction công khai nhỏ (vài trăm mẫu). Thử nghiệm với 2 giá trị rank khác nhau (r=8 và r=32), so sánh training loss cuối cùng và chất lượng output chủ quan trên vài câu hỏi test. Ghi lại VRAM thực tế đã dùng.

📚 Tài nguyên học tập

✅ Tự đánh giá hoàn thành

  • Giải thích được công thức B·A và ý nghĩa của rank
  • Cấu hình đúng target_modules, r, alpha cho một bài toán cụ thể
  • Giải thích được QLoRA tiết kiệm bộ nhớ bằng cơ chế nào (NF4, double quantization)
  • Đã tự fine-tune thành công ít nhất 1 model bằng QLoRA và đo được VRAM sử dụng thật