· llm / fine-tuning / lora
Cách fine-tune LLM nhỏ năm 2026 (LoRA trên laptop)
Fine-tune Llama 3.1 8B QLoRA trên GPU consumer — cài đặt ghim phiên bản, cấu hình training chính xác, xuất GGUF sang Ollama, và tám trường hợp lỗi.
Bởi Ethan
2.051 từ · 11 phút đọc
Fine-tune Llama 3.1 8B trên phần cứng consumer là hoàn toàn khả thi trong năm 2026. Với Unsloth v0.1.39-beta và QLoRA 4-bit quantization, một chiếc RTX 3080 (10 GB VRAM) là đủ — cùng thao tác đó sẽ OOM trên vanilla HuggingFace PEFT. Số tham số trainable là 42M trong tổng số 8B (0.52%), và với dataset 500–1.000 ví dụ, bạn sẽ mất khoảng 20–60 phút trên Colab T4 miễn phí.
Bài viết này dành cho ai
Dành cho các developer cần model đưa ra output đúng định dạng, tuân theo phong cách riêng, hoặc xử lý một lĩnh vực hẹp tốt hơn base model. Bạn cần GPU ít nhất 6 GB VRAM, Python 3.10+, và một dataset nhỏ. Nếu bạn dùng macOS Apple Silicon mà không có discrete GPU, Unsloth chưa hỗ trợ training tính đến tháng 5 năm 2026 — inference thì vẫn chạy được, training thì không.
Yêu cầu cài đặt
Cấu hình tối thiểu (QLoRA 4-bit, Unsloth)
| Model | VRAM cần thiết |
|---|---|
| Llama 3.2 3B | 3.5 GB |
| Mistral 7B | 5 GB |
| Llama 3.1 8B | 6 GB |
| Llama 3.3 70B | 41 GB |
Bài hướng dẫn này dùng Llama 3.1 8B. Nếu VRAM của bạn dưới 6 GB, hãy xuống 3B (unsloth/Llama-3.2-3B-bnb-4bit). Nếu không có GPU, Lambda Labs cho thuê RTX 3090 với giá khoảng $0.50/giờ — đủ cho một lần training ngắn.
Nếu bạn đang cân nhắc giữa fine-tune và gọi hosted API, chi phí thực sự khi chạy đội AI agent năm 2026 phân tích TCO chi tiết bao gồm cả chi phí thuê GPU.
Cài đặt toolchain
Trình cài đặt Unsloth tự động ghim các phiên bản tương thích của transformers, trl, peft và datasets — một số bản vá nhỏ trong dải 4.x gây ra recursion error hoặc lỗi training, và installer sẽ loại chúng ra.
curl -fsSL https://unsloth.ai/install.sh | sh
Kiểm tra các phiên bản đã được resolve:
pip show unsloth peft trl transformers
# unsloth 0.1.39b0
# peft 0.19.1
# trl 0.18.2 (or 0.23+ — see §6 Troubleshooting)
# transformers 4.51.3 (or a later compatible point release)
Trường hợp lỗi: Nếu bạn cài trl riêng và nó resolve sang v0.23 trở lên, SFTConfig (từ trl) là config class được khuyến nghị cho SFTTrainer. Đoạn code training ở §3 dùng transformers.TrainingArguments — vẫn chạy được nhưng bỏ qua một số cấu hình riêng của SFT. Hãy ghim trl<0.23 hoặc cập nhật import (xem §6).
Tải base model
Checkpoint đã được quantize 4-bit có dung lượng ~5.4 GB, trong khi phiên bản 16-bit chiếm ~16 GB. Luôn dùng biến thể bnb-4bit:
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Meta-Llama-3.1-8B-bnb-4bit",
max_seq_length=2048,
load_in_4bit=True,
dtype=None, # auto-detects BF16 on Ampere and later
)
Trên GPU 8 GB, Unsloth chạy được 2.972 token context với Llama 3.1 8B. Vanilla HuggingFace với Flash Attention 2 sẽ OOM trên cùng phần cứng đó.
Chuẩn bị dữ liệu
Định dạng
Dùng ShareGPT JSONL với danh sách conversations. Đây là định dạng mà notebook chuẩn của mlabonne sử dụng và helper train_on_responses_only của Unsloth mong đợi:
{"conversations": [{"from": "human", "value": "Summarize this contract clause in plain English: ..."}, {"from": "gpt", "value": "This clause means the vendor can ..."}]}
{"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]}
Mỗi dòng là một ví dụ training. Trường from phải là "human" và "gpt" — các giá trị khác sẽ làm hỏng mapping của template ChatML.
Cần bao nhiêu ví dụ
| Nhiệm vụ | Tối thiểu |
|---|---|
| Phân loại (mỗi class) | 100–300 |
| Trích xuất có cấu trúc | 200–500 |
| Instruction-following tổng quát | 500–1.000 |
| Sinh nội dung | 500–2.000 |
| Domain phức tạp (pháp lý, y tế) | 1.000–5.000 |
Chất lượng quan trọng hơn số lượng. 200 ví dụ được chọn lọc kỹ luôn cho kết quả tốt hơn 2.000 ví dụ thu thập vội. Một ví dụ sai định dạng duy nhất có thể dạy model một pattern sai xuyên suốt quá trình training.
Tải dữ liệu với datasets
from datasets import load_dataset
dataset = load_dataset("json", data_files="my_data.jsonl", split="train")
Trường hợp lỗi: Một số bản vá nhỏ của datasets 4.x gây ra recursion error — installer của Unsloth sẽ tự loại chúng. Nếu bạn đang dùng môi trường cũ, hãy chạy pip show datasets và cài lại qua script của Unsloth để lấy phiên bản tương thích.
Chạy fine-tune
Gắn LoRA adapter vào rồi chạy trainer. Cấu hình này được lấy từ notebook chuẩn của mlabonne và đã được xác nhận hoạt động — điều chỉnh cho dataset nhỏ ở local.
from unsloth import FastLanguageModel, is_bfloat16_supported
from unsloth.chat_templates import get_chat_template
from trl import SFTTrainer
from transformers import TrainingArguments
from datasets import Dataset
# (assuming model, tokenizer, dataset from previous steps)
# Apply ChatML template
tokenizer = get_chat_template(tokenizer, chat_template="chatml")
def format_conversations(examples):
texts = [
tokenizer.apply_chat_template(conv, tokenize=False, add_generation_prompt=False)
for conv in examples["conversations"]
]
return {"text": texts}
dataset = dataset.map(format_conversations, batched=True)
# Attach LoRA adapter — trains 42M of 8B params (0.52%)
model = FastLanguageModel.get_peft_model(
model,
r=16,
lora_alpha=16,
lora_dropout=0,
target_modules=["q_proj", "k_proj", "v_proj", "up_proj", "down_proj", "o_proj", "gate_proj"],
use_rslora=True, # Rank-Stabilized LoRA — more stable at r=16
use_gradient_checkpointing="unsloth", # Unsloth's custom gradient checkpointing — extra VRAM savings
)
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=2048,
packing=True,
args=TrainingArguments(
learning_rate=3e-4,
per_device_train_batch_size=2,
gradient_accumulation_steps=4, # effective batch = 8
num_train_epochs=1,
optim="adamw_8bit",
weight_decay=0.01,
warmup_steps=10,
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
output_dir="outputs",
logging_steps=10,
),
)
trainer.train()
Kết quả có thể mong đợi
| Phần cứng | Kích thước dataset | Thời gian ước tính |
|---|---|---|
| Colab T4 (16 GB) | 500–2.000 ví dụ | 20–60 phút |
| Colab T4 (16 GB) | 100k ví dụ | ~47 giờ |
| A100 40 GB | 100k ví dụ | ~4 giờ 45 phút |
| RTX 3080 (10 GB) | 500–2.000 ví dụ | 30–90 phút |
Lần chạy đầu tiên sẽ chậm — quá trình warmup của torch.compile mất tới 5 phút. Chỉ đo throughput sau khi vài bước đầu đã ổn định.
Trường hợp lỗi — OOM: Giảm per_device_train_batch_size xuống 1 và tăng gradient_accumulation_steps để giữ nguyên effective batch. Thử thêm maximum_memory_usage=0.5 nếu OOM xảy ra trong lúc evaluation.
Đánh giá kết quả
Theo dõi training loss trong logs. Các mức bình thường:
| Giá trị loss | Ý nghĩa |
|---|---|
| 1.5–2.5 | Quá cao — model chưa học được; kiểm tra lại định dạng dữ liệu |
| 0.5–1.0 | Bình thường |
| 0.1–0.4 | Đang xuống thấp — một epoch thường là đủ |
| Gần 0.0 | Overfit — giảm số epoch hoặc tăng kích thước dataset |
Sau khi training xong, hãy kiểm tra nhanh kết quả trước khi xuất:
FastLanguageModel.for_inference(model)
messages = [{"role": "user", "content": "Your test prompt here"}]
inputs = tokenizer.apply_chat_template(
messages, tokenize=True, add_generation_prompt=True, return_tensors="pt"
).to("cuda")
outputs = model.generate(inputs, max_new_tokens=256, temperature=0.7)
print(tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True))
Nếu output mạch lạc và đúng nhiệm vụ, model đã sẵn sàng để xuất. Nếu output bị lặp hoặc vô nghĩa, hãy kiểm tra chat template và đường loss trước khi kết luận training thất bại.
Quantize sang GGUF để inference local
Unsloth xuất trực tiếp sang GGUF và tự động tạo Ollama Modelfile, xử lý chat template mapping cho các họ model được hỗ trợ gồm Llama-3, Mistral, Phi-3 và Gemma. Đừng tự viết Modelfile — sai template là nguyên nhân phổ biến nhất sau khi xuất.
# Q4_K_M: good quality/size balance, ~4.5 GB output
model.save_pretrained_gguf("model_gguf", tokenizer, quantization_method="q4_k_m")
Các tùy chọn quantization:
| Phương pháp | Dung lượng | Chất lượng |
|---|---|---|
q8_0 | ~8 GB | Gần lossless — dùng để kiểm tra chất lượng |
q5_k_m | ~5.5 GB | Điểm cân bằng tốt |
q4_k_m | ~4.5 GB | Khuyến nghị cho production |
Trường hợp lỗi — OOM khi lưu: Truyền maximum_memory_usage=0.5 vào save_pretrained_gguf. Giá trị mặc định là 0.75, đủ để đẩy card 8 GB vượt ngưỡng.
Sau khi file GGUF được tạo:
# Unsloth wrote model_gguf/Modelfile — use it directly
ollama create my-fine-tuned-model -f model_gguf/Modelfile
ollama run my-fine-tuned-model
Thử với cùng prompt bạn đã dùng lúc kiểm tra kết quả. Nếu output khớp với những gì bạn thấy trong Python, việc xuất file thành công.
Để so sánh chi tiết Ollama với LM Studio — throughput, hiệu quả bộ nhớ và API compatibility — xem Ollama vs LM Studio trên Mac: cái nào dùng được hàng ngày?.
Xử lý khi gặp sự cố
OOM trong lúc training
- Đặt
per_device_train_batch_size=1, tănggradient_accumulation_stepsđể bù lại. - Giảm
max_seq_lengthxuống 1024. - Đặt
fp16_full_eval=Truevà thêmeval_accumulation_steps=4.
Loss bị kẹt ở 0 (không phải NaN — chỉ đứng ở 0.0)
Nguyên nhân: train_on_responses_only đang mask toàn bộ vì delimiter không khớp với token boundaries của Llama 3.1.
Cách sửa — dùng đúng delimiter của Llama 3.1:
from unsloth.chat_templates import train_on_responses_only
trainer = train_on_responses_only(
trainer,
instruction_part="<|start_header_id|>user<|end_header_id|>\n\n",
response_part="<|start_header_id|>assistant<|end_header_id|>\n\n",
)
Nếu bạn không dùng train_on_responses_only, hãy kiểm tra JSONL có "from": "gpt" (không phải "from": "assistant") — template ShareGPT ánh xạ gpt sang vai trò assistant, không phải chuỗi literal assistant.
Loss NaN
- Learning rate quá cao — giảm
learning_ratetừ3e-4xuống1e-4. - JSONL sai định dạng — một dòng có cấu trúc sai (thiếu key
conversations, trộn lẫn template) có thể gây NaN loss. Validate dataset:assert all("conversations" in ex for ex in dataset). - Gradient explosion — thêm
max_grad_norm=0.3vàoTrainingArguments(mặc định là 1.0).
Catastrophic forgetting
Triệu chứng: model chỉ sinh ra text trông giống data training của bạn, mất khả năng xử lý ngôn ngữ tổng quát.
Cách khắc phục:
- Giảm rank:
r=8thay vìr=16. - Ít epoch hơn: 1 epoch thường là đủ.
- Trộn thêm 10–20% ví dụ từ domain tổng quát như
mlabonne/FineTome-100k. - Thêm dropout nhỏ:
lora_dropout=0.05.
LoRA vốn kháng catastrophic forgetting tốt hơn full fine-tuning, nhưng không hoàn toàn miễn nhiễm khi dataset nhỏ và đồng nhất.
TRL 0.23+ — import TrainingArguments thất bại
SFTConfig (từ trl) là config class được khuyến nghị cho SFTTrainer từ TRL 0.9+. Nó xử lý các argument riêng của SFT vốn trước đây được truyền trực tiếp vào SFTTrainer.__init__. Dùng transformers.TrainingArguments vẫn chạy được nhưng bỏ qua các cấu hình đó. Nếu bạn đang dùng TRL 0.23 trở lên, hãy chuyển sang:
# trl >= 0.23
from trl import SFTConfig
args = SFTConfig(
learning_rate=3e-4,
per_device_train_batch_size=2,
# ... rest of args unchanged
output_dir="outputs",
)
Phần còn lại của trainer setup không thay đổi. Nếu bạn muốn ghim API cũ: pip install "trl<0.23".
Sai template sau khi xuất GGUF
Triệu chứng: model sinh ra lặp vô hạn hoặc vô nghĩa sau khi chạy ollama run.
Cách sửa: luôn dùng Modelfile mà Unsloth tạo ra tại model_gguf/Modelfile. Đừng tự tạo file riêng. Nếu bạn đã tự tạo một cái, hãy xóa nó đi và chạy lại save_pretrained_gguf.
Lỗi biên dịch CUDA
Đặt UNSLOTH_COMPILE_DISABLE=1 trước khi bắt đầu training. Download chậm và bị treo ở 90–95%: đặt UNSLOTH_STABLE_DOWNLOADS=1. Cả hai đều là biến môi trường — đặt trong shell hoặc ở đầu script với os.environ["UNSLOTH_COMPILE_DISABLE"] = "1".
macOS Apple Silicon
API training Python của Unsloth chưa hỗ trợ MLX (tính đến tháng 5 năm 2026). Inference trên model GGUF vẫn chạy tốt qua Ollama. Để training trên Mac dòng M, dùng Axolotl với MPS backend trực tiếp — chậm hơn và tốn RAM hơn CUDA, nhưng hoạt động được.
Tham khảo
- Unsloth v0.1.39-beta — source, install script, compatibility matrix
- PEFT v0.19.1 — underlying LoRA engine
- VRAM requirements — per-model minimums and context length benchmarks
- Unsloth benchmarks — 2× speed, >70% less VRAM methodology
- mlabonne canonical notebook — Llama 3.1 8B / Unsloth / T4
- mlabonne HuggingFace post — walkthrough for the canonical notebook
- LoRA hyperparameters guide — rank, alpha, dropout tradeoffs
- Unsloth → Ollama tutorial — GGUF export and Modelfile
- Ollama import guide — custom GGUF import workflow
- Unsloth troubleshooting — OOM, NaN loss, CUDA errors
- Dataset size empirical guide — minimum examples per task type