markdown
02-LoRA 原理与关键超参
论文信息
- 标题:LoRA: Low-Rank Adaptation of Large Language Models
- 作者:Hu et al., Microsoft
- 发表:2021 年
- 核心洞察:预训练模型在适配下游任务时,权重的变化矩阵具有"内在低秩性"(intrinsic low dimensionality),即巨大的参数更新其实可以用低秩矩阵近似表达。
数学原理:低秩分解
问题设定
对于一个预训练的权重矩阵 (如 Transformer 中某个线性层),全参微调会学习一个更新量 ,使得微调后的权重为:
LoRA 的核心假设是: 可以被低秩分解为两个矩阵的乘积:
其中:
- ("up-projection",升维矩阵)
- ("down-projection",降维矩阵)
- 是 秩(rank),通常取 4、8、16、32、64
因此前向传播变为:
初始化策略
- :使用高斯随机初始化(Kaiming 初始化)
- :初始化为全零矩阵
这样在训练开始时 ,模型输出与原始预训练模型完全一致,保证训练起点稳定。
参数量对比
以一个 的线性层为例:
| 方法 | 参数量 |
|---|---|
| 全参微调 | (约 16.7M) |
| LoRA(rank=4) | (约 33K) |
| 压缩比 | 约 512 倍 |
对于 LLaMA-2-7B(32 层,每层注意力有 q/k/v/o 四个投影矩阵),应用 LoRA rank=16:
- 可训练参数约 13.6M,仅占总参数的 0.19%
LoRA 减少了什么,没减少什么
减少了什么
- 训练参数量:从全部参数降到 0.1%~1%,这是最直观的减少。
- 优化器状态显存:Adam 优化器为每个可训练参数维护 momentum(一阶矩)和 variance(二阶矩),各占一份 FP32 副本。训练参数减少 500 倍,优化器状态也减少约 500 倍。这是 LoRA 节省显存的主要来源。
- 梯度显存:只有 A 和 B 需要存储梯度,冻结的 不需要。
- 存储空间:每个任务只保存 A 和 B 矩阵(MB 级),而非整个模型副本。
没有减少什么
- 前向传播的激活值显存:这是关键且常被误解的点。LoRA 的前向传播仍然是 ,其中 产生了与原始模型相同大小的中间激活。激活值的大小取决于
(batch_size, sequence_length, hidden_dim)这些维度,与参数是否冻结无关。LoRA 不改变模型架构和隐藏维度,因此激活显存与全参微调完全一致。 - 权重存储(推理时):训练后的权重需要 合并,大小与原始模型相同。不过在训练阶段, 可以用低精度存储(如 FP16/BF16 而不需要 FP32 的主权重)。
- KV Cache:推理阶段的 KV Cache 不受 LoRA 影响。
- 微小的额外开销:LoRA 分支 本身也会产生少量额外激活值。
显存分解示意(7B 模型,单卡)
| 显存组成部分 | 全参微调 | LoRA | 说明 |
|---|---|---|---|
| 模型权重(FP16) | ~14 GB | ~14 GB | 相同(冻结但仍需存储) |
| 梯度 | ~14 GB | ~0.03 GB | 仅 LoRA 参数 |
| 优化器状态(FP32) | ~28 GB | ~0.06 GB | 仅 LoRA 参数 |
| 激活值 | ~10-20 GB | ~10-20 GB | 基本相同 |
| 合计 | ~66-76 GB | ~24-34 GB | 主要节省来自优化器状态 |
关键超参数
1. rank(r)
- 含义:低秩矩阵的秩,控制适配器的"容量"或表达能力。
- 取值范围:通常 4、8、16、32、64、128
- 经验法则:
- 简单任务(风格迁移、格式适配):rank 4-8
- 中等任务(单领域 SFT):rank 16-32
- 复杂任务(代码生成、多任务):rank 64-128
- 权衡:rank 越高表达能力越强,但参数量和显存开销线性增长。超过一定阈值后收益递减。
- 原论文发现:rank=4 已经能取得很好的效果,rank=8 基本与全参微调持平。
2. lora_alpha(α)
- 含义:缩放因子,控制 LoRA 更新相对于原始权重的"强度"。
- 公式:实际缩放系数为
- 常见设置:
- (最常见,如 r=16, alpha=32)
- (缩放系数=1)
- (缩放系数=1/r,极小的更新)
- 直觉:alpha 类似于 LoRA 分支的学习率缩放。较大的 alpha 意味着 LoRA 更新对输出的影响更大。将 alpha 与 rank 解耦,允许在不改变缩放效果的情况下调整 rank。
- 重要:当 alpha=rank 时,缩放系数=1,此时 alpha 的效果等同于关闭。推荐 alpha = 2 * rank。
3. target_modules
- 含义:指定对哪些层的权重矩阵应用 LoRA。
- Transformer 中常见的选择:
| 模块 | 位置 | 说明 |
|---|---|---|
q_proj |
注意力 Query 投影 | 原论文推荐,效果最显著 |
k_proj |
注意力 Key 投影 | 辅助 |
v_proj |
注意力 Value 投影 | 原论文推荐与 q_proj 配合 |
o_proj |
注意力输出投影 | 增强 |
gate_proj |
MLP 门控投影 | LLaMA 架构特有 |
up_proj |
MLP 上投影 | LLaMA 架构特有 |
down_proj |
MLP 下投影 | LLaMA 架构特有 |
- 策略推荐:
- 基础:
["q_proj", "v_proj"](原论文验证最佳性价比) - 推荐:
["q_proj", "k_proj", "v_proj", "o_proj"](覆盖全部注意力) - 最强:所有线性层(注意力 + MLP),QLoRA 论文推荐此方案
- 基础:
4. lora_dropout
- 含义:LoRA 分支上的 Dropout 比率。
- 取值:通常 0.0-0.1
- 作用:防止过拟合,尤其在数据量少时有用。数据量大时可设为 0。
5. bias
- 含义:是否训练 bias 参数。
- 取值:
"none"(不训练)、"all"(训练所有 bias)、"lora_only"(只训练 LoRA 层的 bias) - 推荐:
"none",原论文指出训练 bias 收益极小。
代码示例
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(base_model, lora_config)
model.print_trainable_parameters()
# 输出类似:trainable params: 13,631,488 || all params: 6,738,415,616 || trainable%: 0.2023
训练后合并
训练完成后,可以将 直接加回原始权重:
合并后模型与原始模型结构完全相同,无额外推理延迟。这也是 LoRA 相比 Adapter 的核心优势之一。
参考:LoRA 论文 — https://arxiv.org/abs/2106.09685 参考:HuggingFace PEFT LoRA 文档 — https://huggingface.co/docs/peft/conceptual_guides/lora
---
## 跨库关联
- [ML 基础视角的 Fine-tuning 概览](/projects/knowledge-04-llmfine-tuning-lora-rlhf-dpo-eb7b7cdbc1) — LoRA、RLHF、DPO 的总览入口
- [PEFT总览](/projects/knowledge-peft-01-f9be571d4c) — LoRA 在参数高效微调中的整体位置
- [量化](/projects/knowledge-int8-int4-gptq-awq-ecdc477490) — QLoRA 所依赖的量化基础