跳到主要内容

LoRA 与参数高效微调

参数高效微调(Parameter-Efficient Fine-Tuning,PEFT)是在资源受限条件下定制 LLM 的核心技术体系。LoRA(Low-Rank Adaptation)是其中最广泛应用的方法,使得在消费级 GPU 上微调数十亿参数模型成为可能。

全量微调的资源消耗问题

在讨论 LoRA 之前,先理解全量微调(Full Fine-Tuning)的资源挑战:

显存构成分析(以 7B 模型为例)

全量微调需要在 GPU 显存中同时保存:

  • 模型权重:7B × 2 bytes(BF16)= 14 GB
  • 梯度:与权重相同大小 = 14 GB
  • 优化器状态(AdamW):一阶矩 + 二阶矩 = 14 GB × 2 = 28 GB(FP32)
  • 激活值(Activation):与 Batch Size 和序列长度相关,约 10-20 GB

总计:约 60-80 GB,超出单块 A100 80GB 显存的极限,必须使用多卡并行。

训练时间与成本

  • 7B 模型全量微调 1 万条数据(3 epochs):4 × A100,约 2-4 小时
  • 70B 模型全量微调:需要 32+ 块 A100,成本高达数千至数万美元

这对大多数个人研究者和中小企业来说难以承受。

LoRA 原理:低秩矩阵分解

论文LoRA: Low-Rank Adaptation of Large Language Models(Hu 等,2021)

核心思想

大模型在微调过程中,权重矩阵的变化量(ΔW)具有**低内在秩(Intrinsic Rank)**特性——尽管权重矩阵本身是高维的(如 4096×4096),但有意义的权重更新实际上处于一个低维子空间中。

因此,可以用两个低秩矩阵的乘积来近似 ΔW:

W' = W + ΔW = W + B × A

其中:

  • W:原始预训练权重(冻结,不更新)
  • A:形状为 [r, d_in] 的低秩矩阵,随机高斯初始化
  • B:形状为 [d_out, r] 的低秩矩阵,初始化为零(确保初始 ΔW=0)
  • r:秩(rank),远小于 d_in 和 d_out(如 r=8 vs d=4096)

参数量节省

以 4096×4096 的注意力投影矩阵为例:

  • 全量微调需要更新:4096 × 4096 = 16,777,216 个参数
  • LoRA(r=8)只需要更新:(4096×8) + (8×4096) = 65,536 个参数
  • 参数节省:99.6%

LoRA 推理时的合并

训练完成后,可以将 LoRA 权重合并回原始权重,推理时无额外开销:

W_merged = W + B × A × (α / r)

其中 α 是 LoRA 缩放因子(见下节),合并后的模型与原始模型架构完全相同。

rank 与 alpha 超参

rank(r)

  • 含义:低秩矩阵的秩,决定 LoRA 的参数量和表达能力
  • 典型取值:4、8、16、32、64
  • 选择原则
    • r 越大,表达能力越强,参数越多,但过大时趋近于全量微调
    • 对于简单任务或风格迁移,r=4 或 r=8 通常足够
    • 对于复杂任务(如数学推理微调),r=16 或 r=32 更合适

alpha(α)

  • 含义:LoRA 输出的缩放因子,实际缩放比例为 α/r
  • 典型取值:通常等于 rank 或其 2 倍(如 r=8,α=16)
  • 作用:平衡 LoRA 更新相对于原始权重的影响力

实践中常见设置:

r = 16
lora_alpha = 32 # alpha/r = 2,表示 LoRA 更新被放大 2 倍
lora_dropout = 0.05

适用的模块

LoRA 可应用于 Transformer 中的线性层。选择哪些层应用 LoRA 影响效果与参数量:

注意力层(通常必选)

  • Q(Query)投影
  • K(Key)投影
  • V(Value)投影
  • O(Output)投影

前馈网络(FFN)

  • Gate 投影(SwiGLU 中)
  • Up 投影
  • Down 投影

实验发现

  • 只对 Q/V 应用 LoRA:最省参数,适合存储资源极度受限的场景
  • 对所有注意力层和 FFN 应用 LoRA:效果通常最好
  • 对所有线性层应用 LoRA(包括 Embedding 层):参数最多,效果进一步提升

QLoRA:将 7B 微调压缩到 6-8 GB 显存

论文QLoRA: Efficient Finetuning of Quantized LLMs(Dettmers 等,2023)

QLoRA 是 LoRA 的进一步优化,通过三项关键技术将显存需求压缩至极致:

1. NF4 量化(4-bit Normal Float)

  • 将模型权重从 BF16(2 bytes)量化至 NF4(0.5 bytes/weight)
  • NF4 是一种专为正态分布权重设计的非均匀量化格式,在 4-bit 精度下损失最小
  • 7B 模型权重:从 14 GB 压缩至约 3.5 GB

2. 双重量化(Double Quantization)

  • 对量化常数(Scale 因子)再次量化,进一步减少内存开销
  • 每参数额外节省约 0.37 bit

3. 分页优化器(Paged Optimizer)

  • 利用 NVIDIA 统一内存机制,将优化器状态在 GPU 和 CPU 内存之间动态换页
  • 防止 GPU OOM(内存溢出)崩溃,实现更稳定的长序列训练

QLoRA 实际显存需求

模型QLoRA 显存LoRA(BF16) 显存全量微调显存
7B~6-8 GB~16-20 GB~60-80 GB
13B~10-12 GB~30-40 GB~120+ GB
70B~48 GB>200 GB需要集群

QLoRA 使得在单块 RTX 4090(24GB)上微调 7B 模型成为可能,在 A100 80GB 上可微调 70B 模型。

PEFT 库使用(Hugging Face)

Hugging Face 的 peft 库封装了主流 PEFT 方法:

from peft import LoraConfig, get_peft_model, TaskType

# 配置 LoRA
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05,
bias="none",
task_type=TaskType.CAUSAL_LM
)

# 将 LoRA 应用于模型
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# trainable params: 41,943,040 || all params: 6,738,415,616 || trainable%: 0.62%

LoRA 合并(merge_adapter)

训练完成后,可以将 LoRA 权重合并至基础模型权重,得到一个可独立使用的完整模型:

merged_model = model.merge_and_unload()
merged_model.save_pretrained("merged_model/")

合并后:

  • 推理时无额外的矩阵乘法开销(与原始模型完全相同)
  • 可以作为独立检查点分享或部署

多 LoRA 切换

在服务器部署场景中,可以基于同一基础模型加载多个 LoRA Adapter,按需切换:

  • 基础模型权重在 GPU 显存中常驻(最大开销)
  • 不同 LoRA Adapter 按请求动态加载(开销极小)
  • 实现"一个基础模型 + 多个任务专用 LoRA"的高效服务架构

peft 库的 set_adapter() 方法支持运行时切换 LoRA。

与全量微调的效果对比

LoRA 的效果与全量微调的差距在不断缩小:

  • 简单任务(风格迁移、格式调整):LoRA 效果与全量微调几乎无差异
  • 中等难度任务(特定领域知识注入、指令跟随改善):LoRA(r=64)达到全量微调约 95% 的效果
  • 高难度任务(大幅改变推理方式、深度领域专家化):全量微调仍有明显优势
  • QLoRA 的损失:量化引入额外 1-3% 的性能损失(可通过提高 rank 部分弥补)

对于大多数实际应用场景(企业私有数据微调、特定风格定制),LoRA/QLoRA 已经足够,是首选方案。