最近在看 LoRA(Low-Rank Adaptation)的时候,碰到了一个公式:
$$ \Delta W = BA $$其中 \(B \in \mathbb{R}^{m \times r}\),\(A \in \mathbb{R}^{r \times n}\),中间那个维度 \(r\) 被称为 Rank。
我对 Rank 这个词并不陌生——以前在 ACM-ICPC 里写高斯消元的时候,算自由变量个数就用到了矩阵的秩。但当时的关注点全在怎么求解、怎么判断无解多解、怎么把实际问题转化成线性方程组。至于 Rank 本身代表什么数学结构、跟低秩分解有什么关系,说实话没有认真想过。
于是我试着把这些概念重新串一遍:Gaussian Elimination 做了什么,Pivot 到底是什么,Rank 为什么等于 Pivot 的数量,行秩和列秩为什么相等,自由变量的个数从哪来,低秩矩阵为什么可以用更少的参数表示。
这些概念之间的联系比我预期的要紧密。
从线性相关开始#
先看两个二维向量:
$$ v_1 = (1, 2), \qquad v_2 = (2, 4) $$\(v_2 = 2v_1\),方向完全相同,只是长度不同。如果我用这两个向量做线性组合 \(a v_1 + b v_2\),不管 \(a\) 和 \(b\) 怎么取,结果永远落在同一条直线上。第二个向量没有提供任何新的方向信息。
这就是 Linear Dependence(线性相关):一组向量中,某个向量可以写成其他向量的线性组合。
反过来,如果一组向量中任何一个都不能写成其他的线性组合,就是 Linear Independence(线性无关)。比如 \(u_1 = (1, 0)\) 和 \(u_2 = (0, 1)\),它们指向完全不同的方向,线性组合可以覆盖整个二维平面。
一组向量能够张成(Span)的空间,取决于其中有多少个线性无关的向量。把这些向量排成矩阵的行(或列),这个最大线性无关向量的个数,就是矩阵的 Rank。
来看一个具体的矩阵:
$$ A = \begin{bmatrix} 1 & 2 \\ 2 & 4 \end{bmatrix} $$两行向量分别是 \((1, 2)\) 和 \((2, 4)\),第二行是第一行的 2 倍。只有一个独立方向,所以 \(\operatorname{rank}(A) = 1\)。
对比单位矩阵:
$$ I = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} $$两行向量分别指向两个坐标轴方向,互相独立,\(\operatorname{rank}(I) = 2\)。
矩阵有多少行、多少列,并不等价于它有多少个独立方向。 一个 \(1000 \times 1000\) 的矩阵完全可能 Rank 只有 3——意味着它的 1000 个行向量中,只有 3 个是真正独立的,其余都是这 3 个的线性组合。
重新理解 Gaussian Elimination#
这一节想回答的问题是:高斯消元到底在做什么?它跟 Rank 有什么关系?
取一个 \(3 \times 3\) 矩阵:
$$ A = \begin{bmatrix} 1 & 2 & 3 \\ 2 & 5 & 8 \\ 3 & 7 & 11 \end{bmatrix} $$按标准的高斯消元步骤,用第一行去消掉下面两行的第一列:
$$ R_2 \leftarrow R_2 - 2R_1: \quad \begin{bmatrix} 1 & 2 & 3 \\ 0 & 1 & 2 \\ 3 & 7 & 11 \end{bmatrix} $$$$ R_3 \leftarrow R_3 - 3R_1: \quad \begin{bmatrix} 1 & 2 & 3 \\ 0 & 1 & 2 \\ 0 & 1 & 2 \end{bmatrix} $$第三行和第二行变得完全一样了。继续消:
$$ R_3 \leftarrow R_3 - R_2: \quad U = \begin{bmatrix} 1 & 2 & 3 \\ 0 & 1 & 2 \\ 0 & 0 & 0 \end{bmatrix} $$最终得到一个行阶梯形矩阵(Row Echelon Form)\(U\),第三行变成了全零。
这里有几个问题值得停下来想想。
为什么可以用一行减去另一行的倍数? 因为行变换等价于对方程组做等价变换——把一个方程减去另一个方程的倍数,不会改变方程组的解集。
为什么消元是可逆的? \(R_2 \leftarrow R_2 - 2R_1\) 这个操作可以通过 \(R_2 \leftarrow R_2 + 2R_1\) 恢复原样。整个消元过程等价于左乘一个可逆矩阵 \(E\),使得 \(EA = U\)。
第三行变成零意味着什么? 它说明原来的第三行 \((3, 7, 11)\) 是前两行的线性组合。实际上可以验证 \(r_3 = r_1 + r_2\):\((1,2,3) + (2,5,8) = (3,7,11)\)。消元的过程把这个冗余关系暴露了出来。
现在来看 Pivot。在行阶梯形 \(U\) 中,每个非零行的第一个非零元素叫做 Pivot(主元)。这里第一行的 Pivot 是第一列的 1,第二行的 Pivot 是第二列的 1。
Pivot 所在的列叫做 Pivot Column(主元列)。在 \(U\) 中,第 1 列和第 2 列是 Pivot Column,第 3 列不是。
Pivot 的数量就是非零行的数量,也就是矩阵的 Rank:
$$ \operatorname{rank}(A) = 2 $$高斯消元做的事情,就是通过可逆的行变换,把矩阵中隐藏的线性相关关系一层层剥离出来。 最终剩下的非零行,就是这个矩阵真正独立的行向量,它们的数量就是 Rank。
行秩和列秩为什么相等#
到这里为止,我一直在用行向量来讨论 Rank。但矩阵也有列向量,列向量的最大线性无关个数叫做 Column Rank(列秩),行向量的叫做 Row Rank(行秩)。
一个事实是,对于任意矩阵,行秩总是等于列秩。这一点我以前知道结论但从来没认真想过为什么。
先在具体矩阵上验证。原矩阵 \(A\) 的三个列向量分别是:
$$ c_1 = \begin{pmatrix} 1 \\ 2 \\ 3 \end{pmatrix}, \quad c_2 = \begin{pmatrix} 2 \\ 5 \\ 7 \end{pmatrix}, \quad c_3 = \begin{pmatrix} 3 \\ 8 \\ 11 \end{pmatrix} $$可以验证 \(c_3 = -c_1 + 2c_2\):
$$ -\begin{pmatrix} 1 \\ 2 \\ 3 \end{pmatrix} + 2\begin{pmatrix} 2 \\ 5 \\ 7 \end{pmatrix} = \begin{pmatrix} 3 \\ 8 \\ 11 \end{pmatrix} $$三个列向量中只有两个是独立的,所以 Column Rank = 2,恰好和 Row Rank = 2 一致。
需要注意的是,行向量和列向量的线性组合系数可以完全不同。行向量满足 \(r_3 = r_1 + r_2\)(系数是 1, 1),列向量满足 \(c_3 = -c_1 + 2c_2\)(系数是 -1, 2)。相等的是独立向量的个数,不是线性组合的系数。
为什么一般情况下总是相等?从高斯消元的角度可以看到一条清晰的推理路径。
设 \(E\) 是消元过程对应的可逆矩阵,\(U = EA\) 是行阶梯形。
行变换不改变行秩。 \(E\) 是可逆的,所以 \(U\) 的行空间和 \(A\) 的行空间相同——\(E\) 把原来的行做了可逆的线性组合,不会增加也不会减少独立行的数量。在 \(U\) 中,非零行的数量就是行秩,因为行阶梯形的非零行天然互相独立(每行的 Pivot 位置不同,前面行在该位置已经是零)。
行变换不改变列秩。 这一步稍微需要想一想。假设 \(A\) 的列向量满足某个线性相关关系 \(\alpha_1 c_1 + \alpha_2 c_2 + \dots + \alpha_n c_n = 0\),这等价于 \(A\alpha = 0\),其中 \(\alpha = (\alpha_1, \dots, \alpha_n)^T\)。左乘可逆矩阵 \(E\) 后:
$$ EA\alpha = E \cdot 0 = 0 $$所以 \(U\) 的列向量也满足完全一样的线性相关关系。反过来,如果 \(U\alpha = 0\),那么 \(A\alpha = E^{-1}U\alpha = 0\)。可逆的行变换不会增加也不会减少列向量之间的线性相关关系,因此列秩也没有改变。
在行阶梯形 \(U\) 中:
- 非零行的数量 = 行秩
- Pivot Column 的数量 = 列秩(Pivot Column 对应的列向量互相独立,非 Pivot Column 的列向量可以由 Pivot Column 线性表出)
- 每个非零行恰好贡献一个 Pivot
所以:
$$ \boxed{\text{Row Rank} = \text{Pivot 数量} = \text{Column Rank}} $$这就是为什么可以直接说"矩阵的 Rank",不用区分行秩和列秩。
Rank 与自由变量#
这一节连接回算法竞赛中最常打交道的场景:解线性方程组。
考虑齐次方程组 \(Ax = 0\),其中 \(A \in \mathbb{R}^{m \times n}\),\(\operatorname{rank}(A) = r\)。
对 \(A\) 做高斯消元化成行阶梯形 \(U\) 后,有 \(r\) 个 Pivot Column,对应 \(r\) 个 Pivot Variable(主元变量)。剩下的 \(n - r\) 个变量没有对应的 Pivot,它们可以自由取值,叫做 Free Variable(自由变量)。
用前面那个 \(3 \times 3\) 矩阵来看。\(U = \begin{bmatrix} 1 & 2 & 3 \\ 0 & 1 & 2 \\ 0 & 0 & 0 \end{bmatrix}\),Pivot 在第 1 列和第 2 列,所以 \(x_1\) 和 \(x_2\) 是主元变量,\(x_3\) 是自由变量。
从第二行回代:\(x_2 + 2x_3 = 0\),得 \(x_2 = -2x_3\)。
从第一行回代:\(x_1 + 2x_2 + 3x_3 = 0\),代入 \(x_2 = -2x_3\),得 \(x_1 = x_3\)。
令 \(x_3 = t\),解为:
$$ x = t \begin{pmatrix} 1 \\ -2 \\ 1 \end{pmatrix} $$自由变量的数量:
$$ \boxed{n - r = 3 - 2 = 1} $$解空间是一维的——由一个自由参数 \(t\) 张成。
这里有几个需要区分的地方:
- 齐次方程组 \(Ax = 0\) 至少有零解 \(x = 0\),不可能无解。
- 非齐次方程组 \(Ax = b\) 可能无解(当 \(b\) 不在 \(A\) 的列空间中)。
- 对有解的非齐次方程组,自由变量数量仍然是 \(n - r\)。
- Rank 越小,自由变量越多,解空间的自由度越高。
联系 ACM-ICPC 中的异或高斯消元#
以前做的HDU 5833就是一个典型的例子。那道题的核心是在 GF(2)(二元有限域,运算规则是异或)上建立方程组,要求每个素因子的指数为偶数——等价于异或为 0 的齐次方程组。
高斯消元的逻辑在 GF(2) 上完全一样:消元、找 Pivot、数自由变量。如果有 \(k\) 个自由变量,每个自由变量有两种取值(0 或 1),解的总数就是 \(2^k\)。题目要求排除全零解(空集),答案就是 \(2^k - 1\)。
需要注意的是,\(2^k\) 这个解的计数方式是 GF(2) 的特性——每个自由变量只有 0 和 1 两个取值。在实数域上,自由变量可以取任意实数值,解有无穷多个(形成一个向量子空间),不能用 \(2^k\) 来计数。
从 Rank 到 Low-Rank Factorization#
回到开头那个 Rank = 1 的矩阵:
$$ W = \begin{bmatrix} 1 & 2 \\ 2 & 4 \end{bmatrix} $$因为第二行是第一行的 2 倍,只有一个独立方向。实际上可以把它写成两个向量的外积:
$$ W = \begin{bmatrix} 1 \\ 2 \end{bmatrix} \begin{bmatrix} 1 & 2 \end{bmatrix} $$验证一下:右边是一个 \(2 \times 1\) 矩阵乘以一个 \(1 \times 2\) 矩阵,结果确实是 \(\begin{bmatrix} 1 & 2 \\ 2 & 4 \end{bmatrix}\)。
四个元素的矩阵被拆成了两个向量(共 4 个标量),看起来没省什么。但如果矩阵更大呢?
推广一下。如果 \(W \in \mathbb{R}^{m \times n}\),且 \(\operatorname{rank}(W) = r\),那么 \(W\) 可以精确分解为:
$$ W = BA $$其中 \(B \in \mathbb{R}^{m \times r}\),\(A \in \mathbb{R}^{r \times n}\)。
为什么 \(\operatorname{rank}(BA) \leq r\)?乘积 \(BA\) 的每一列是 \(B\) 的 \(r\) 个列向量的线性组合——\(A\) 的每一列提供组合系数。无论怎么组合,结果都落在 \(B\) 的列向量张成的 \(r\) 维子空间里,所以 \(BA\) 的列秩不可能超过 \(r\)。
矩阵的元素数量(\(m \times n\) 个标量),并不等价于它包含的独立线性信息量。 如果 Rank 远小于 \(\min(m, n)\),矩阵中的大部分信息是冗余的,可以用 \(m \times r + r \times n\) 个参数来精确表示。
当然,对于满秩矩阵,用较小的 \(r\) 做分解只能得到近似,不能精确重建。
矩阵分解的思想在推荐系统中很早就有应用。以前写过一篇LFM 隐因子模型笔记,核心就是把稀疏的用户-物品评分矩阵 \(R\) 近似分解为 \(P \times Q\),中间维度 \(K\) 对应隐因子的数量。同样的低秩分解思路,只是应用场景不同。
回到 LoRA#
现在回到文章最开始的问题。
微调一个预训练模型,常规做法是直接更新权重矩阵:
$$ W' = W + \Delta W $$其中 \(\Delta W\) 是训练过程中学到的增量,跟 \(W\) 同样大小。对于大语言模型中动辄 \(4096 \times 4096\) 的权重矩阵,\(\Delta W\) 有 \(4096^2 = 16{,}777{,}216\) 个参数需要训练。
LoRA 的核心假设是:微调过程中,权重的增量 \(\Delta W\) 不需要是满秩的——用一个低秩矩阵就够了:
$$ W' = W + BA $$\(B \in \mathbb{R}^{4096 \times r}\),\(A \in \mathbb{R}^{r \times 4096}\)。预训练的 \(W\) 保持冻结不动,只训练 \(B\) 和 \(A\)。
取 \(r = 8\),可训练参数量:
$$ 4096 \times 8 + 8 \times 4096 = 65{,}536 $$是原始矩阵参数量的 \(1/256\)。
这里有几个需要注意的地方:
- 上面的对比只是单个目标矩阵的参数量,不是整个模型的显存节省比例。
- \(r = 8\) 限制的是 \(\operatorname{rank}(\Delta W) \leq 8\),并不是说 \(\Delta W\) 的 Rank 一定等于 8——实际训练中 Rank 可能更低。
- 原始的 \(W\) 本身可以是满秩矩阵,低秩约束只加在增量上。
- LoRA 能工作的前提假设是:对于特定下游任务,权重的有效调整集中在少数几个方向上,不需要在所有方向上都有变化。这个假设在很多任务上经验性地成立,但不是对所有场景都一定成立。
LoRA 的初始化策略、缩放系数 \(\alpha / r\)、优化器状态、和全量微调的性能对比,这些工程细节留给独立的 LoRA 博客。这篇只关心数学上的一件事:为什么两个小矩阵可以代替一个大矩阵做更新。
写在最后#
回头看,这篇文章想串起来的东西并不复杂:
Gaussian Elimination 通过消除线性相关的冗余,揭示矩阵有多少个独立约束——这个数量就是 Rank。 行变换是可逆的,不会增加也不会减少独立方向,所以行秩和列秩总是相等。
低秩矩阵的冗余意味着更紧凑的参数表示。 如果一个 \(m \times n\) 的矩阵只有 \(r\) 个独立方向,它的信息完全可以用 \(m \times r + r \times n\) 个参数来编码。
LoRA 把这个数学性质用到了神经网络微调上:约束权重更新的 Rank,用远少于原始矩阵的参数实现有效的适配。
Gaussian Elimination、Matrix Rank、Low-Rank Factorization 并不是三个独立的知识点。它们背后是同一件事——线性相关、独立方向、信息冗余——只是分别从算法执行、数学结构、参数表示的角度在观察。
当然,高斯消元本身并不能证明 LoRA 的低秩更新假设在实际任务中有效——那是一个经验性的发现。但至少在数学层面上,理解 Rank 的含义之后,\(\Delta W = BA\) 这个公式就不再神秘了。
参考资料#
- Jacobian 连乘与矩阵作为线性变换 — 之前写过矩阵的线性变换视角
- LFM 隐因子模型笔记 — 推荐系统中的矩阵分解
- HDU 5833 题解 — GF(2) 上的异或高斯消元
- Hu et al., LoRA: Low-Rank Adaptation of Large Language Models, 2021
- Gilbert Strang, Introduction to Linear Algebra, Chapter 2-3