LoRA 微调笔记
1. LoRA 是什么
LoRA,全称 Low-Rank Adaptation,低秩自适应,是一种用于高效微调大模型的技术。
它的核心思想是:
冻结原始大模型参数,不直接更新大模型原来的权重矩阵,而是在部分线性层中加入两个小的低秩矩阵,只训练这两个小矩阵。
因此,LoRA 可以在较少参数、较低显存和较短训练时间下,让大模型适应新任务。
2. 为什么需要 LoRA
传统全参数微调需要更新模型中的全部参数。
对于大语言模型来说,这会带来几个问题:
- 参数量太大
大模型通常有数十亿甚至上千亿参数,全部更新成本很高。 - 显存需求高
训练时不仅要存模型参数,还要存梯度、优化器状态和中间激活值。 - 训练时间长
每次反向传播都要更新大量参数。 - 存储成本高
每个任务都保存一份完整微调模型,会占用大量空间。
LoRA 的做法是:不修改原始模型的大部分参数,而是训练少量额外参数,用这些参数来适配新任务。
3. LoRA 的核心数学思想
假设原始模型中的某个线性层权重矩阵为 。
传统微调是直接更新整个矩阵:
其中, 表示模型为了适应新任务而学习到的权重变化。
LoRA 的假设是:这个权重更新矩阵 不一定需要是一个完整的大矩阵,它可以用两个小矩阵的乘积近似表示。于是:
所以 LoRA 形式为:
其中:
- :原始预训练权重,冻结不训练;
- 、:LoRA 新增的低秩矩阵,只训练它们;
- :低秩维度,也叫 rank,通常远小于 hidden size。
4. 参数量为什么变少
假设原始权重矩阵大小为:
如果全参数微调,需要训练 个参数。
LoRA 将更新矩阵拆成两个小矩阵:
那么 LoRA 需要训练的参数量为:
其中:
举例: 如果 ,,:
- 全参数微调需要: 个参数。
- LoRA 只需要: 个参数。
因此 LoRA 可以大幅减少可训练参数数量。
5. hidden size 和线性层参数量的关系
hidden size 可以理解为模型内部表示每个 token 的向量维度。 例如,hidden size = 768 表示每个 token 在模型内部是一个 768 维向量。
如果一个线性层把 768 维输入变成 768 维输出 (),那么它的权重矩阵大小就是 。 这是因为线性层的参数量等于:输入维度 × 输出维度。
所以, 不是因为 hidden size 和层数有平方关系,而是因为这个线性层的输入和输出维度刚好都等于 hidden size。
并不是所有线性层都是 hidden size 的平方。例如前馈神经网络中常见的升维层 (),它的参数量就是 。
6. LoRA 的前向传播过程
普通线性层的计算是:
加入 LoRA 后变成:
其中:
- 是原始模型输出;
- 是 LoRA 提供的任务适配修正;
- 原始权重 不训练;
- 只训练低秩矩阵 和 。
可以理解为:LoRA 不是替换原模型,而是在原模型输出上加一个小的任务修正项。
7. rank 的作用
是 LoRA 中非常关键的超参数,它决定了 LoRA 更新矩阵的表达能力。
可以简单理解为:
- 越大,LoRA 能学习的变化越多,但参数量和显存开销也越大;
- 越小,LoRA 越轻量,但表达能力可能不足。
常见选择:
| 任务类型 | 常见 rank |
|---|---|
| 简单分类任务 | 4 / 8 |
| 普通问答、指令微调 | 8 / 16 |
| 复杂生成任务 | 16 / 32 |
| 代码生成等复杂任务 | 32 / 64 |
经验上:
- 太小:模型可能学不动;
- 太大:训练成本上升,甚至可能过拟合; 需要根据任务复杂度和显存条件调整。
8. LoRA alpha
很多 LoRA 实现中还会加入一个缩放系数:
其中:
- 是 LoRA alpha;
- 是 rank;
- 是实际缩放比例。
它的作用是控制 LoRA 更新对原模型输出的影响强度。
- 如果 太小:LoRA 修正太弱,效果不明显。
- 如果 太大:LoRA 修正太强,可能破坏原模型原有能力。
因此,LoRA 的效果通常与 rank 和 alpha 的组合有关。
9. LoRA 通常加在哪些层
LoRA 通常不会加到模型的所有参数上,而是选择加在 Transformer 的关键线性层上。
9.1 Attention 层
常见模块与含义:
| 模块 | 作用 |
|---|---|
q_proj | 生成 Query,决定当前 token 想关注什么 |
k_proj | 生成 Key,决定 token 如何被匹配 |
v_proj | 生成 Value,决定被关注后提供什么信息 |
o_proj | 将多头注意力结果投影回隐藏空间 |
在 Attention 层加入 LoRA,主要影响模型的注意力计算方式。
9.2 前馈神经网络层
常见模块与含义:
| 模块 | 作用 |
|---|---|
gate_proj | 控制特征通过 |
up_proj | 将 hidden size 扩展到更高维 |
down_proj | 将高维特征压回 hidden size |
在 FFN 层加入 LoRA,主要影响模型的语义特征变换能力。
10. LoRA 的训练流程
LoRA 的训练过程可以概括为:
- 加载预训练模型:加载原始大模型参数 。
- 冻结原始模型参数: 不参与训练。
- 插入 LoRA 矩阵:在指定线性层中加入 和 。
- 前向传播:原始输出 + LoRA 修正输出。
- 反向传播:只更新 和 。
- 保存 LoRA 权重:保存少量 LoRA adapter 参数。
11. LoRA 的推理方式
LoRA 推理时一般有两种方式。
11.1 不合并权重
推理时仍然计算:
- 优点:方便切换不同 LoRA;适合多任务、多风格模型切换。
- 缺点:推理时会多一点计算。
11.2 合并权重
提前将 LoRA 权重合并进原始权重:
之后推理时直接使用:
- 优点:推理结构简单;推理时没有额外 LoRA 分支。
- 缺点:不方便动态切换不同 LoRA。
12. LoRA 的主要优点
- 参数高效:只训练少量新增参数,而不是更新整个模型。
- 显存占用低:冻结原模型参数后,训练时需要保存的梯度和优化器状态大幅减少。
- 训练速度快:需要更新的参数少,因此训练更快。
- 存储空间小:LoRA adapter 通常远小于完整模型权重,方便保存和分发。
- 方便多任务切换:可以为不同任务保存不同 LoRA 权重,例如:基础模型 + 医学 LoRA / 基础模型 + 法律 LoRA / 基础模型 + 代码 LoRA。
- 降低过拟合风险:由于可训练参数较少,LoRA 在小数据场景下可能比全参数微调更稳定。
13. LoRA 的局限
LoRA 虽然高效,但不是所有情况下都一定最优。常见问题包括:
- rank 太小会限制表达能力:如果任务复杂,而 设置过小,模型可能学不到足够的任务信息。
- rank 太大会降低效率:如果 太大,参数量上升,LoRA 的轻量化优势变弱。
- 目标层选择很重要:LoRA 加在哪些层,会明显影响最终效果。
- alpha 需要调节: 太小可能没效果,太大可能影响原模型稳定性。
- 不一定完全等价于全参数微调:对于某些非常复杂或需要深度改变模型能力的任务,全参数微调可能仍然更强。
14. LoRA 和 QLoRA
QLoRA 可以理解为:量化模型 + LoRA 微调。
它通常会将原始大模型权重量化到 4-bit,然后只训练 LoRA 参数。这样可以进一步降低显存占用,使较大模型也能在消费级显卡上进行微调。
简单理解:
- LoRA:冻结原模型,只训练低秩矩阵。
- QLoRA:先把原模型量化,再训练低秩矩阵。
参考资料: https://blog.csdn.net/woshihlf/article/details/149120921?spm=1001.2014.3001.5501
LoRA 微调笔记
https://ryanjxy123.github.io/post/share_somethings/learning_recording/lora/