LoRA(Low-Rank Adaptation)
一种参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)方式
在冻结原始大模型参数的前提下,只训练一个很小的低秩权重增量
原理
需要一点点基础的线性代数的知识
假设某一层原始权重是:
$$ W_0 \in \mathbb{R}^{d_{out} \times d_{in}} $$
输入是:
$$ x \in \mathbb{R}^{d_{in}} $$
原始线性层输出是:
$$ y = W_0 x $$
全量微调会直接更新整个 $W_0$,得到:
$$ W_{new} = W_0 + \Delta W $$
这里的 $\Delta W$ 就是微调过程对原模型权重造成的变化。
而LoRA 不直接训练完整的 $\Delta W$,而是把它写成两个小矩阵的乘积: $$ \Delta W \approx BA $$
其中:
$$ A \in \mathbb{R}^{r \times d_{in}} $$
$$ B \in \mathbb{R}^{d_{out} \times r} $$
并且:
$$ r \ll \min(d_{in}, d_{out}) $$
于是原来的线性层从:
$$ y = W_0 x $$
变成:
$$ y = W_0 x + \frac{\alpha}{r}BAx $$
这里:
- $W_0$:预训练模型原始权重,冻结不训练;
- $A$ 和 $B$:LoRA 新增的小矩阵,训练时只更新它们;
- $r$:rank,控制 LoRA 的容量;
- $\alpha$:缩放系数,控制 LoRA 分支对原模型的影响强度;
- $\frac{\alpha}{r}$:常见实现里的归一化缩放。
一句话总结:
LoRA 假设“微调需要的权重变化”是低秩的,所以只训练一个低秩增量,而不是训练整个大模型。
所谓的低秩的假设就是说微调中重要的调整方向是较少的,因此就可以在限制r这一自由度的低秩矩阵基础上,达到类似的全量微调的效果
详细讲就是:
矩阵的秩可以粗略理解为:这个矩阵真正包含多少个独立方向。
如果一个大矩阵的变化可以由少数几个方向组合出来,它就是低秩或近似低秩的。
举一个线性层例子:
$$ W_0 \in \mathbb{R}^{4096 \times 4096} $$
全量微调这一层需要训练:
$$ 4096 \times 4096 = 16,777,216 $$
个参数。
如果 LoRA 取:
$$ r = 8 $$
那么只训练:
$$ A: 8 \times 4096 $$
$$ B: 4096 \times 8 $$
总参数量是:
$$ 8 \times 4096 + 4096 \times 8 = 65,536 $$
这一层的可训练参数约减少:
$$ \frac{16,777,216}{65,536} = 256 $$
倍。
LoRA 的训练过程
LoRA 训练时一般这样初始化:
- 冻结 $W_0$;
- 随机初始化 $A$;
- 将 $B$ 初始化为 0。
因为开始时:
$$ B = 0 $$
所以:
$$ BA = 0 $$
模型初始输出仍然是:
$$ y = W_0 x $$
这意味着 LoRA 微调从原模型行为出发,而不是一开始就扰动模型。
训练过程中,梯度只更新 $A$ 和 $B$:
base model W0: 冻结
LoRA A: 训练
LoRA B: 训练
最后得到一个任务专用的 adapter:
LoRA adapter = A + B + 配置参数
这个 adapter 通常很小,可以单独保存、分享和加载。
LoRA 的推理过程
训练完成后有两种使用方式。
第一种是分离加载:
base model + LoRA adapter
推理时仍然计算:
$$ y = W_0 x + \frac{\alpha}{r}BAx $$
第二种是合并权重:
$$ W_{merged} = W_0 + \frac{\alpha}{r}BA $$
合并后推理就变成普通线性层:
$$ y = W_{merged}x $$
合并后几乎没有额外推理延迟。
LoRA 加在哪里
Transformer里有大量线性层。LoRA 本质上可以加到任何线性层上,但在LLM里常见目标包括:
- attention 的
q_proj; - attention 的
k_proj; - attention 的
v_proj; - attention 的
o_proj; - MLP 的
gate_proj; - MLP 的
up_proj; - MLP 的
down_proj。
早期 LoRA 常只加在 attention 的 query 和 value 投影上,例如 q_proj、v_proj。现在很多指令微调实践会把 MLP 投影层也纳入目标模块。
选择方式可以这样理解:
| LoRA 位置 | 优点 | 代价 |
|---|---|---|
| 只加 attention 的部分投影层 | 参数最省,训练快 | 表达能力较弱 |
| 加 attention 全部投影层 | 仍然比较省,效果更稳 | 参数略增 |
| 加 attention + MLP | 更适合复杂任务和领域适配 | 显存、训练时间增加 |
| 加所有线性层 | 最接近全量微调 | 参数最多,也更可能过拟合 |
核心超参数
rank:
r:上面说了缩放系数:
lora_alpha:LoRA 实际使用的更新通常是:
$$ \Delta W = \frac{\alpha}{r}BA $$
所以就是控制LoRA矩阵的影响力的
常见设置会让
alpha与r相等或是r的 2 倍
dropout:
lora_dropout:顾名思义是加在 LoRA 分支上的 dropout,dorpout是指训练中的一种防过拟合方法,训练时随机关闭一部分神经元或信号路径,让模型不要依赖单一特征,从而减少过拟合目标模块:
target_modules:加在哪些层例如
["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
其他微调方式与比较
| 方法 | 更新什么 | 优点 | 缺点 |
|---|---|---|---|
| 全量微调 | 更新整个模型 | 效果上限高 | 显存、存储和部署成本高 |
| Prompt Tuning | 学习软提示 | 极省参数 | 表达能力有限 |
| Prefix Tuning | 学习前缀向量 | 省参数,适合生成任务 | 推理上下文开销更明显 |
| Adapter | 插入小模块 | 参数高效 | 推理路径通常增加额外计算 |
| LoRA | 学习低秩权重增量 | 参数少,可合并,推理友好 | rank 太小会欠拟合 |
这里其他的还没太多了解
变体:QLoRA、AdaLoRA、DoRA
QLoRA
可以理解成:
QLoRA = 量化的冻结基座模型 + LoRA adapter 训练
普通 LoRA 减少的是可训练参数和优化器开销,但 base model 本身仍然要放进显存。
QLoRA 进一步把冻结的 base model 量化t,从而显著降低显存占用。训练时,梯度通过量化后的模型传播,但实际更新的仍然是 LoRA adapter。
AdaLoRA
普通 LoRA 通常给不同层分配相同的 rank。
但不同层的重要性并不一样。有的层对任务适配很关键,有的层几乎不需要动。
AdaLoRA 的思想是:
总 rank 预算固定,但动态分配给更重要的层和方向
它会根据重要性评分保留更有价值的低秩方向,剪掉不重要的方向。
DoRA
全称 Weight-Decomposed Low-Rank Adaptation。
它认为权重可以拆成两个方面:
- magnitude:权重大小;
- direction:权重方向。
普通 LoRA 主要通过低秩更新改变权重方向,但对 magnitude 的建模不够直接。
DoRA 把 magnitude 和 direction 分开处理,用 LoRA 更专注地适配 direction,同时单独学习 magnitude。这样可以更接近全量微调的效果。