Skip to content

参数高效微调:LoRA、QLoRA 与 Adapter ​

标签
AI/infra/微调
AI/infra/显存管理
字数
3093 字
阅读时间
13 分钟

训练大模型有两条正交的省资源路线:分布式(多切几张卡,见 02-分布式训练总论与显存账本)与只训一小部分参数(PEFT)。

这一篇讲后者 —— 它的核心假设只有一句:微调不需要更新所有参数。

全量微调的显存账 ​

组成部分BF16 下的显存7B70B
模型参数2N14 GB140 GB
梯度2N14 GB140 GB
Adam 两个状态(m、v)8N(FP32)56 GB560 GB
激活值随 batch / 序列长度约 10–30 GB约 100–300 GB
合计约 12N + 激活约 100 GB约 1 TB

这张表少了一项

来源给的是 12N,漏了 FP32 主权重(4N)。完整账本是 16N —— 因为低精度权重上过小的更新会被直接舍掉,优化器必须保留一份 FP32 副本(推导见 05-优化器与显存开销)。

两处口径的差异就这一行,引用时注意。这里保留来源的 12N 而把完整账本指过去,不覆盖任何一方。

无论按哪个口径,结论一样:一块 A100 80 GB 连全量微调 7B 都勉强,70B 要 16+ 块卡。

PEFT 的核心假设 ​

全量微调把参数从 W0 改成 W0+ΔW。PEFT 的假设是:

ΔW 具有低维结构 —— 它可以用远少于 N 个参数表示。

不同的 PEFT 方法,本质上是对 ΔW 的结构做了不同的假设。这句话是理解整个 PEFT 领域的钥匙。

理论依据来自 Aghajanyan et al.(2020):预训练模型微调时的内在维度(Intrinsic Dimensionality)远低于参数空间的实际维度 —— 数百万参数的模型,微调的有效自由度可能只有几千。

LoRA ​

核心假设:ΔW 低秩。

ΔW=BA,B∈Rd×r, A∈Rr×k, r≪min(d,k)

训练时 W0 冻结,只训 A 与 B:

h=W0x+ΔWx=W0x+BAx

两个实现细节 ​

细节做法为什么
初始化A 随机高斯,B 初始化为零保证训练开始时 ΔW=BA=0,模型行为与预训练完全一致,不会因随机扰动破坏已有能力
缩放因子实际用 αrBA,α 常取 r 或 2r控制 LoRA 更新的幅度。α=r 时缩放为 1
h=W0x+αrBAx

参数量(d=4096 的线性层) ​

方法可训练参数占比
全量微调16.7 M100%
LoRA(r=8)65 K0.39%
LoRA(r=16)131 K0.78%
LoRA(r=64)524 K3.1%

用在哪几层 ​

该文只在 WQ 与 WV 上应用(实验发现这就够了)。但后续实践表明:

在所有线性层上都应用("full LoRA")通常效果更好,尤其在复杂任务上。LLaMA-Factory 等工具默认对所有线性层应用。

这里的「所有线性层」指 Q/K/V/O 投影 + SwiGLU FFN 的三个投影(Wgate、Wup、Wdown)—— 见 12-FFN 与激活函数。

推理时合并:零额外开销 ​

W=W0+αrBA

合并后的模型与全量微调在推理时完全等价 —— 相同架构、相同计算量、相同速度。所以 LoRA 是一个「训练省显存、推理零代价」的方案。

再加一条工程优势:可以为不同任务训不同的 adapter,按需加载与切换 —— 每个只要几十到几百 MB,不必存整份模型副本。

低秩约束不只是省参数,还起正则化作用。 它限制了模型的更新自由度,降低小数据集上的过拟合风险 —— 这也是 LoRA 在数据量少时往往优于全量微调的原因之一。

r 怎么选 ​

任务r
简单(情感分类、格式调整)8
中等(指令微调、对话)16 – 32
复杂(数学推理、代码生成)64 或更高

r 太小(1–4)适配能力不足;太大(256+)参数量逼近全量微调,PEFT 的优势就没了。

LoRA 把 ΔW 分解成一对低秩矩阵,挂在冻结的 W0 旁边:

                输入 x
                  │
        ┌─────────┴─────────┐
        ▼                   ▼
   ┌──────────┐        ┌──────────┐
   │ W₀       │        │ A (r×k)  │   随机高斯初始化
   │ 冻结不动  │        └────┬─────┘
   └────┬─────┘             ▼
        │             ┌──────────┐
        │             │ B (d×r)  │   初始化为零 ⇒ 训练开始时 ΔW = BA = 0
        │             └────┬─────┘      (模型行为与预训练完全一致)
        │                  │ 整体乘 α/r
        └────────┬─────────┘
                 ▼
      h = W₀x + (α/r)·BAx

  ⇒ 只训 A 与 B,W₀ 不动
  ⇒ r ≪ min(d, k):d = 4096 的层在 r = 8 时可训练参数只有原来的 0.39%
  ⇒ 推理时合并成 W = W₀ + (α/r)BA ⇒ 架构、计算量、速度与全量微调完全一致

QLoRA ​

LoRA 的漏洞:虽然只训少量参数,但前向传播仍需完整模型 —— 70B 的 W0 在 BF16 下仍要 140 GB。

QLoRA(Dettmers et al., 2023)把冻结的 W0 量化到 4-bit,在 4-bit 模型上训 LoRA。

三个关键创新 ​

创新内容
NF4(NormalFloat 4-bit)传统 4-bit 量化把值域均匀分 16 段;但预训练权重近似正态分布(大部分集中在零附近)。NF4 按正态分布的分位数定区间,使每段包含大致相同数量的权重,最小化量化误差
双重量化每个量化块要存一个 FP32 缩放因子。块大小 64 时开销 4/64=0.0625 字节/参数 —— 看着少,70B 上就是 4.4 GB。双重量化把缩放因子本身也量化到 8-bit,降到 0.0156 字节/参数
分页优化器利用 NVIDIA Unified Memory,让优化器状态在 GPU 显存与 CPU 内存间自动分页 —— 显存不足时换出,需要时换入

NF4 按正态分布的分位数定区间(与传统均匀分箱对照):

  传统 4-bit:把值域均匀切成 16 段

    ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
    │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
    └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
     ↑ 零附近被分到和两端一样多的「格位」,但那里的权重远比两端密

  NF4:按正态分布的分位数定区间 ⇒ 每段包含大致相同数量的权重

    ┌┬─┬──┬───┬────┬─────┬──────┬───────┬──────────┐
    └┴─┴──┴───┴────┴─────┴──────┴───────┴──────────┘
     密(零附近区间窄)                        疏(尾部区间宽)

  ⇒ 预训练权重近似正态分布,所以按分位数分箱比均匀分箱的量化误差更小

  双重量化:每个量化块还要存一个 FP32 缩放因子(块大小 64)
     ⇒ 4/64 = 0.0625 字节/参数 —— 70B 上就是 4.4 GB
     ⇒ 把缩放因子本身也量化到 8-bit ⇒ 降到 0.0156 字节/参数

显存对比 ​

配置7B70B
全量微调(BF16)约 100 GB约 1 TB
LoRA(BF16 模型)约 15 GB约 145 GB
QLoRA(NF4 + LoRA)约 6 GB约 48 GB

QLoRA 让单卡 24 GB(如 RTX 4090)微调 7B、单卡 A100 80 GB 微调 70B 成为可能。

效果与代价 ​

效果:该文报告 4-bit QLoRA 微调的效果与 16-bit 全量微调接近。Guanaco(QLoRA 微调 LLaMA-65B)在 Vicuna 基准上达到 ChatGPT 的 99.3%。

机制解释:量化损失被 LoRA 补偿了 —— LoRA 训练的参数是 BF16 精度的,它们能「修正」4-bit 量化引入的误差。

代价:

QLoRA 训练比 BF16 LoRA 慢 30–50%

4-bit 权重每次前向都要反量化回 BF16 —— 这就是它省显存的代价。显存够(比如多卡)时,BF16 LoRA 是更快的选择;QLoRA 的价值是「显存极其有限时仍能训」。

其他路线 ​

Adapter(Houlsby et al., 2019) ​

在 Transformer 每个子层之后插入一个小瓶颈网络:

Adapter(h)=h+f(hWdown)Wup

Wdown∈Rd×r 降维、f 是非线性激活、Wup∈Rr×d 升回。残差连接确保不破坏原始信息流 —— 与 13-归一化与残差连接 里那条「恒等通道」是同一个设计。

与 LoRA 的差别:Adapter 改结构(新增模块),LoRA 改权重(在既有层上加增量)。前者推理时有额外计算,后者没有。

Prompt-based:软提示 ​

方法做法
Prefix-Tuning(Li & Liang, 2021)在每层 Self-Attention 里前置一组可学习的虚拟 token,扩 K/V:K′=[Kprefix;K]、V′=[Vprefix;V]。真实 token 可以「关注」这些前缀,前缀充当任务指示
P-Tuning v2(Liu et al., 2022)在每一层都加可学习 prefix(而非只在输入层),并在更多任务类型上验证

为什么式微:

  • 有效容量有限 —— prefix 通常只有 10–100 个 token,能编码的任务信息有限
  • 占上下文窗口 —— 推理时减少可用的输入长度
  • 效果不及 LoRA —— 在大模型上几乎全面落后

综合对比 ​

方法可训练参数推理额外开销显存效果现状
全量微调100%无极高最好(上限)有资源时首选
LoRA0.1–3%零(可合并)中接近全量当前主流
QLoRA0.1–3%零(合并后)极低接近 LoRA显存受限时首选
Adapter0.5–5%有(额外层)中接近全量已被 LoRA 取代
Prefix-Tuning< 0.1%有(增序列长)低低于 LoRA已少用

选型只看一个问题:显存够不够。

显存充足(多卡 / A100 / H100) → 全量微调(效果上限)
显存有限但够放 BF16 权重      → LoRA(效果与速度的平衡)
单卡 ≤ 24 GB                 → QLoRA(NF4 + LoRA)

实践 ​

超参数 ​

参数推荐说明
lora_rank8 – 64简单任务小、复杂任务大
lora_alpha2r 或 r控制更新缩放
lora_dropout0.05 – 0.1正则化
target_modules所有线性层q/k/v/o_proj + gate/up/down_proj
learning_rate1×10−4 – 5×10−4比全量微调高一个量级
epochs2 – 5数据少时可多跑几轮

变体 ​

变体改动
DoRA把权重分解为幅度 + 方向,只对方向部分用 LoRA
LoRA+对 A 与 B 用不同学习率(B 更高)
rsLoRA缩放因子从 α/r 改成 α/r —— 让不同 rank 的训练更稳定,换 r 时不必重调 α
AdaLoRA动态分配 rank —— 重要的层给高 rank,不重要的给低 rank 甚至剪枝

多任务与组合 ​

一个基础模型训多个 LoRA(中文对话 / 代码 / 医疗…),推理时按需加载 —— 基础模型只存一份。

更进一步,多个 LoRA 可以线性组合:

W=W0+w1ΔW1+w2ΔW2

不重新训练就能组合多个能力。

这条与 06-vLLM 部署、参数与服务特性 里的 Multi-LoRA 服务是同一件事的两端:训练侧产出多个 adapter,服务侧在同一个引擎里动态加载切换。LoRA 的「可组合」是它区别于其他 PEFT 方法的工程价值。

工具 ​

工具特点
PEFT(Hugging Face)LoRA / QLoRA / Prefix-Tuning,与 Transformers 深度集成
LLaMA-Factory全流程(SFT / RLHF / DPO)+ Web UI
Unsloth自定义 Triton kernel,号称提速 2–5×
AxolotlYAML 配置驱动,多数据格式
TRL(Hugging Face)RLHF / DPO,与 PEFT 结合

相关 ​

参考 ​

该篇的「12N」口径漏了 FP32 主权重一项,本笔记按 16N 补全并保留了来源口径;部分公式在原站转换中退化,已按原论文重建

贡献者 ​

文件历史 ​