[AGENT] 8 分鐘閱讀OraCore 編輯部

PEFT LoRA 微調 LLM 實作指南

這篇教你用 PEFT 和 LoRA 只訓練小型 adapter,完成 LLM 微調、保存與部署。

分享 LinkedIn
PEFT LoRA 微調 LLM 實作指南

這篇教你用 PEFT 和 LoRA 只訓練小型 adapter,完成 LLM 微調、保存與部署。

這篇給想在不重訓整個 LLM 的前提下,快速做領域微調的開發者。照著做完,你會得到一個可訓練的 LoRA adapter、可重複使用的訓練流程,以及一份能確認只有少量參數在更新的檢查結果。

你也會知道 PEFT 為什麼適合 production,adapter 和 full fine-tuning 的差別是什麼,還能把訓練出的 adapter 掛回同一個 base model 做推論。

開始之前

訂閱 AI 趨勢週報

每週精選模型發布、工具應用與深度分析,直送信箱。不定期,不騷擾。

不會寄垃圾信,隨時可取消。

  • Python 3.10+
  • PyTorch 2.1+
  • Hugging Face Transformers
  • Hugging Face PEFT
  • CUDA GPU,至少 16 GB VRAM,適合跑小型 LoRA 範例
  • Hugging Face 帳號與 access token,用於下載 gated model
  • 一個 pretrained causal LLM,例如 Llama、Mistral 或較小的開源模型
  • Git 與終端機,環境可在 macOS、Linux 或 Windows Subsystem for Linux

Step 1: 選定基礎模型

這一步的產出是「凍結的 base model」。PEFT 的核心是保留既有語言能力,只對任務相關行為做小幅調整,所以你要先選一個已經懂語言的 pretrained model。

PEFT LoRA 微調 LLM 實作指南

先用較小的開源模型驗證流程,再往更大的模型擴充。這樣你可以先確認資料載入、訓練與儲存都正常,再處理更高的顯存需求。

from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "mistralai/Mistral-7B-v0.1"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto")

for param in model.parameters():
    param.requires_grad = False

你應該看到模型成功載入,而且所有原始權重都被標記為 frozen。若你印出幾個參數,requires_grad 應該是 False

Step 2: 掛上 LoRA adapter

這一步的產出是「可訓練的 LoRA 模組」。LoRA 會在指定投影層加入低秩矩陣,讓模型只更新很小一部分參數,就能學到新任務行為。

PEFT LoRA 微調 LLM 實作指南

初學時先鎖定 attention projection,例如 q_projv_proj。這樣 adapter 夠小,訓練成本也低,通常已經能看到明顯效果。

from peft import LoraConfig, get_peft_model

config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    target_modules=["q_proj", "v_proj"],
    task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
model.print_trainable_parameters()

你應該看到 trainable parameter 的報告,而且可訓練比例遠低於整個模型。健康的 LoRA 設定通常只會更新不到 1% 的參數。

Step 3: 整理任務資料

這一步的產出是「一致格式的訓練資料集」。PEFT 不能取代資料品質,它只是把需要更新的參數變少,所以資料仍然要能清楚對應你要的行為。

把資料寫成固定模板,例如 prompt 和 ideal response。若你要做客服助理,就放客服問題與標準回覆;若你要做程式助手,就放指令與修正版輸出。

格式要穩定,因為模型會從重複模式中學習。如果每幾筆資料的提示詞結構都不同,adapter 會更難收斂,評估也會變得模糊。

你應該看到每筆樣本都能直接看出任務目標,而且輸入與輸出邊界清楚。只要人類一眼能判斷這筆資料在教什麼,資料集就算合格。

Step 4: 執行 adapter 訓練

這一步的產出是「只更新 LoRA 權重的訓練結果」。PEFT 的主要價值就在這裡:顯存壓力更低、可訓練參數更少、checkpoint 也更小。

你可以用 Transformers 的 Trainer,或自己寫訓練迴圈。重點不是訓練框架,而是確認 base model 沒有被解凍,只有 adapter 在接收梯度。

from transformers import Trainer, TrainingArguments

args = TrainingArguments(
    output_dir="./peft-output",
    per_device_train_batch_size=2,
    num_train_epochs=3,
    learning_rate=2e-4,
    fp16=True,
)

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=train_dataset,
)
trainer.train()

你應該看到 loss 逐步下降,而且 GPU 記憶體用量明顯低於 full fine-tuning。如果記憶體還是像整模型更新一樣暴增,先回頭檢查 base model 是否真的 frozen。

Step 5: 匯出並重載 adapter

這一步的產出是「可攜式 adapter 檔案」。這也是 PEFT 很適合 production 的原因之一,因為你通常只要保存小型 adapter,不必重新分發整個 base checkpoint。

把 adapter 單獨存起來,之後在推論時再掛回同一個 base model。這樣你就能維持一份共同的基礎模型,同時切換多個不同任務的專用版本。

model.save_pretrained("./customer-support-lora")
tokenizer.save_pretrained("./customer-support-lora")

# Later
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto")
adapted_model = PeftModel.from_pretrained(base_model, "./customer-support-lora")

你應該看到 adapter 很快載入,推論結果也開始帶有任務特徵。如果輸出還是很泛用,通常是 adapter 和 base model 版本不一致。

Step 6: 比較 PEFT 方法

這一步的產出是「方法選擇判斷」。LoRA 很常是第一選擇,但 adapter、prompt tuning、prefix tuning 和 IA³ 都是在不同限制下解同一類問題。

如果你要一個品質與部署平衡都不錯的預設方案,先選 LoRA。若你想把更新量再壓低,可以考慮 prompt tuning 或 prefix tuning;若你偏好模組化架構,adapter 會更直觀。

你現在應該能解釋 PEFT 為什麼有效:多數任務只需要調整模型行為,不需要重寫整個模型。這就是它能在有限算力下完成 LLM 微調的原因。

指標基準/優化前結果/優化後
可訓練參數7B 全量微調約 5M 到 20M,LoRA 套在 7B 模型
可訓練參數13B 全量微調約 10M 到 40M,LoRA 套在 13B 模型
可訓練參數70B 全量微調約 50M 到 200M,LoRA 套在 70B 模型
Adapter 大小完整模型 checkpoint常見 production 情境下約 50 MB 到 200 MB

常見錯誤

  • 忘記凍結 base model。修法:在訓練前檢查 pretrained weights 的 requires_grad=False
  • 選錯 target modules。修法:先看模型結構,確認 attention projection 的實際名稱,再套用 LoRA。
  • base 與 adapter 版本不一致。修法:adapter 一律掛回訓練時使用的同一個 base model family 與 revision。

接下來可以看什麼

如果你已經能順利跑 LoRA,下一步可以看 adapter merging、QLoRA 的量化微調,以及把 adapter-only 模型和 full fine-tuning 在同一組任務上做評估比較。