LLaMA、Mistral、Qwen這些大語言模型動(dòng)輒數(shù)十億參數(shù),在自定義數(shù)據(jù)上全量微調(diào)代價(jià)極高:65B 模型光是 float16 就要消耗掉約 130GB 顯存,頂配硬件跑個(gè)幾天幾周很正常而多數(shù)下游任務(wù)根本用不到那么多參數(shù)。
于是研究者們開始思考:能不能只調(diào)一小部分參數(shù),效果還不差?答案是可以。這類方法統(tǒng)稱為參數(shù)高效微調(diào)(PEFT)。LoRA、QLoRA、DoRA 各自從不同角度切入這個(gè)問題。
![]()
LoRA——低秩自適應(yīng)
論文:LoRA: Low-Rank Adaptation of Large Language Models, Hu et al. (ICLR 2022)
核心思想
LoRA 的做法很簡(jiǎn)單,凍結(jié)原始權(quán)重矩陣 W在旁邊掛兩個(gè)小的可訓(xùn)練矩陣 A 和 B。前向傳播時(shí)的計(jì)算表達(dá)式為:
output = W·x + (B·A)·x × (alpha/r)
訓(xùn)練過程中只更新 A 和 B,W 保持不動(dòng)。這里的秩 r 是分解的瓶頸維度常見取值 4、8 或 16。alpha 是縮放因子控制 LoRA 更新的強(qiáng)度。
參數(shù)量的減少非常明顯以一個(gè) (4096, 4096) 的權(quán)重矩陣為例,原始參數(shù)量是 1670 萬換成 rank=8 的 LoRA 之后,A 的維度 (8, 4096) 有 32,768 個(gè)參數(shù),B 的維度 (4096, 8) 同樣 32,768 個(gè),加起來總共 65,536——比原來少了 99.6%。
LoRA 論文中一個(gè)關(guān)鍵發(fā)現(xiàn)是,微調(diào)過程中大部分有意義的權(quán)重更新本來就集中在低維子空間里,所以把更新約束在低秩矩陣上并不會(huì)造成多大損失。
微軟官方倉(cāng)庫(kù)中 Linear 層的實(shí)現(xiàn):
# Source: github.com/microsoft/LoRA — loralib/layers.py
class Linear(nn.Linear, LoRALayer):
def __init__(
self,
in_features: int,
out_features: int,
r: int = 0,
lora_alpha: int = 1,
lora_dropout: float = 0.,
merge_weights: bool = True,
**kwargs
):
nn.Linear.__init__(self, in_features, out_features, **kwargs)
LoRALayer.__init__(self, r=r, lora_alpha=lora_alpha,
lora_dropout=lora_dropout,
merge_weights=merge_weights)
if r > 0:
self.lora_A = nn.Parameter(
self.weight.new_zeros((r, in_features))
)
self.lora_B = nn.Parameter(
self.weight.new_zeros((out_features, r))
)
self.scaling = self.lora_alpha / self.r
# Freeze the pretrained weights
self.weight.requires_grad = False
def forward(self, x: torch.Tensor):
if self.r > 0 and not self.merged:
# Original frozen weights + low-rank update
result = F.linear(x, self.weight, bias=self.bias)
result += (
self.lora_dropout(x)
@ self.lora_A.transpose(0, 1)
@ self.lora_B.transpose(0, 1)
) * self.scaling
return result
else:
return F.linear(x, self.weight, bias=self.bias)
實(shí)際用于替換注意力投影的寫法:
# Source: github.com/microsoft/LoRA — README [1]
import loralib as lora
# Before: standard attention projection
# qkv_proj = nn.Linear(d_model, 3*d_model)
# After: apply LoRA to Q and V, freeze K
qkv_proj = lora.MergedLinear(
d_model, 3*d_model,
r=8,
enable_lora=[True, False, True] # Q=LoRA, K=frozen, V=LoRA
)
# Mark only LoRA parameters as trainable
lora.mark_only_lora_as_trainable(model)
基準(zhǔn)測(cè)試結(jié)果(來自 LoRA 論文 )
論文在 GPT-2(自然語言生成)和 RoBERTa/DeBERTa(GLUE 基準(zhǔn))上做了評(píng)測(cè)。下面是 GPT-2 在 E2E NLG Challenge 上的成績(jī),LoRA 與全量微調(diào)及其他 PEFT 方法的對(duì)比:
| Method | Trainable Params | BLEU | NIST | MET | ROUGE-L | CIDEr |
|-------------------|-----------------|------|------|------|----------|-------|
| Full Fine-Tuning | 117M | 68.2 | 8.62 | 46.2 | 71.0 | 2.47 |
| Adapter (Houlsby) | 1.0M | 66.3 | 8.41 | 45.0 | 69.8 | 2.40 |
| Prefix Tuning | 0.35M | 68.1 | 8.59 | 46.3 | 70.8 | 2.47 |
| LoRA (r=4) | 0.77M | 70.4 | 8.85 | 46.8 | 71.8 | 2.53 |
LoRA 不只是持平全量微調(diào)而是在訓(xùn)練不到 1% 參數(shù)的情況下反超了。論文把原因歸結(jié)于正則化效應(yīng):低秩約束本身起到了防止過擬合的作用。
關(guān)鍵超參數(shù)
秩 r 定義瓶頸維度,4 到 16 的范圍能覆蓋絕大多數(shù)場(chǎng)景,r 越大容量越高、參數(shù)也越多。Alpha(lora_alpha)是縮放因子,LoRA 更新的有效學(xué)習(xí)率等于 alpha/r一般設(shè) alpha=2r 是不錯(cuò)的起點(diǎn)。Target Modules 決定 LoRA 掛載到哪些層,注意力中的 q_proj 和 v_proj 是最常見的選擇。
QLoRA——量化 LoRA
論文:QLoRA: Efficient Finetuning of Quantized LLMs, Dettmers et al. (NeurIPS 2023)
LoRA 沒徹底解決的問題
LoRA 雖然減少了可訓(xùn)練參數(shù),但基礎(chǔ)模型還是得完整加載。65B 模型 float16 要 130GB 顯存通常得上多塊 A100,大部分研究者手里沒這個(gè)條件。
QLoRA 先把基礎(chǔ)模型量化成 4 位再在上面以 16 位跑 LoRA 適配器。
三大技術(shù)創(chuàng)新(來自 QLoRA 論文 )
QLoRA 論文同時(shí)引入了三項(xiàng)相互配合的技術(shù)。
第一項(xiàng)是 NF4(4-bit NormalFloat)一種專門針對(duì)正態(tài)分布權(quán)重設(shè)計(jì)的 4 位數(shù)據(jù)類型。神經(jīng)網(wǎng)絡(luò)權(quán)重天然服從正態(tài)分布NF4 從信息論角度是最優(yōu)的編碼方式,存儲(chǔ)效率上比 INT4 和 FP4 都要高出一截。
第二項(xiàng)是雙重量化,量化常數(shù)(把 4 位值轉(zhuǎn)換回浮點(diǎn)時(shí)用的常數(shù))本身也占內(nèi)存,QLoRA 干脆把量化常數(shù)也給量化了,65B 模型上約省 3GB 內(nèi)存。
第三項(xiàng)針對(duì)梯度檢查點(diǎn)帶來的內(nèi)存峰值,QLoRA 利用 NVIDIA 統(tǒng)一內(nèi)存機(jī)制,當(dāng)某條序列在 GPU 上觸發(fā) OOM 風(fēng)險(xiǎn)時(shí),自動(dòng)把優(yōu)化器狀態(tài)卸載到 CPU RAM等需要時(shí)再換回來。
代碼(來自 artidoro/qlora)
# Source: github.com/artidoro/qlora — qlora.py [2]
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
import torch
# Step 1: Define 4-bit quantization config (NF4)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NormalFloat4
bnb_4bit_compute_dtype=torch.bfloat16, # Compute in bf16
bnb_4bit_use_double_quant=True, # Double quantization
)
# Step 2: Load base model in 4-bit
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=bnb_config,
device_map="auto",
)
# Step 3: Prepare for k-bit training (handles frozen layer casting)
model = prepare_model_for_kbit_training(model)
# Step 4: Apply LoRA adapters in 16-bit on top
lora_config = LoraConfig(
r=64,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
整個(gè)過程中基礎(chǔ)模型權(quán)重始終保持 NF4(4 位、凍結(jié)狀態(tài))只有 LoRA 適配器權(quán)重以 bf16 精度參與訓(xùn)練。
基準(zhǔn)測(cè)試結(jié)果(來自 QLoRA 論文 )
QLoRA 訓(xùn)練出了 Guanaco 系列模型。在 GPT-4 評(píng)估的 Vicuna 基準(zhǔn)測(cè)試中,Guanaco 65B 用單塊 48GB GPU 訓(xùn)練了 24 小時(shí),就達(dá)到了 ChatGPT 性能的 99.3%。
更關(guān)鍵的一點(diǎn)是4 位量化相比 16 位 LoRA 幾乎沒有性能損失:
| Method | Model | Memory Usage | Vicuna Score vs ChatGPT |
|----------------------|-------|--------------|--------------------------|
| Full Fine-Tuning (fp16) | 65B | >780 GB | — |
| LoRA (fp16) | 65B | ~130 GB | — |
| QLoRA (NF4) | 65B | ~48 GB | 99.3% |
| QLoRA (NF4) | 33B | ~24 GB | 97.8% |
| QLoRA (NF4) | 7B | ~5 GB | ~87% |
7B Guanaco 模型只占 5GB 顯存在 Vicuna 基準(zhǔn)上卻以超過 20 個(gè)百分點(diǎn)的差距碾壓了 Alpaca 65B。這才是 QLoRA 真正革命性的地方:拿一塊普通 GPU 就能微調(diào)大規(guī)模語言模型門檻一下子降到了底。
DoRA——權(quán)重分解低秩自適應(yīng)
論文:DoRA: Weight-Decomposed Low-Rank Adaptation, Liu et al. (ICML 2024, Oral)
LoRA 表現(xiàn)不錯(cuò)但和全量微調(diào)之間始終有一道精度差距,當(dāng)秩取值較低時(shí)這個(gè)差距尤為明顯——LoRA 沒法完整復(fù)現(xiàn)全參數(shù)更新時(shí)的梯度學(xué)習(xí)行為。
問題出在哪?DoRA 論文提出了一種權(quán)重分解分析來回答這個(gè)問題。
核心洞察
任何權(quán)重矩陣 W 都可以拆成兩個(gè)分量:
W = m × (V / ||V||_c)
其中 m 是幅度分量,這個(gè)標(biāo)量向量反映每個(gè)輸出神經(jīng)元權(quán)重的"大小";V / ||V||_c 是方向分量,即權(quán)重的單位方向向量。
DoRA 論文發(fā)現(xiàn),全量微調(diào)時(shí)幅度和方向是以靈活、耦合的方式同步更新的。但 LoRA 的更新方式把兩者綁在一起,主要只改變了方向上的朝向反而限制了模型的學(xué)習(xí)能力。
DoRA 的做法是把兩者拆開:凍結(jié)分解結(jié)構(gòu)后,只讓 LoRA 作用于方向分量,同時(shí)把幅度 m 當(dāng)作獨(dú)立的可學(xué)習(xí)標(biāo)量自由更新。
W' = (m + Δm) × ((V + ΔV_LoRA) / ||V + ΔV_LoRA||_c)
這樣一來 LoRA 的學(xué)習(xí)模式就更接近全量微調(diào)了。并且由于幅度和方向在部署前可以合并回單個(gè)權(quán)重矩陣,推理階段零額外開銷。
代碼(來自 NVlabs/DoRA )
DoRA 的前向傳播邏輯,簡(jiǎn)化后展示核心分解機(jī)制:
# Source: github.com/NVlabs/DoRA — adapted from DoRA paper implementation [3]
import torch
import torch.nn as nn
import torch.nn.functional as F
class DoRALayer(nn.Module):
def __init__(self, d_in, d_out, rank, lora_alpha):
super().__init__()
# Frozen pretrained weight
self.weight = nn.Parameter(
torch.randn(d_out, d_in), requires_grad=False
)
# Learnable magnitude (one scalar per output neuron)
self.m = nn.Parameter(
self.weight.norm(p=2, dim=1, keepdim=True)
)
# LoRA matrices for directional updates (trainable)
std = 1 / torch.sqrt(torch.tensor(rank).float())
self.lora_A = nn.Parameter(torch.randn(d_in, rank) * std)
self.lora_B = nn.Parameter(torch.zeros(rank, d_out))
self.rank = rank
self.scaling = lora_alpha / rank
def forward(self, x):
# Compute the directional update from LoRA
lora_update = (self.lora_A @ self.lora_B).T * self.scaling
# Adapted weight = base weight + LoRA update
adapted = self.weight + lora_update
# Column-wise normalization → unit direction vectors
column_norms = adapted.norm(p=2, dim=1, keepdim=True)
V_normalized = adapted / column_norms
# Scale by learned magnitude
effective_weight = self.m * V_normalized
return F.linear(x, effective_weight)
通過 HuggingFace PEFT 啟用 DoRA(peft>=0.9.0 已支持 ):
# Source: HuggingFace PEFT documentation — DoRA is supported from peft>=0.9.0 [3]
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
use_dora=True, # ← This single flag enables DoRA
)
model = get_peft_model(model, lora_config)
就一個(gè)標(biāo)志位這是和 LoRA 配置的唯一區(qū)別。
基準(zhǔn)測(cè)試結(jié)果(來自 DoRA 論文 [3])
DoRA 論文在 8 個(gè)常識(shí)推理數(shù)據(jù)集(BoolQ、PIQA、HellaSwag、WinoGrande、ARC-e、ARC-c、OBQA 及綜合平均)上做了評(píng)測(cè),在 LLaMA-7B 和 LLaMA2-7B 兩個(gè)模型上與 LoRA 對(duì)比,秩相同、可訓(xùn)練參數(shù)量相同,確保公平:
| Method | BoolQ | PIQA | HellaSwag | WinoGrande | ARC-e | ARC-c | OBQA | Avg |
|--------------|-------|------|-----------|------------|-------|-------|------|-------|
| Full FT | 69.4 | 82.3 | 89.7 | 82.4 | 79.8 | 60.7 | 81.6 | 77.99 |
| LoRA (r=32) | 68.9 | 80.9 | 90.0 | 82.1 | 78.2 | 59.8 | 80.0 | 77.13 |
| DoRA (r=32) | 70.0 | 83.6 | 91.0 | 83.0 | 81.4 | 65.8 | 83.4 | 79.75 |
8 個(gè)數(shù)據(jù)集上 DoRA 全面勝出。差距在復(fù)雜推理任務(wù)上尤其明顯比如 ARC-c 從 LoRA 的 59.8 跳到 65.8,幅度相當(dāng)大。論文還跑了 LLaVA-1.5-7B 的視覺指令調(diào)優(yōu)和 VL-BART 的圖像/視頻-文本理解實(shí)驗(yàn),DoRA 同樣全面壓過 LoRA。
對(duì)比
內(nèi)存需求
| Method | LLaMA 7B | LLaMA 13B | LLaMA 33B | LLaMA 65B |
|-------------------------|----------|-----------|-----------|-----------|
| Full Fine-Tuning (fp16) | ~28 GB | ~52 GB | ~130 GB | ~260 GB |
| LoRA (fp16) | ~14 GB | ~26 GB | ~65 GB | ~130 GB |
| QLoRA (NF4) | ~5 GB | ~8 GB | ~20 GB | ~48 GB |
| DoRA (fp16) | ~14 GB | ~26 GB | ~65 GB | ~130 GB |
DoRA 因?yàn)槎嗔艘粋€(gè)幅度向量(每個(gè)目標(biāo)層的每個(gè)輸出神經(jīng)元一個(gè)標(biāo)量),會(huì)有少量額外開銷但實(shí)際可以忽略。
與全量微調(diào)的性能對(duì)比
| Method | Commonsense Reasoning | Instruction Tuning | Memory |
|-------------------|-----------------------|------------------------|------------|
| Full Fine-Tuning | Baseline | Baseline | Very High |
| LoRA | -0.86 avg | Comparable | Medium |
| QLoRA | ~Same as LoRA | 99.3% of ChatGPT | Low |
| DoRA | +2.62 avg over LoRA | Better than LoRA | Medium |
訓(xùn)練速度(相對(duì)值)
| Method | Speed |
|--------|------------------------------------------|
| LoRA | Fast |
| DoRA | Fast (near identical to LoRA) |
| QLoRA | Moderate (quantize/dequantize overhead) |
各場(chǎng)景下的最佳選擇
該用 LoRA 的場(chǎng)景
顯存 16GB 以上,模型不超過 13B 左右想要一個(gè)穩(wěn)定可靠、經(jīng)過實(shí)戰(zhàn)驗(yàn)證的方案。所有后續(xù)方法都建立在 LoRA 之上,HuggingFace 的 PEFT 生態(tài)圍繞它搭建,工具鏈最成熟。沒做過 PEFT 的人從這里入手最省心。
該用 QLoRA 的場(chǎng)景
顯存小但又想跑 30B 以上的大模型。QLoRA 讓單塊 48GB GPU 微調(diào) LLaMA-65B 成為現(xiàn)實(shí),24GB 顯存就能跑 LLaMA-33B。用免費(fèi) Colab T4(15GB 顯存)想動(dòng) 20B+ 模型的話,QLoRA 基本是唯一可行方案。
該用 DoRA 的場(chǎng)景
在跟 LoRA 一樣的參數(shù)預(yù)算下追求最高精度,DoRA 是 LoRA 的即插即用替代品配置里加個(gè) use_dora=True 就行推理零成本,并且在復(fù)雜推理任務(wù)上提升尤為突出。
該用 QLoRA + DoRA(QDoRA)的場(chǎng)景
既要省顯存又要高精度這種組合已獲官方支持,實(shí)驗(yàn)數(shù)據(jù)證明效果優(yōu)于單純的 QLoRA。部分早期實(shí)驗(yàn)甚至表明 QDoRA 能持平乃至超過全量微調(diào)。
總結(jié)
2025 年的微調(diào)生態(tài)分三層:LoRA 是底座,簡(jiǎn)單、快、生態(tài)好,多數(shù)場(chǎng)景夠用;QLoRA 打開了消費(fèi)級(jí)顯卡微調(diào)大模型的大門精度還沒怎么掉;DoRA 則是免費(fèi)升級(jí)——跟 LoRA 成本一樣但結(jié)果更好,use_dora=True 一行代碼就能搞定。
三種方法各有分工,互為補(bǔ)充,你唯一需要考慮的是哪種 PEFT 方案最貼合自己的硬件條件和精度要求。
https://avoid.overfit.cn/post/154732e0b0474f2ea8e2a4dac0d16819
by Harish K
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.