6.3 LoRA 与 QLoRA
LoRA 公式、关键超参、QLoRA 的 NF4 量化、DoRA / VeRA 简介与显存对比
为什么需要参数高效微调
对一个 8B 参数的模型做全参数微调,显存开销大致如下:
| 组件 | 计算方式 | 显存 |
|---|---|---|
| 模型参数(FP16) | 8B × 2 bytes | 16 GB |
| 梯度(FP16) | 8B × 2 bytes | 16 GB |
| 优化器状态(AdamW FP32 动量 + 方差) | 8B × 8 bytes | 64 GB |
| 激活值(batch=4, seq=2048 估计) | — | 4-16 GB |
| 合计 | ~100-112 GB |
一张 A100-80G 都跑不动。优化器状态是最大的开销——它本身就比参数大 4 倍。
参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)的核心思路:
冻结大部分原始参数,只训练一小部分"新增的低秩"参数——梯度和优化器状态只要在这一小部分上维护,显存开销立刻降一个数量级。
LoRA:低秩适配
核心假设
LoRA(Low-Rank Adaptation, Hu et al. 2021)基于一个被反复验证的经验观察:
预训练模型在做下游任务微调时,权重的更新量具有低秩结构。
换言之, 的本征维度远低于 本身的维度。
数学形式
对预训练权重矩阵 ,LoRA 不直接更新 ,而是把更新量 分解为两个低秩矩阵的乘积:
其中:
- ,初始化为全零
- ,用 Kaiming 初始化
- 是秩(rank),通常取 8-64
- 冻结,不计算梯度
- 只有 参与训练
实际前向传播加上缩放因子:
其中 是缩放因子(alpha), 控制了 LoRA 更新的幅度。
参数效率
以 Qwen2.5 里一个典型的 attention 线性层()为例:
| 方法 | 可训参数 | 相对占比 |
|---|---|---|
| 全参数微调 | 100% | |
| LoRA(r=8) | 0.78% | |
| LoRA(r=32) | 3.1% | |
| LoRA(r=64) | 6.3% |
关键超参数
秩 r 决定 LoRA 的表达能力:
r = 4-8:简单任务(风格微调、格式遵循),参数少、训练快r = 16-32:通用起点,适用于指令跟随、领域适配r = 64-128:复杂任务(大规模领域迁移、多语言),接近全参数但仍省显存
经验法则:先用 r=32 跑通,根据效果和资源再调整。
alpha () 与 共同决定缩放:
社区共识:
alpha = 2 * r(如 r=32, α=64)是 Qwen / Llama 推荐默认alpha = r:更小的更新幅度,训练更稳但收敛慢- 先固定 ,通过调
learning_rate来控制训练强度——这比调 alpha 更直观
LoRA 可以挂在 Transformer 里任何线性层上。优先级:
| 模块 | 作用 | 优先级 |
|---|---|---|
q_proj / k_proj / v_proj / o_proj | Attention 四个投影 | 最高 |
gate_proj / up_proj / down_proj | FFN 三个投影(SwiGLU) | 高 |
lm_head | 输出头 | 低(一般不挂) |
embed_tokens | 词嵌入 | 低(仅新增大量词汇时挂) |
推荐:挂在所有 attention + FFN 线性层上(7 个)。实验反复表明这比只挂 q_proj + v_proj(原始论文的选择)效果好,增加的参数也不多。
LoRA dropout 对低秩矩阵 的输出做 dropout,防止过拟合:
0.0:数据量大(>10K)时用0.05:通用默认0.1:数据量小(<1K)或数据噪声大时用
PEFT 配置代码
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=32,
lora_alpha=64, # α = 2r
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 典型输出:trainable params: 18M || all params: 1.5B || trainable%: 1.2QLoRA:量化 + 低秩适配
QLoRA(Dettmers et al. 2023)是工业界部署最广的 PEFT 方案。它在 LoRA 之上做了三件事:
- 4-bit NF4 量化:把冻结的 从 FP16 量化到 4-bit,显存再减半
- 双重量化(Double Quantization):对量化常数本身再做一次量化,额外省 ~0.4 bit/参数
- 分页优化器(Paged Optimizers):用 NVIDIA 的统一内存,把优化器状态临时页交换到 CPU 内存,避免 OOM
NF4 量化的关键观察
预训练模型的权重近似服从正态分布。NF4(NormalFloat 4-bit)把正态分布的分位数作为量化级别——在信息论意义上,这是 4-bit 量化的理论最优。
计算时再反量化到 BF16:
BitsAndBytesConfig 配置
from transformers import BitsAndBytesConfig, AutoModelForCausalLM
import torch
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # 启用 4-bit 加载
bnb_4bit_quant_type="nf4", # NF4(优于 FP4)
bnb_4bit_compute_dtype=torch.bfloat16, # 计算时反量化到 BF16
bnb_4bit_use_double_quant=True, # 双重量化
)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-1.5B",
quantization_config=bnb_config,
device_map="auto",
trust_remote_code=True,
)QLoRA 的代价:反量化发生在每一次前向传播上,训练速度比纯 LoRA 慢 15-30%。如果显存够(A100-40G 以上),优先用纯 LoRA;显存吃紧才用 QLoRA。
DoRA / VeRA(一笔带过)
DoRA:权重分解 LoRA
DoRA(Weight-Decomposed LoRA, 2024)把权重分解为幅度(magnitude)和方向(direction):
是一个可训练的每列幅度向量。DoRA 在同参数预算下通常比 LoRA 好 0.5-2 个百分点,计算开销略增。PEFT 库支持:use_dora=True。
VeRA:共享随机矩阵
VeRA(Vector-based Random Matrix Adaptation, 2023)把 设为跨层共享的随机矩阵(冻结),只训练两个小的缩放向量。参数量比 LoRA 还少一个数量级,适合极端资源受限场景。
选型建议:入门和工业主流仍然是 LoRA / QLoRA。DoRA 适合追求最后一两个百分点的场景;VeRA 适合部署多个适配器的服务(每个适配器只有几 MB)。
显存对比
以 Qwen2.5-7B 为例,不同微调方法的显存对比(batch=4, seq=2048, r=32):
| 方法 | 模型加载 | 可训参数 + 梯度 | 优化器状态 | 激活 | 总显存 | GPU 需求 |
|---|---|---|---|---|---|---|
| 全参数(FP16) | 14 GB | 14 GB | 56 GB | ~10 GB | ~94 GB | 2× A100-80G |
| LoRA(BF16, r=32) | 14 GB | 80 MB | 320 MB | ~10 GB | ~25 GB | 1× A100-40G / RTX 4090 |
| QLoRA(NF4, r=32) | 4 GB | 80 MB | 320 MB | ~10 GB | ~15 GB | 1× RTX 3090 / T4-16G |
显存经验法则(7B 模型):
- 全参数微调:~100 GB(至少 2 张 A100-80G)
- LoRA(BF16):~25 GB(A100-40G 或 RTX 4090)
- QLoRA(NF4):~15 GB(一张消费级 24G / 16G 也能跑)
QLoRA 真正把大模型微调搬到了消费级 GPU 上。
实践默认值(抄这张表)
| 决策 | 推荐 | 说明 |
|---|---|---|
| 全参 vs PEFT | PEFT(LoRA/QLoRA) | 除非资源充裕 + 数据极多 |
| LoRA vs QLoRA | QLoRA(显存紧)/ LoRA(A100-40G+) | 纯 LoRA 快 15-30% |
| 秩 r | 32 | 起点,按需 8-64 |
| alpha | 2 × r | 即 α=64 when r=32 |
| target_modules | all attention + FFN 共 7 层 | 已被反复验证 |
| dropout | 0.05 | 数据少加到 0.1 |
| 学习率 | 2e-4(PEFT) / 2e-5(全参) | PEFT 可以大 10 倍 |
| 精度 | BF16 | 比 FP16 稳定,A100/H100 原生 |
本节小结
| 概念 | 要点 |
|---|---|
| LoRA 公式 | , 冻结 |
| 初始化 | 全零、 Kaiming,保证开始时 |
| 秩 r | 8-64,通用起点 32 |
| alpha | 默认 ,用 lr 调训练强度 |
| 目标模块 | 所有 attention + FFN(共 7 个),别只挂 q/v |
| QLoRA | NF4 量化 + 双重量化 + paged optimizer |
| DoRA | 幅度 + 方向分解,略优于 LoRA |
| 显存对比 | 7B 全参 100GB → LoRA 25GB → QLoRA 15GB |