先看 Boss:全參數巨人
想像要教一個已經讀完醫學院的畢業生「如何做病歷去識別化」。你會把整套醫學院課程重教一遍嗎?當然不會,只要幾週的專項訓練。
模型也是一樣。Gemma 4 E2B 這類模型本身有數十億個參數(作者實測載入時顯示總計約 46.3 億,包含大量給每一層用的 embedding)。如果每次微調都要更新全部參數,記憶體要裝的東西包含:模型本身、每個參數的梯度(gradient,告訴你該往哪調)、優化器狀態(optimizer state,例如 Adam 的動量)。總量會是模型本身的好幾倍。這就是全參數巨人(full fine-tuning):筆電裝不下。
LoRA:只改一點點
LoRA(Low-Rank Adaptation,低秩適配)的核心想法是:原本的權重凍結,不動;旁邊加兩個很小的矩陣 A 和 B,只訓練這兩個。
假設某一層的權重是一個 d × k 的大矩陣 W。全參數微調要更新 d × k 個數字。LoRA 改成:
- A:d × r 的小矩陣
- B:r × k 的小矩陣
- 推論時用 W + (A × B) 的結果
其中 r 就是 rank(秩),通常只有 8、16、32 這種小數字。可訓練的參數量是 r × (d + k)。
用實際數字感受一下:一個 2048 × 2048 的矩陣,原本有 4,194,304 個數字。rank 8 的 LoRA 只有 8 × (2048 + 2048) = 32,768 個,約 0.78%。
作者在 M5 32GB 的實測(mlx-lm 0.32 預設設定:rank 8、只在最後 16 層加 LoRA;Gemma 4 E2B 4-bit):可訓練參數 6,815,744(約 6.8M),總參數 4,628M,約 0.147%。可以看到「只訓練很小一部分」是真的。Gemma 4 E2B 後段的層共用前面層的 KV,這些層沒有 k_proj / v_proj,所以也沒有對應的 LoRA;逐項明細見下方計算器。Colab notebook 用的是 Unsloth 的 r = 16,並同時套在注意力與 MLP 模組上,設定與 Mac 不同,數字不能直接相比;用 model.print_trainable_parameters() 會印出實際數字【實測數據待補】。
為什麼這樣有效?直覺上,微調要學的是「在原本能力上做小幅調整」,這種調整往往可以用低秩(資訊量很少)的方式表達。LoRA 的原始論文就是這個假設。
類比:
- pre-training(預訓練) 像醫學院:讀海量資料、打下通識。
- SFT(supervised fine-tuning,監督式微調) 像住院醫師訓練:用標好答案的例子教它做某類特定工作。
- LoRA 是做 SFT 的一種省資源方式,像在既有訓練上加修一個次專科,不必重讀醫學院。
這是教學用的比喻,不是嚴格對應,例如 LoRA 之後可以拆掉 adapter 回到原本的模型,現實中的訓練就沒辦法這麼乾脆。
QLoRA:底座壓縮成 4-bit
QLoRA 是在 LoRA 的基礎上,把凍結的底座模型先量化(quantize)成 4-bit,再接上 LoRA adapter。底座不需要被更新,壓縮之後對記憶體的負擔大幅降低。Unsloth 官方文件說明 QLoRA 可省下約 75% 的記憶體(截至 2026-10,出處見 Unsloth fine-tuning guide)。
作者在 Mac 上用 gemma-4-E2B-it-MLX-4bit 做的就是 QLoRA(4-bit 底座 + LoRA)。
但要記得兩件事:
- 量化會損失一點精度。對多數任務影響不大,但有些模型不建議這麼做。例如 Unsloth 文件指出 Qwen3.5 不建議 QLoRA,建議用 16-bit 的 LoRA(截至 2026-10)。選模型前先查最新建議。
- Colab 免費版的 T4 沒有 bf16(一種常用的 16-bit 格式),只能用 fp16。本站 notebook 預設
load_in_4bit = False,若遇到 CUDA out of memory 再改成True。
超參數:旋鈕有哪些
| 參數 | 意義 | 類比 |
|---|---|---|
| rank (r) | adapter 的容量 | 次專科訓練的深度 |
| alpha | LoRA 輸出的縮放係數,常設為與 rank 相同或 2 倍 | 這份次專科訓練影響日常判斷的權重 |
| learning rate | 每次調整的步伐 | 修正診斷偏差時的力道 |
| epoch | 完整讀過一遍訓練資料的次數 | 同一份病例集複習幾輪 |
| batch size | 一次看幾筆再調整 | 一次討論幾個病人再更新作法 |
| gradient accumulation | 累積幾個小 batch 才更新 | 記憶體不夠時,分幾次看再一起結算 |
文獻中的設定可作參考,但不要照抄:
- 澳洲皮膚病理報告研究用 rank 8、alpha 16、3 epochs、4-bit(PLOS Digit Health,PMID 42424371)。
- 法國 Bordeaux 急診病歷去識別化研究用 rank 32、alpha 64(JMIR AI,PMID 40605780)。
兩者任務、模型大小、資料量都不同,數字只是「別人這樣設過」。Unsloth 對初學者的建議是 1–3 個 epoch,並留約 20% 的資料做測試。若訓練 loss 一路逼近 0,通常是死背的訊號(下一關會看到曲線)。
互動道具:LoRA Rank 計算器
下面這個計算器可以輸入 hidden size、層數、要套用 LoRA 的模組與 rank,看可訓練參數量與占整體的比例。試試看:rank 從 8 調到 64,可訓練參數量如何變化?
LoRA 不改動原模型,只在選定的矩陣旁訓練兩個小矩陣 A(r × 輸入維度)與 B(輸出維度 × r)。 每個被套用的矩陣新增 r × (d_in + d_out) 個參數。
- 可訓練參數
- 1.57 M
- 佔全模型
- 0.034%
- Adapter 檔案(fp16)
- 3.1 MB
實測對照
作者在 M5 32GB 的實測(mlx-lm 0.32 預設:rank 8、只訓練最後 16 層,Gemma 4 E2B 4-bit):可訓練 6.8M / 4,628M = 0.147%。 上面的快速估算假設每個矩陣都是 hidden × hidden 的方陣,只適合抓數量級。下表是從實際存下的 adapter 檔讀出的真實矩陣尺寸,用 rank 8 逐項相加:
| 矩陣 | 輸入 → 輸出 | 個數 | 參數 |
|---|---|---|---|
q_proj(局部注意力層) | 1536 → 2048 | 12 | 344,064 |
q_proj(全域注意力層) | 1536 → 4096 | 4 | 180,224 |
o_proj(局部注意力層) | 2048 → 1536 | 12 | 344,064 |
o_proj(全域注意力層) | 4096 → 1536 | 4 | 180,224 |
gate_proj | 1536 → 12288 | 16 | 1,769,472 |
up_proj | 1536 → 12288 | 16 | 1,769,472 |
down_proj | 12288 → 1536 | 16 | 1,769,472 |
per_layer_input_gate | 1536 → 256 | 16 | 229,376 |
per_layer_projection | 256 → 1536 | 16 | 229,376 |
| 合計 | 6,815,744 | ||
rank 8 時合計 6,815,744,與訓練紀錄完全一致。沒有 k_proj / v_proj,是因為 Gemma 4 E2B 後段的層共用前面層的 KV;MLP 寬度 12,288 也是這些層特有的設計。換別的模型時,維度請看該模型的 config.json。
🍎 Mac 與 ☁️ Colab 實作
🍎 Mac:用計算器試算 Gemma 4 E2B 在你的 Mac 上要用什麼設定。作者在 32GB 上實測 batch 4、max seq 2048、開 gradient checkpointing,peak memory footprint 為 14.8GB。16GB Mac 的建議降配見下一關(標示【待實測】)。
☁️ Colab:在 notebook 的 get_peft_model 之後加一行 model.print_trainable_parameters(),把印出來的數字與計算器的估計值對照。數字有差距是正常的,因為 Gemma 4 的結構與計算器的簡化假設不完全相同。
本關重點
- LoRA 凍結原權重,只訓練旁邊兩個低秩小矩陣 A、B,可訓練參數量為 r × (d + k),通常只占整體不到 1%。作者實測 Gemma 4 E2B 為 0.147%。
- QLoRA = 4-bit 量化的底座 + LoRA;省記憶體,代價是些許量化誤差,部分模型(如 Qwen3.5)官方不建議。
- pre-training、SFT、LoRA 的醫學教育類比有助記憶,但只是比喻。
- rank、learning rate、epoch 都有「過猶不及」;文獻設定只供參考,要用 valid 集自己驗證。