人工智能实践(语言智能)
第6讲:大模型微调

6.3 LoRA 与 QLoRA

LoRA 公式、关键超参、QLoRA 的 NF4 量化、DoRA / VeRA 简介与显存对比

为什么需要参数高效微调

对一个 8B 参数的模型做全参数微调,显存开销大致如下:

组件计算方式显存
模型参数(FP16)8B × 2 bytes16 GB
梯度(FP16)8B × 2 bytes16 GB
优化器状态(AdamW FP32 动量 + 方差)8B × 8 bytes64 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)基于一个被反复验证的经验观察:

预训练模型在做下游任务微调时,权重的更新量具有低秩结构

换言之,ΔW\Delta W 的本征维度远低于 W0W_0 本身的维度。

数学形式

对预训练权重矩阵 W0Rd×kW_0 \in \mathbb{R}^{d \times k},LoRA 不直接更新 W0W_0,而是把更新量 ΔW\Delta W 分解为两个低秩矩阵的乘积:

W=W0+ΔW=W0+BAW = W_0 + \Delta W = W_0 + BA

其中:

  • BRd×rB \in \mathbb{R}^{d \times r},初始化为全零
  • ARr×kA \in \mathbb{R}^{r \times k},用 Kaiming 初始化
  • rmin(d,k)r \ll \min(d, k)(rank),通常取 8-64
  • W0W_0 冻结,不计算梯度
  • 只有 A,BA, B 参与训练

实际前向传播加上缩放因子:

h=W0x+ΔWx=W0x+αrBAxh = W_0 x + \Delta W \, x = W_0 x + \frac{\alpha}{r} \cdot BA \, x

其中 α\alpha缩放因子(alpha),αr\frac{\alpha}{r} 控制了 LoRA 更新的幅度。

参数效率

以 Qwen2.5 里一个典型的 attention 线性层(d=k=2048d = k = 2048)为例:

方法可训参数相对占比
全参数微调dk=4,194,304d \cdot k = 4{,}194{,}304100%
LoRA(r=8)dr+rk=32,768d \cdot r + r \cdot k = 32{,}7680.78%
LoRA(r=32)dr+rk=131,072d \cdot r + r \cdot k = 131{,}0723.1%
LoRA(r=64)dr+rk=262,144d \cdot r + r \cdot k = 262{,}1446.3%

关键超参数

秩 r 决定 LoRA 的表达能力:

  • r = 4-8:简单任务(风格微调、格式遵循),参数少、训练快
  • r = 16-32通用起点,适用于指令跟随、领域适配
  • r = 64-128:复杂任务(大规模领域迁移、多语言),接近全参数但仍省显存

经验法则:先用 r=32 跑通,根据效果和资源再调整

alpha (α\alpha)rr 共同决定缩放:

实际缩放=αr\text{实际缩放} = \frac{\alpha}{r}

社区共识:

  • alpha = 2 * r(如 r=32, α=64)是 Qwen / Llama 推荐默认
  • alpha = r:更小的更新幅度,训练更稳但收敛慢
  • 先固定 α=2r\alpha = 2r,通过调 learning_rate 来控制训练强度——这比调 alpha 更直观

LoRA 可以挂在 Transformer 里任何线性层上。优先级:

模块作用优先级
q_proj / k_proj / v_proj / o_projAttention 四个投影最高
gate_proj / up_proj / down_projFFN 三个投影(SwiGLU)
lm_head输出头低(一般不挂)
embed_tokens词嵌入低(仅新增大量词汇时挂)

推荐:挂在所有 attention + FFN 线性层上(7 个)。实验反复表明这比只挂 q_proj + v_proj(原始论文的选择)效果好,增加的参数也不多。

LoRA dropout 对低秩矩阵 AA 的输出做 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.2

QLoRA:量化 + 低秩适配

QLoRA(Dettmers et al. 2023)是工业界部署最广的 PEFT 方案。它在 LoRA 之上做了三件事:

  1. 4-bit NF4 量化:把冻结的 W0W_0 从 FP16 量化到 4-bit,显存再减半
  2. 双重量化(Double Quantization):对量化常数本身再做一次量化,额外省 ~0.4 bit/参数
  3. 分页优化器(Paged Optimizers):用 NVIDIA 的统一内存,把优化器状态临时页交换到 CPU 内存,避免 OOM

NF4 量化的关键观察

预训练模型的权重近似服从正态分布。NF4(NormalFloat 4-bit)把正态分布的分位数作为量化级别——在信息论意义上,这是 4-bit 量化的理论最优

WNF4=QuantizeNF4(W0)W0W_{\text{NF4}} = \text{Quantize}_{\text{NF4}}(W_0) \approx W_0

计算时再反量化到 BF16:

h=Dequant(WNF4)x+αrBAxh = \text{Dequant}(W_{\text{NF4}}) \, x + \frac{\alpha}{r} BA \, x

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):

W=mW0+BAW0+BAcW = m \cdot \frac{W_0 + BA}{\lVert W_0 + BA \rVert_c}

mm 是一个可训练的每列幅度向量。DoRA 在同参数预算下通常比 LoRA 好 0.5-2 个百分点,计算开销略增。PEFT 库支持:use_dora=True

VeRA:共享随机矩阵

VeRA(Vector-based Random Matrix Adaptation, 2023)把 A,BA, B 设为跨层共享的随机矩阵(冻结),只训练两个小的缩放向量。参数量比 LoRA 还少一个数量级,适合极端资源受限场景。

选型建议:入门和工业主流仍然是 LoRA / QLoRA。DoRA 适合追求最后一两个百分点的场景;VeRA 适合部署多个适配器的服务(每个适配器只有几 MB)。

显存对比

Qwen2.5-7B 为例,不同微调方法的显存对比(batch=4, seq=2048, r=32):

方法模型加载可训参数 + 梯度优化器状态激活总显存GPU 需求
全参数(FP16)14 GB14 GB56 GB~10 GB~94 GB2× A100-80G
LoRA(BF16, r=32)14 GB80 MB320 MB~10 GB~25 GB1× A100-40G / RTX 4090
QLoRA(NF4, r=32)4 GB80 MB320 MB~10 GB~15 GB1× 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 PEFTPEFT(LoRA/QLoRA)除非资源充裕 + 数据极多
LoRA vs QLoRAQLoRA(显存紧)/ LoRA(A100-40G+)纯 LoRA 快 15-30%
秩 r32起点,按需 8-64
alpha2 × r即 α=64 when r=32
target_modulesall attention + FFN 共 7 层已被反复验证
dropout0.05数据少加到 0.1
学习率2e-4(PEFT) / 2e-5(全参)PEFT 可以大 10 倍
精度BF16比 FP16 稳定,A100/H100 原生

本节小结

概念要点
LoRA 公式W=W0+αrBAW = W_0 + \frac{\alpha}{r} BAW0W_0 冻结
初始化BB 全零、AA Kaiming,保证开始时 ΔW=0\Delta W = 0
秩 r8-64,通用起点 32
alpha默认 α=2r\alpha = 2r,用 lr 调训练强度
目标模块所有 attention + FFN(共 7 个),别只挂 q/v
QLoRANF4 量化 + 双重量化 + paged optimizer
DoRA幅度 + 方向分解,略优于 LoRA
显存对比7B 全参 100GB → LoRA 25GB → QLoRA 15GB