微调贵的不在算法本身,而在你有没有把「改多少参数、占多少显存」这件事算清楚。全参微调、LoRA、QLoRA 三个名字听起来都像银弹,但真正决定选哪个的,往往是你手里的显存、数据量和任务离预训练分布有多远。这篇把它们放在同一把尺子上。
全参微调贵在哪
全参微调的意思是模型里每一个可训练权重都参与更新。训练时不只要存权重,还要存梯度、优化器状态和激活。粗算一笔 bf16 下的账:7B 权重约 14GB,梯度约 14GB,AdamW 优化器给每个参数维护一阶、二阶两个动量、又是约 28GB,加上激活,全参训练态轻松冲到 64GB 上下。很多人以为「模型文件 14G,我卡 24G 肯定够」,那是把推理显存误当成了训练显存。激活还随 batch 和序列长度线性涨,长上下文时经常反超权重本身。所以全参微调适合数据充足、领域分布和预训练差得远、又有多卡预算的团队,单卡硬上是账没算完。
LoRA:低秩补丁
LoRA(Low-Rank Adaptation)的假设很直接:微调的权重更新 ΔW 具有低秩特性,有意义的更新集中在少数方向上。于是它把 ΔW 拆成两个瘦矩阵的乘积 B×A,A 是 r×d、B 是 d×r,秩 r 通常取 8 到 32。原来动几亿参数,现在只动几百万。用 Hugging Face 的 PEFT 挂一层是这样:
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B-Instruct",
torch_dtype="auto",
device_map="auto",
)
lora = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora)
model.print_trainable_parameters()
跑完 print_trainable_parameters 你会看到可训练参数降到 1% 以下。两个工程红利:适配器文件只有几十 MB,方便做版本管理、坏了秒回滚;推理时可以把 B×A 合并回原权重,几乎不增加延迟,也可以不合并、一个底座按需热切换多套业务适配器。
QLoRA:4bit 底座再挂 LoRA
QLoRA 可以理解成「先把冻住的底座压成 4bit,再贴 LoRA 补丁」。底座用 NF4 这类对正态分布友好的量化格式存进显存,前向时反量化参与计算,真正训练的仍是那层补丁,且补丁保持较高精度。它省的是存底座的显存,让单张 24G 卡摸到 13B 甚至更大模型成为现实。配置上在加载模型时加一段量化:
from transformers import BitsAndBytesConfig
from peft import prepare_model_for_kbit_training
bnb = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype="bfloat16",
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B-Instruct",
quantization_config=bnb,
device_map="auto",
)
model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, lora)
代价是量化引入信息损失、训练速度通常慢于同配置的 LoRA,调试时多一个量化变量。它解决的是「底座太大塞不进卡」,不是「随便怎么配都永不 OOM」——上下文一开到 8k、16k,激活照样把卡撑爆。
选型看数据与预算
| 条件 | 推荐 |
|---|---|
| 数据 100k,追求能力上限 | 全参微调 |
| 显存极紧 / 想摸更大底座 | QLoRA |
三种方法不是谁取代谁,而是一条成本-能力权衡线:全参微调买上限,LoRA 买工程效率,QLoRA 买可及性。多数团队的正确姿势是先用 LoRA 建基线,评估找到瓶颈后先加数据、再提 rank、最后才考虑全参微调。
常见误区
第一个误区是把全参微调当「效果更好的默认项」,小数据上它常输给 LoRA,因为低秩的归纳偏置更贴近「只学变化量」。第二个误区是 rank 无脑拉高,r 超过任务内在维度后只有成本没有收益,数据几千条时过大 r 等于给噪声开更多通道。第三个误区是只盯 loss——loss 下降不等于业务指标上升,得有一套固定评测集,每次改 r、改层、改学习率都跑同一套。第四个误区是 target_modules 抄错架构,trainable% 低得离谱等于空转,LLaMA 系叫 q_proj/v_proj,有的模型叫 query_key_value,抄之前先核对一层。最后一个误区是以为 QLoRA 万能,它省的是底座显存,不是激活,序列长度照样是天花板。
收尾
一句话:先算显存账,再选粒度。能贴补丁就别重刷墙,补丁还塞不下,再谈把墙压成 4bit。每次改 r、改层、改学习率,都该有同一套评测集的数字背书,而不是听说隔壁全参了就跟着上。