LoRA 英文全称为 Low-Rank Adaptation,中文:“低秩适配器”
秩的概念
英文 Rank 中文:秩
所谓秩,指的就是一个矩阵中 真正包含的信息量 有多少。
🙋小红买了 3 个苹果 4 个桃子,花了 18 元,小明买了 2 个苹果 3 个桃子,花了 13 元,请问苹果和桃子各多少钱。
苹果:x
桃子:y
3x + 4y = 18
2x + 3y = 13
6x + 8y = 36
6x + 9y = 39
y = 3
🙋小红买了 3 个苹果 4 个桃子,花了 18 元,小明买了 6 个苹果 8 个桃子,花了 36 元,请问苹果和桃子各多少钱。
3x + 4y = 18
6x + 8y = 36
上面的式子中,虽然有两个式子,但是第二个式子没有带来额外的信息。
例如:
$$
A = \begin{bmatrix}
0 & 0 \
0 & 0
\end{bmatrix}
$$
秩为 0,因为这个矩阵没有任何的信息。
$$
A = \begin{bmatrix}
1 & 0 \
0 & 1
\end{bmatrix}
$$
秩为 2,矩阵的两行提供了两条有效的信息。
$$
A = \begin{bmatrix}
1 & 2 \
2 & 4
\end{bmatrix}
$$
秩为 1,虽然矩阵有两行,但是两行是倍数关系(是线性相关的),只包含一条有效信息。
秩:矩阵中有效信息的数量。
LoRA原理
假设大模型原始权重矩阵(W)为:
$$
W = \begin{bmatrix}
1.0 & 2.0 & 3.0 & 4.0 \
5.0 & 6.0 & 7.0 & 8.0 \
9.0 & 10.0 & 11.0 & 12.0 \
13.0 & 14.0 & 15.0 & 16.0 \
17.0 & 18.0 & 19.0 & 20.0 \
\end{bmatrix}
$$
全量微调需要更新全部 5 x 4 = 20 个参数。假设微调后的参数为:
$$
W' = \begin{bmatrix}
1.41 & 2.44 & 3.47 & 4.50 \
5.93 & 7.0 & 8.07 & 9.14 \
10.45 & 11.56 & 12.67 & 13.78 \
14.97 & 16.12 & 17.27 & 18.42 \
19.49 & 20.68 & 21.87 & 23.06 \
\end{bmatrix}
$$
也就是说,微调后的矩阵可以看作是 一个原始矩阵 加上 一个增量矩阵,如下:
$$
W' = W + △W = \begin{bmatrix}
1.0 & 2.0 & 3.0 & 4.0 \
5.0 & 6.0 & 7.0 & 8.0 \
9.0 & 10.0 & 11.0 & 12.0 \
13.0 & 14.0 & 15.0 & 16.0 \
17.0 & 18.0 & 19.0 & 20.0 \
\end{bmatrix} + \begin{bmatrix}
0.41 & 0.44 & 0.47 & 0.50 \
0.93 & 1.00 & 1.07 & 1.14 \
1.45 & 1.56 & 1.67 & 1.78 \
1.97 & 2.12 & 2.27 & 2.42 \
2.49 & 2.68 & 2.87 & 3.06 \
\end{bmatrix}
$$
到目前为止,增量矩阵就是微调的参数数量,一共 20 个。(全量微调)
换成 LoRA 微调。
先对这个增量矩阵进行低秩分解:
$$
\Delta W = A \cdot B
$$
A 和 B 是什么呢?也是拆分出来的两个矩阵:
$$
\Delta W = A \cdot B
= \begin{bmatrix}
0.1 & 0.2 \
0.3 & 0.4 \
0.5 & 0.6 \
0.7 & 0.8 \
0.9 & 1.0 \
\end{bmatrix} ・ \begin{bmatrix}
1.1 & 1.2 & 1.3 & 1.4 \
1.5 & 1.6 & 1.7 & 1.8 \
\end{bmatrix}
$$
🤔为什么 增量矩阵 可以拆解成 A・B 呢?
这其实就是低秩分解的思想,指的是 矩阵里的信息往往是不均匀分布的,很多维度是冗余的,只需要抓住主要方向就够了。
因此在线性代数的矩阵乘法中,存在这么一个基础结论:
对于任意秩为 r 的矩阵:
$$
M \in \mathbb{R}^{m \times n}
$$
矩阵 M 是一个大小为 m 行 n 列的实数矩阵。
M
:表示一个矩阵的名字(矩阵通常用大写字母表示);
∈
:表示“属于”;
ℝ
:表示“实数集合”,所有实数的集合;
ℝ^{m × n}
:表示所有 m 行 n 列的实数矩阵 组成的集合。
总是存在两个矩阵:
$$
A \in \mathbb{R}^{m \times r} \
B \in \mathbb{R}^{r \times n}
$$
这就使得:
$$
M = A \cdot B
$$
这个结论是线性代数中“秩分解”(Rank factorization)的经典定理。
一个“秩分解”的详细例子,假设有这么一个矩阵:
$$
M = \begin{bmatrix}
2 & 4 \
6 & 12
\end{bmatrix}
$$
🤔 先思考这个矩阵的秩是多少?
秩为 1,代表着这是一个低秩,因此可以进行一个低秩分解。秩越低,信息量就越少。
于是我们就可以将它分解为:
$$
M = \begin{bmatrix}
1 \
3
\end{bmatrix}
\cdot
\begin{bmatrix}
2 & 4 \
\end
\begin{bmatrix}
2 & 4 \
6 & 12
\end{bmatrix}
$$
可以看到,在上面的示例中,一个秩为 1 的 2x2 矩阵就可以拆成一个 2x1 和 1x2 的矩阵乘积。
好了,回到我们刚才增量所拆解出来的矩阵 A 和 B 这里。拆解出 A 和 B 两个新的矩阵后,接下来我们就可以针对 A 和 B 两个矩阵来做训练:
- A:5 x 2 = 10 个参数
- B:2 x 4 = 8 个参数
- LoRA 总参数量:10 + 8 = 18 个参数
可以看到,通过 LoRA 微调,调参对象从 △W 的 20 个参数下降到 A・B 的 18 个参数。
🙋这没下降多少啊?
在实际的模型中,参数量往往是 数十亿,而通过 LoRA 只需训练原始参数总量的 0.01%~3% 的新增参数模块,就能获得接近全量微调的效果。
举个例子,假设你用的是一个 7B (70亿)参数的大模型:
- 全量微调:更新 7,000,000,000 个参数
- LoRA 微调:只引入了约 1,000,000 ~ 200,000,000 个可训练参数(取决于秩值 r、插入的层数等)
因此参数量变为了原来的:
$$
\frac{\text{LoRA 可训练参数}}{\text{原始参数总量}} \approx 0.01% \sim 3%
$$
总结 LoRA 优点:
-
参数少:它只微调原始参数的 1% 甚至更少
-
在 GPT-3 或类似大模型上,LoRA(如 r = 8)时可训练参数量仅为全量微调的 0.01% - 0.1%,但在多个自然语言任务(如问答、摘要)中,其性能已能达到全量微调的 95%~99%。
-
在 GLUE 基准测试中(使用 BERT),LoRA 设置 r = 16 时,仅使用约 0.1% 的参数,就能在多个子任务上达到与全量微调相近的表现,平均仅低 0.5–1 分。
[!tip]
以上数据均来自原始论文《LoRA: Low-Rank Adaptation of Large Language Models》
-
-
速度快:训练和部署都比全量微调省时省力。
-
模块化:训练好的 LoRA 插件可以随时加载或者卸载,不影响原始模型,特别适合多任务场景。可扩展性以及灵活性比较高。
微调实战?
🙋是否可以实战大模型微调?
目前为止,微调大模型高度依赖 Python 环境。
- 主流工具和框架依赖 Python:像 Hugging Face Transformers、PEFT、PyTorch Lightning、TensorFlow、DeepSpeed、Axolotl 等主流微调工具,都是基于 Python 构建的。它们提供了训练循环、优化器、数据加载器、分布式训练等关键能力,而这些功能的 API 和调用方式基本都依赖 Python。
- 数据准备环节使用 Python 工具链:微调之前的数据处理(如格式转换、清洗、tokenize、构造 prompt 等),普遍使用如
pandas
、json
、datasets
等 Python 库来完成。 - 训练环境配置依赖命令行 + Python:微调通常需要在具备 GPU 的环境中完成(无论是本地还是云端),这些环境的配置如 CUDA 驱动安装、依赖管理(conda、pip)、模型下载和运行,也大多围绕 Python 生态展开。
- 模型理解和调试文档以 Python 为主:无论是 Hugging Face 的官方文档,还是 GitHub 上的开源实现、论文配套代码,几乎都是 Python 编写的,理解其结构也离不开对 Python 的基本掌握。
-EOF-