参数高效微调(PEFT)总论

1. 背景与动机

1.1 全参数微调的代价

给定预训练模型参数 ,全参数微调的目标是

梯度更新需对全部参数计算并存储:

  • 显存:梯度 与优化器状态(如 Adam 的 )需要占用 显存。对 7B 参数的模型,仅 Adam 状态就需要约 56 GB(FP32)。
  • 算力:前向+反向均需遍历全部参数,计算量与参数量线性成正。
  • 存储:每次微调需保存一套完整模型权重,7B 模型在 FP32 下约 28 GB。
  • 部署:每个下游任务需独立部署一套完整权重,无法共享底座。

1.2 PEFT 的核心思想

PEFT(Parameter-Efficient Fine-Tuning)的基本假设是:下游任务的适配信息可以被压缩到一小簇参数中。具体做法是:

  1. 冻结(freeze)大部分预训练参数 ,使其在训练中保持不变。
  2. 只训练少量新增参数 ,或对部分参数做低秩改造。

最终优化目标变为:

这使显存占用、计算量、存储成本均大幅下降,同时保留了对新任务的有效适配能力。


2. 从统一视角理解 PEFT

所有主流 PEFT 方法都可以看作是对模型架构的某个局部结构进行参数化改造,然后只训练这些新引入的结构。我们用下表统一刻画各方法修改的”位置”与”形式”:

方法修改的模型部位新增/修改参数的数学形式训练参数量级(以 7B 模型为例)
LoRA / QLoRA注意力权重矩阵 (部分或全部),其中 几 MB()至 几百 MB(
AdapterFFN 层之间插入瓶颈层,瓶颈维度 几十 MB
Prefix Tuning每层注意力机制的键/值序列可学习向量 拼接到原始 几 MB~几十 MB
Prompt Tuning输入 embedding 层可学习向量 拼接到输入序列前端几 MB
IA³激活值逐通道缩放< 1 MB

3. 核心方法详解

3.1 LoRA(Low-Rank Adaptation)

数学形式

设原始权重矩阵 ,前向传播为 。LoRA 将权重更新建模为:

其中 。训练时冻结 ,只更新 ,推理时将 合并为 ,实现零额外推理延迟。

为什么低秩有效?

直觉来自文献中观察到的现象:梯度矩阵在微调过程中往往本征维度较低。即:

这意味着参数更新方向可以由少量正交方向张成。从优化角度看,用低秩矩阵逼近真实梯度方向,是合理且高效的。

QLoRA

QLoRA(Quantized LoRA)在 LoRA 基础上引入NF4(4-bit NormalFloat)量化的基座模型,结合 LoRA 微调。典型配置:

  • 基座模型以 4-bit NF4 量化存储(约节省 4 倍空间)
  • LoRA 的 以 16-bit 或 32-bit 全精度训练
  • 精度对比:7B 模型全精度 ~28 GB → 4-bit QLoRA ~4 GB 加载 + LoRA adapter ~几百 MB

这使单卡(24 GB)微调 65B 模型成为可能。

3.2 Adapter

Adapter 在 Transformer 每层的 FFN 之后(有时在注意力之后)插入一个瓶颈结构:

其中瓶颈维度 通常取 。核心特性:

  • 逐层独立:每个 Adapter 独立训练,推理时要么串行计算(增加延迟),要么吸收进主网络。
  • 残差设计:恒等映射保证训练稳定性。

3.3 Prefix Tuning

Prefix Tuning 在每层注意力计算前,向 序列拼接可学习的前缀向量:

每个 Transformer 层独立学习一组前缀。可学习参数总数约为

物理含义:在每层为模型注入”任务专属的上下文先验”,使后续 token 的注意力机制能以任务感知的方式运行。

3.4 Prompt Tuning

仅在输入层添加可学习的软提示向量:

与 Prefix Tuning 的区别在于:Prompt Tuning 只在第一层注入提示,而不修改每一层的 。参数量更少,但表达能力也较弱。

3.5 其他方法简述

  • IA³(Infused Adapter by Inhibiting and Amplifying Inner Activations):学习逐通道缩放向量 ,修改 。参数量极小(约 0.1%),适合极端资源受限场景。
  • LoRA+:为 设置不同的学习率,缓解收敛速度不匹配的问题。
  • AdaLoRA / QLoRA+:自适应调整不同层的 rank 分配,优先在更重要/高敏感度的权重上分配更多参数。
  • VeRA(Vector-based Random Matrix):用随机投影向量代替可学习矩阵,大幅减少可训练参数。
  • LoRA-FA(LoRA with Fisher):基于 Fisher 信息确定哪些层更重要,动态调整 rank。

4. 数学视角:为什么少量参数也能有效适配?

4.1 从参数更新角度的推导

全参数微调的参数更新为 ,其中 是梯度, 是预条件矩阵(Adam 等)。PEFT 只在一个小子空间优化,即:

这要求任务相关的参数更新方向落在低维子空间中。大量实验和理论分析表明,当预训练模型足够大时,下游任务的适应方向确实呈现出低维结构(” intrinsically low-dimensional”)。

4.2 渐进式表征理论

从 representation 的角度看:预训练模型已经学到了丰富的语言知识和世界知识(存储在参数中)。PEFT 只负责在冻结的基础表示之上,添加任务特定的轻微扰动。

这个 由少量新增参数生成,因此可以解释为对高层表征的”线性探测”(linear probing)。

4.3 灾难性遗忘的缓解机制

全参数微调时,由于所有参数都参与梯度更新,模型容易覆盖预训练阶段获得的知识(灾难性遗忘)。PEFT 通过以下机制缓解:

  1. 冻结即保护:预训练知识存储在 中,永不更新。
  2. 低维约束:即使训练目标有噪声,有限的自由度也约束了破坏性更新的能力。
  3. 正则化隐式:LoRA 的 可以看作是对 的贝叶斯后验估计中加入了先验( 接近原始值),具有隐式正则化效果。

5. 能力边界与任务适配

5.1 PEFT 适合的任务类型

任务类型PEFT 适用性说明
文本分类 / 情感分析非常适合简单序列标注,LoRA/Adapter 通常优于全参
对话 / 指令微调适合需要对齐时通常配合 RLHF 使用 PEFT
领域自适应(法律、医疗)适合知识迁移型任务
复杂推理 / 长链推理效果偏弱复杂推理可能需要更多参数灵活性
超大规模多任务部分适合若任务间差异极大,多任务 PEFT 可能不如全参

5.2 PEFT 与全参微调的能力边界

  • 小模型(< 1B):PEFT 效果显著弱于全参微调。小模型本征表达能力有限,冻结大部分参数会导致容量不足。
  • 中模型(1B ~ 7B):PEFT 与全参差距较小,多数任务 PEFT 效果可达到全参的 90%~95%。
  • 大模型(> 7B):PEFT 效果与全参几乎持平(如 LLaMA-2 7B 以上),且大模型本身过参数化,低秩适应足以捕获任务信息。
  • 极端情况:当目标任务与预训练数据分布差异极大时(如预训练为英文,任务为中文古诗),全参微调仍更优。

6. 工程流程

6.1 如何选择 PEFT 方法

决策流程:
1. GPU 显存 ≤ 8 GB?
   → QLoRA(4-bit 量化 + LoRA)

2. 训练速度优先?
   → LoRA(几乎零推理延迟开销,训练/推理均快)

3. 需要同时运行多个任务(多 Adapter 切换)?
   → LoRA / Adapter(各 Adapter 可独立保存,按需加载)

4. 只需要极少量参数(< 1MB)?
   → IA³

5. 任务较简单(分类、NER)且模型较大(> 7B)?
   → Prompt Tuning / Prefix Tuning

6. 内存 + 精度双约束?
   → QLoRA

6.2 如何冻结/解冻参数

以 HuggingFace PEFT 库为例:

from peft import get_peft_model, LoraConfig, TaskType
 
# 冻结全部参数
for name, param in model.named_parameters():
    param.requires_grad = False
 
# LoRA 配置:只解冻 A, B 矩阵
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj"],  # 只在 Q/V 上加 LoRA
    lora_dropout=0.05,
    bias="none",
    task_type=TaskType.CAUSAL_LM
)
 
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 输出: "trainable params: 4,194,304 || all params: 6,738,415,616 || trainable%: 0.062%"

target_modules 选择注意:若选错(如选了模型不支持的层名),PEFT 将无效,模型不会学到任何东西。常用 target_modules:

  • LLaMA/Mistral:["q_proj", "k_proj", "v_proj", "o_proj"]
  • ChatGLM:["query_key_value"]
  • Bloom:["query_layer", "key_layer", "value_layer", "dense"]

6.3 训练流程

# 优化器配置:只优化 PEFT 参数
optimizer = torch.optim.AdamW(
    filter(lambda p: p.requires_grad, model.parameters()),
    lr=2e-4,
    weight_decay=0.01
)
 
# 训练循环
for batch in dataloader:
    outputs = model(**batch)
    loss = outputs.loss
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()

6.4 保存 Adapter 与 Merge

保存 Adapter(不合并):

# 只保存 adapter 权重(推荐用于多任务切换场景)
model.save_pretrained("./lora_adapter")
# 会在 ./lora_adapter 下保存:adapter_config.json, adapter_model.safetensors

合并权重(推理时使用):

from peft import PeftModel, LoraConfig
 
# 加载基座模型 + adapter,合并后保存
base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b")
peft_model = PeftModel.from_pretrained(base_model, "./lora_adapter")
merged_model = peft_model.merge_and_unload()
merged_model.save_pretrained("./merged_model")

注意:合并后模型文件大小恢复为完整基座模型大小,但推理时无任何额外计算开销。

6.5 部署推理

# 方式1:加载合并后的完整模型(单卡推理推荐)
model = AutoModelForCausalLM.from_pretrained("./merged_model")
 
# 方式2:加载基座 + adapter,实时合并(节省存储,按需加载)
base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b")
peft_model = PeftModel.from_pretrained(base_model, "./lora_adapter")
# 合并到基座后再推理(见上方 merge_and_unload)

6.6 多 PEFT 方法组合

PEFT 方法之间可组合使用,但需注意:

# 多 LoRA 叠加(如多个不同任务的 adapter)
from peft import PeftModel
 
model = AutoModelForCausalLM.from_pretrained("base_model")
for adapter_path in ["task_a_adapter", "task_b_adapter"]:
    model = PeftModel.from_pretrained(model, adapter_path)
    model.merge_and_unload()  # 依次合并
# LoRA + Prefix Tuning 组合(更灵活但更复杂)
from peft import get_peft_model, LoraConfig, PrefixTuningConfig
 
config = LoraConfig() + PrefixTuningConfig()  # 需框架支持

7. 性能对比

7.1 显存占用

以 LLaMA-2 7B 为例:

配置加载基座(GB)梯度/优化器(GB)总显存(GB)相对全参
全参微调14(FP16)~28(Adam状态)~421.0×
LoRA(r=16, 2层)14~0.4~150.36×
QLoRA(4-bit NF4)~3.5~0.4~40.10×
Adapter(bottleneck=64)14~0.5~150.36×
Prompt Tuning(l=20)14~0.002~140.33×

7.2 训练速度

方法相对全参速度原因
全参微调1.0×全部参数参与前反向传播
LoRA~0.9×主要计算仍在主干,仅额外少量矩阵乘(Q, V 的低秩分解)
QLoRA~0.5×(相对全参)4-bit 推理本身较慢,但计算量少
Adapter~0.85×插入层带来额外前向计算
Prefix Tuning~0.95×仅注意力 K/V 扩展,计算量增加较少

7.3 可移植性

方法可移植性说明
LoRA极高adapter 权重文件(几十 MB)可在同架构不同基座间复用
QLoRA需基座量化方式一致(NF4 vs. Int8)
Adapter但推理时需动态加载,计算开销稍大
Prefix Tuning前缀向量与模型层强绑定,迁移需相同结构
Prompt Tuning极度依赖输入 embedding,对不同 tokenizer 敏感

7.4 可组合性

方法可组合性说明
LoRA非常高可同时加载多个 adapter 并 merge,组合灵活
Adapter同一层可叠加多个 adapter(串行或并行)
Prefix Tuning多任务前缀可拼接,但扩展性有限
Prompt Tuning软提示间相互干扰,多任务效果较差

8. 常见坑与避坑指南

8.1 target_module 选错

表现:训练损失几乎不变,model.print_trainable_parameters() 显示参数为 0。

检查步骤

  1. 打印模型所有层名,确认存在目标模块:for name, _ in model.named_modules(): print(name)
  2. 对比 PEFT 配置文件中的 target_modules,确保命名完全匹配(大小写敏感)。
  3. 部分模型(如 ChatGLM)使用统一层名 query_key_value,而非分开的 q_proj, k_proj, v_proj

8.2 rank 过小

表现:训练收敛慢,最终精度不足。

经验值

  • 简单任务(分类、NER): 足够
  • 中等复杂度(对话、翻译):
  • 高复杂度(代码生成、多跳推理):
  • 过大(>128)且数据量少,容易过拟合。

8.3 学习率不匹配

LoRA 的最佳学习率通常比全参微调高 10~100 倍。原因是 LoRA 参数空间小,需要更大的步长来有效更新。

微调方式学习率范围
全参微调1e-5 ~ 5e-5
LoRA(r=8~64)1e-4 ~ 3e-3
QLoRA3e-4 ~ 1e-2
Adapter5e-5 ~ 1e-3

8.4 合并后精度变化

现象:merge_and_unload() 后 perplexity 或下游指标轻微下降(0.5%~2%)。

原因:合并过程存在数值精度问题,尤其在 为 FP16 而 为 BF16 时。

缓解方法

  • 使用 merge_and_unload() 前先将 adapter 转为与基座相同的精度。
  • 若精度损失敏感,考虑在推理时使用 PeftModel 动态合并,而非预合并。

8.5 数据量与 PEFT 方法的匹配

数据规模推荐方法
< 1k 条Prompt Tuning / IA³(极低过拟合风险)
1k ~ 50kLoRA(r=8~32)或 Adapter
50k ~ 500kLoRA(r=16~64)或全参微调
> 500k全参或 LoRA + 全参混合

9. PEFT 方法对比表

方法可训练参数量推理延迟多任务支持实现难度显存优化适用场景
LoRA0.1%~1%(r 决定)无额外延迟非常高(多个 adapter 合并)好(可 QLoRA)通用场景,对话/指令微调,领域适配
QLoRA0.1%~0.5%轻微增加(4-bit 推理)高(需同量化格式)极好(4-bit)单卡微调大模型(>7B)
Adapter0.5%~2%增加(逐层串行计算)中(动态加载)中等需要逐层控制的中等规模模型
Prefix Tuning0.1%~2%(取决于前缀长度)轻微增加(K/V 序列更长)中(前缀拼接)需要逐层任务感知的场景
Prompt Tuning< 0.1%(仅 embedding)几乎无低(软提示相互干扰)极好超大模型 + 小数据量(分类为主)
IA³< 0.1%(仅缩放向量)无额外延迟极好极端资源受限 + 简单任务

10. 选择 PEFT 方法的决策建议

决策树

开始
│
├─ GPU 显存 ≤ 8 GB
│   └─ 是 → QLoRA(4-bit NF4 + LoRA)
│           └─ 若仍显存不足 → 降低 r 或进一步量化
│
├─ 需要部署多任务(按需切换 Adapter)?
│   └─ 是 → LoRA(adapter 体积小,切换灵活,合并后零推理开销)
│
├─ 任务非常简单(分类、NER)且数据量少(< 1k)?
│   └─ 是 → Prompt Tuning(参数最少,不易过拟合)
│
├─ 需要极低延迟推理(实时系统)?
│   └─ 是 → LoRA 或 IA³(无推理额外计算)
│
├─ 模型规模 > 30B?
│   └─ 是 → QLoRA(其他方法显存难以承受)
│
├─ 需要多层任务感知(前缀影响每一层)?
│   └─ 是 → Prefix Tuning(比 Prompt Tuning 表达力更强)
│
└─ 以上都不满足 → LoRA(r=16~32)作为默认选择

快速参考

  • 入门首选:LoRA r=16, alpha=32, lr=2e-4, target_modules 覆盖 Q/K/V/O
  • 单卡微调 7B+:QLoRA 4-bit NF4 + LoRA r=64
  • 极致参数节省:IA³ 或 Prompt Tuning
  • 多任务切换:LoRA,每个任务一个 adapter 目录,推理时按需 load + merge
  • 生产部署:合并后的模型(merged_model),无 PEFT 运行时依赖

文档版本:2026-05-17