LLM 微調教學:LoRA 與 QLoRA 從零開始的 Fine-tuning 實戰指南
自從 ChatGPT 爆紅之後,大家都在談大型語言模型(LLM),但你有沒有想過:這些通用模型真的能完全符合你的業務需求嗎?答案通常是「差一點」。這時候,微調(Fine-tuning)就派上用場了。而 LoRA 和 QLoRA 的出現,更是讓微調 LLM 變得前所未有地容易和便宜。
我第一次嘗試微調 LLM 是在去年,當時用一張消費級的 RTX 3090 就成功微調了一個 7B 參數的模型,整個過程只花了幾個小時。這在以前根本是不可能的事情。今天,我就來分享這段經歷和實際操作的方法。
什麼是 LLM 微調?跟 Prompt Engineering 有什麼不同?
LLM 微調是指在一個已經預訓練好的大型語言模型上,用特定領域的資料進一步訓練,讓模型更擅長處理你的特定任務。
你可能會想:「我用 Prompt Engineering 進階技巧不就好了嗎?」確實,Prompt Engineering 在很多場景下已經夠用了。但微調和 Prompt Engineering 適用的場景不同:
- Prompt Engineering:適合快速原型、簡單任務、不想花太多成本的情況
- 微調:適合需要一致性高的輸出格式、特定領域知識、降低推論成本(因為不需要很長的 prompt)的場景
舉個例子,如果你要讓 LLM 持續用特定的語氣和格式回覆客服問題,微調會比每次都寫一大段 system prompt 來得更有效率。
全參數微調的困境
傳統的全參數微調(Full Fine-tuning)需要更新模型的所有參數。以一個 7B 參數的模型為例,光是模型本身就需要約 14GB 的顯存(FP16),加上梯度和優化器狀態,總共需要超過 100GB 的顯存。這意味著你至少需要好幾張 A100 GPU,成本非常驚人。
這就是為什麼 LoRA 的出現被稱為是一場革命。
LoRA 是什麼?低秩適應的核心概念
LoRA(Low-Rank Adaptation)是微軟在 2021 年提出的一種參數高效微調方法。它的核心概念非常優雅:與其更新整個模型的所有參數,不如只訓練一小組「適配器」參數。
LoRA 的運作原理
技術上來說,LoRA 會在模型的注意力層旁邊插入兩個小矩陣(A 和 B),這兩個矩陣的乘積就是對原始權重的調整量。因為這兩個矩陣的「秩」(rank)很低,所以需要訓練的參數量大幅減少。
用更直覺的方式來理解:原本你要改造整棟大樓(全參數微調),現在你只需要加裝幾個小配件(LoRA 適配器),就能達到類似的效果。通常只需要訓練原始模型 0.1% 到 1% 的參數量。
LoRA 的優勢
- 大幅降低顯存需求:可以在單張消費級 GPU 上微調大模型
- 訓練速度更快:需要更新的參數少,自然訓練得快
- 可以疊加多個 LoRA:針對不同任務訓練不同的 LoRA 適配器,推論時按需載入
- 不會破壞原始模型:原始權重保持不變,新增的適配器可以隨時移除
QLoRA:更進一步的量化微調
QLoRA 在 LoRA 的基礎上再進一步,它先將原始模型量化到 4-bit(NF4 格式),然後在量化後的模型上做 LoRA 微調。這個技術讓你用一張 24GB 顯存的 GPU 就能微調 65B 參數的模型,簡直不可思議。
QLoRA 的關鍵技術包括:
- 4-bit NormalFloat 量化:一種特別適合神經網路權重分佈的量化方式
- 雙重量化:連量化常數本身也做量化,進一步節省記憶體
- 分頁優化器:當 GPU 顯存不足時,自動將部分資料暫存到 CPU 記憶體
實戰教學:用 QLoRA 微調 Llama 模型
接下來是實際操作的部分。我們會使用 Hugging Face 的生態系來完成整個微調流程。
環境準備
pip install torch transformers peft bitsandbytes datasets trl accelerate這些套件各自的角色:peft 負責 LoRA 實作、bitsandbytes 負責量化、trl 提供簡化的訓練介面。
載入模型與量化設定
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# 量化設定
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype="float16",
bnb_4bit_use_double_quant=True,
)
# 載入模型
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=bnb_config,
device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")設定 LoRA 參數
# LoRA 設定
lora_config = LoraConfig(
r=16, # LoRA 的秩,越大效果越好但越吃資源
lora_alpha=32, # 縮放因子
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, lora_config)這裡的 r=16 是 LoRA 的秩(rank),這個值越大,模型的表達能力越強,但需要的資源也越多。對大多數任務來說,8 到 32 之間是個合理的範圍。
準備訓練資料
訓練資料的品質決定了微調的效果。一般來說,你需要準備數百到數千筆高品質的示範資料,格式通常是指令-回覆對(instruction-response pairs)。
如果你的資料量很大,可能還需要考慮搭配 RAG Chunking 策略來做資料的前處理和分割。而微調後的模型也可以結合 RAG 向量資料庫來增強知識檢索能力。
開始訓練
from trl import SFTTrainer
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir="./results",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
warmup_steps=100,
logging_steps=10,
save_strategy="epoch",
fp16=True,
)
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
args=training_args,
tokenizer=tokenizer,
max_seq_length=2048,
)
trainer.train()微調的注意事項與常見陷阱
根據我自己的經驗,分享幾個重要的注意事項:
- 資料品質比數量重要:100 筆高品質的資料,效果可能比 10000 筆垃圾資料還好
- 不要過度訓練:LLM 微調很容易 overfit,通常 1-3 個 epoch 就夠了
- 學習率要低:建議用 1e-4 到 2e-4,太高會破壞預訓練的知識
- 一定要做評估:不要只看 loss 下降就覺得成功了,要用實際的測試案例來驗證
- 保留原始模型能力:微調目標是增強特定能力,而不是取代通用能力
什麼時候該微調,什麼時候不該?
這是一個很實際的問題。我建議你先嘗試以下方案,如果都不能滿足需求再考慮微調:
- 先用好的 prompt 試試看(零成本)
- 試試 few-shot learning,在 prompt 中給幾個範例
- 用 RAG 來補充外部知識
- 以上都不行,再考慮微調
微調適合的場景:需要特定的輸出格式或語氣、領域專業術語、降低推論延遲和成本、資料隱私考量(不想把資料送到第三方 API)。
總結與下一步
LoRA 和 QLoRA 的出現,真的讓 LLM 微調從「大公司的專利」變成了「每個開發者都能做的事」。只需要一張消費級 GPU,你就能根據自己的需求客製化一個專屬的語言模型。
如果你是第一次嘗試,我建議從一個小模型(例如 7B)和少量高品質資料開始,先跑通整個流程,再慢慢調整參數和擴大規模。記住,微調是一門實驗科學,多試幾次才能找到最佳的配置。
繼續閱讀
AI Agent 記憶系統設計:從短期到長期記憶的完整實作指南
想讓你的 AI Agent 記住之前的對話、學會累積經驗?這篇從零開始教你設計完整的記憶系統。
相關文章
你可能也喜歡
探索其他領域的精選好文