系列回顾:上一篇我们搞清楚了高维空间的直觉——token 嵌入向量为什么需要 7168 维,以及高维空间的四个反直觉现象。最后我们提到了一个关键观察:7168 维的向量,其实际信息量远小于 7168 维,真正的语义结构分布在一个低维子空间里。这一篇我们就来把这个观察变成数学工具:矩阵的秩、SVD 奇异值分解、低秩近似——以及它们如何直接支撑了 DeepSeek V3 的 MLA 压缩和 LoRA 微调。
假设你要记录 1000 个学生的数学和物理成绩。
你有一张 $1000 \times 2$ 的表格——1000 行(学生),2 列(数学、物理)。
但你发现了一个规律:数学好的学生物理几乎都好,数学差的物理也差。两列成绩高度相关,本质上只有"一个方向"的信息——"这个学生理科有多强"。
这张 $1000 \times 2$ 的矩阵,虽然名义上是 2 列,但真正的信息维度只有 1。你可以用一列"理科综合分"来近似表达全部信息,而不损失太多。
这就是低秩(Low-Rank) 的核心直觉:矩阵名义上的维度很高,但真正独立的信息方向("秩")却很低。
DeepSeek V3 利用这个洞见,把 7168 维的 KV 向量压缩到 512 维缓存,推理时显存节省 90%+。LoRA 微调利用同样的洞见,把亿级参数的微调变成了万级参数的微调。
矩阵 $A$ 的秩(Rank),是它线性无关的行(或列)的最大数目。
直观理解:秩衡量矩阵里"真正独立的信息方向"有多少条。
对一个 $m \times n$ 的矩阵,秩最大为 $\min(m, n)$,此时称为满秩(Full Rank)。
例1:秩为 1 的矩阵
$$A = \begin{pmatrix} 1 & 2 & 3 \\ 2 & 4 & 6 \\ 3 & 6 & 9 \end{pmatrix}$$
观察:第2行 = 2 × 第1行,第3行 = 3 × 第1行。三行之间完全线性相关,只有第1行是独立的。
$$\text{rank}(A) = 1$$
这个矩阵虽然是 $3 \times 3$,但本质上只有 1 个独立信息方向。
例2:秩为 2 的矩阵
$$B = \begin{pmatrix} 1 & 0 & 2 \\ 0 & 1 & 3 \\ 1 & 1 & 5 \end{pmatrix}$$
第3行 = 第1行 + 第2行,是前两行的线性组合,冗余。前两行互相独立。
$$\text{rank}(B) = 2$$
$3 \times 3$ 矩阵,但实际只有 2 个独立方向。
例3:满秩矩阵
$$C = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} \quad \text{(单位矩阵)}$$
两行完全独立,$\text{rank}(C) = 2 = \min(2,2)$,满秩。
秩为 $r$ 的 $m \times n$ 矩阵,可以精确分解为 $r$ 个秩为 1 的矩阵之和:
$$A = \sum_{i=1}^r \vec{u}_i \vec{v}_i^T$$
每个 $\vec{u}_i \vec{v}_i^T$ 是一个"外积",是秩为 1 的矩阵——一列向量乘以一行向量。
存储原矩阵需要 $m \times n$ 个数。用这种分解存储只需 $r \times (m + n)$ 个数($r$ 个 $\vec{u}_i$ 向量加 $r$ 个 $\vec{v}_i$ 向量)。
当 $r \ll \min(m, n)$ 时,压缩比极高:
$$\text{压缩比} = \frac{r(m+n)}{mn}$$
这正是 DeepSeek MLA 和 LoRA 的数学基础。
奇异值分解(Singular Value Decomposition,SVD) 是线性代数最重要的分解之一。它说的是:
任意 $m \times n$ 的矩阵 $A$,都可以分解为三个矩阵的乘积:
$$A = U \Sigma V^T$$
其中: - $U \in \mathbb{R}^{m \times m}$:左奇异向量矩阵,列向量两两正交且为单位向量(正交矩阵) - $\Sigma \in \mathbb{R}^{m \times n}$:对角矩阵,对角元素 $\sigma_1 \geq \sigma_2 \geq \cdots \geq \sigma_r > 0$ 称为奇异值,其余为 0 - $V^T \in \mathbb{R}^{n \times n}$:右奇异向量矩阵,行向量两两正交且为单位向量(正交矩阵)
矩阵的秩 $r$ 恰好等于非零奇异值的个数。
任何线性变换(矩阵乘法)都可以分解为三步:
$$\vec{y} = A\vec{x} = U\Sigma V^T \vec{x}$$
奇异值 $\sigma_i$ 表示在第 $i$ 个方向上变换的"强度"。奇异值大 → 这个方向信息重要;奇异值小(接近 0)→ 这个方向几乎没有信息。
SVD 可以展开写成:
$$A = \sum_{i=1}^r \sigma_i \vec{u}_i \vec{v}_i^T$$
每一项 $\sigma_i \vec{u}_i \vec{v}_i^T$ 都是一个秩为 1 的矩阵,奇异值 $\sigma_i$ 是它的"权重"。
由于 $\sigma_1 \geq \sigma_2 \geq \cdots \geq \sigma_r$,第 1 项贡献最大,第 2 项次之,依次递减。
设矩阵(就是前面的秩 1 矩阵):
$$A = \begin{pmatrix} 1 & 2 & 3 \\ 2 & 4 & 6 \end{pmatrix}$$
第 2 行 = 2 × 第 1 行,秩为 1。
我们来找它的 SVD:
第一步,观察结构:$A$ 的每一行都是 $(1, 2, 3)$ 的倍数,所以列空间是 $\vec{u}_1 = \frac{1}{\|\vec{c}\|}\vec{c}$,其中 $\vec{c} = (1, 2)^T$:
$$\vec{u}_1 = \frac{1}{\sqrt{5}}\begin{pmatrix}1\\2\end{pmatrix}$$
第二步,行空间方向:每列都是 $(1,2)^T$ 的倍数,行方向是 $(1,2,3)$:
$$\vec{v}_1 = \frac{1}{\sqrt{14}}\begin{pmatrix}1\\2\\3\end{pmatrix}$$
第三步,奇异值:$\sigma_1 = \|A\vec{v}_1\| = \|\frac{1}{\sqrt{14}}(1\cdot1+2\cdot2+3\cdot3, 2\cdot1+4\cdot2+6\cdot3)^T\|$
$$= \left\|\frac{1}{\sqrt{14}}\begin{pmatrix}14\\28\end{pmatrix}\right\| = \frac{1}{\sqrt{14}}\sqrt{14^2 + 28^2} = \frac{14\sqrt{5}}{\sqrt{14}} = \sqrt{70}$$
验证:$\sigma_1 \vec{u}_1 \vec{v}_1^T = \sqrt{70} \cdot \frac{1}{\sqrt{5}}\begin{pmatrix}1\\2\end{pmatrix} \cdot \frac{1}{\sqrt{14}}\begin{pmatrix}1&2&3\end{pmatrix} = \frac{\sqrt{70}}{\sqrt{70}}\begin{pmatrix}1&2&3\\2&4&6\end{pmatrix} = A$ ✓
既然 SVD 把矩阵分解成按重要性排序的秩 1 分量之和,那么只保留前 $k$ 项,就得到秩为 $k$ 的最优近似:
$$A_k = \sum_{i=1}^k \sigma_i \vec{u}_i \vec{v}_i^T = U_k \Sigma_k V_k^T$$
其中 $U_k, V_k$ 分别取 $U, V$ 的前 $k$ 列,$\Sigma_k$ 取前 $k \times k$ 的子矩阵。
Eckart-Young 定理保证:在所有秩为 $k$ 的矩阵里,$A_k$ 是与 $A$ 最接近的(用 Frobenius 范数衡量):
$$A_k = \arg\min_{\text{rank}(B) \leq k} \|A - B\|_F$$
近似误差精确等于被丢掉的奇异值:
$$\|A - A_k\|_F = \sqrt{\sigma_{k+1}^2 + \sigma_{k+2}^2 + \cdots + \sigma_r^2}$$
奇异值衰减越快,低秩近似越准确。
不同类型矩阵的奇异值衰减速度差异极大:
随机矩阵(完全随机的数字填充):奇异值分布相对均匀,没有明显的"大小之分",低秩近似效果差。
真实数据矩阵(图像像素、文本特征、用户行为):奇异值快速衰减——前几个奇异值远大于后面的,保留前 $k$ 项就能捕捉大部分信息。
训练后的神经网络权重矩阵:同样表现出快速衰减——这正是 LoRA 能有效工作的数学依据。
真实世界的数据不是随机的,它们由少数几个"生成因素"决定。
以人脸图像为例:$128 \times 128$ 的 RGB 图片,名义维度是 $128 \times 128 \times 3 = 49152$。但真实人脸只由约 50 个因素决定(身份、表情、光照方向、角度、年龄……)。所有合法人脸分布在约 50 维的低维流形上,49152 维中大多数方向上是冗余的。
语言同理:7168 维的 token 表示,实际语义自由度可能只有几百维——"这个词的词性""它的情感极性""它在句法中的角色"……这些独立因素加起来可能只有几百个,其余维度是这些因素的各种组合和冗余表达。
因此,神经网络权重矩阵天然是低秩的,或接近低秩的。
DeepSeek V3 有 671B 参数。全量微调(Fine-tuning)需要:
现有最强的单卡 H800 只有 80 GB 显存,10 TB 需要 125 张卡协同工作,成本极高。
LoRA(Low-Rank Adaptation) 的关键假设:
预训练模型的权重矩阵 $W_0$ 已经很好,微调时的更新量 $\Delta W$ 是低秩的。
为什么这个假设合理?因为微调只是在预训练模型的基础上做细微调整(学会特定任务的风格或知识),这种"细微调整"涉及的方向远少于原始参数空间的维度。
LoRA 的做法:
冻结原始权重 $W_0$,把更新量参数化为两个小矩阵的乘积:
$$\Delta W = BA$$
其中 $B \in \mathbb{R}^{m \times r}$,$A \in \mathbb{R}^{r \times n}$,$r \ll \min(m, n)$。
前向传播变为:
$$h = W_0 x + \Delta W x = W_0 x + BAx$$
训练时只更新 $A$ 和 $B$,$W_0$ 保持冻结。
以 DeepSeek V3 的 $W_Q$ 矩阵为例,维度 $7168 \times 7168$:
$r=8$ 时,可训练参数只有全量的 0.22%,但微调效果往往接近全量微调!
这是因为假设成立:$\Delta W$ 确实是低秩的,$r=8$ 已经足以捕捉微调所需的信息方向。
$A$ 用随机高斯初始化,$B$ 初始化为全零。
初始时 $\Delta W = BA = B \cdot A = 0$,模型输出和原始 $W_0$ 完全相同。
训练从"零扰动"开始,稳定且合理。
实际使用中还会加一个缩放因子 $\frac{\alpha}{r}$($\alpha$ 是超参数),控制 LoRA 分支的总体影响力:
$$h = W_0 x + \frac{\alpha}{r} BAx$$
训练完成后,可以把 $\Delta W$ 直接加回 $W_0$:
$$W_{merged} = W_0 + \frac{\alpha}{r}BA$$
推理时直接用 $W_{merged}$,不增加任何额外的计算开销。
标准多头注意力(MHA)在推理时需要缓存每一层的 Key 和 Value 矩阵,称为 KV Cache。
对于 DeepSeek V3:
KV Cache 大小 = $61 \times 2 \times 16384 \times 128000 \times 2\text{bytes}$(BF16)$\approx$ 500 GB
这显然不可行——单台服务器的 GPU 显存根本装不下。
MLA(Multi-head Latent Attention) 的核心思想:不缓存完整的 K、V,而是缓存一个低维压缩表示,推理时再实时解压。
具体地,对于每个 token 的隐状态 $h_t \in \mathbb{R}^{7168}$:
压缩(下投影):
$$c_t^{KV} = W^{DKV} h_t \in \mathbb{R}^{512}$$
$W^{DKV} \in \mathbb{R}^{512 \times 7168}$ 是压缩矩阵,把 7168 维压缩到 512 维。
解压(上投影,推理时执行):
$$k_t = W^{UK} c_t^{KV} \in \mathbb{R}^{16384}$$ $$v_t = W^{UV} c_t^{KV} \in \mathbb{R}^{16384}$$
KV Cache 只需缓存 $c_t^{KV}$(512 维),而不是完整的 K 和 V(各 16384 维)。
KV Cache 从约 500 GB 降到约 15 GB,单台服务器可以轻松装下,推理效率大幅提升。
这正是低秩的核心:虽然 K、V 名义上是 16384 维,但它们的实际信息量分布在一个远低维的子空间里。
类比之前讲的 SVD:对 K 矩阵做 SVD,奇异值会快速衰减——前 512 个奇异值已经捕捉了绝大部分的信息,其余的可以丢弃。
MLA 用学习到的 $W^{DKV}$ 和 $W^{UK}$、$W^{UV}$ 来做这个"找低维子空间"的工作,比手动 SVD 更灵活,因为它可以端到端优化,直接最小化模型的预测损失,而不只是最小化重建误差。
推荐系统里,用户-物品评分矩阵 $R \in \mathbb{R}^{m \times n}$($m$ 用户,$n$ 物品)通常非常稀疏(大多数用户只看过少数物品)。
矩阵分解把 $R$ 近似为低秩:
$$R \approx P Q^T, \quad P \in \mathbb{R}^{m \times k}, Q \in \mathbb{R}^{n \times k}$$
$P$ 的每一行是用户的"隐因子向量"(用户偏好),$Q$ 的每一行是物品的"隐因子向量"(物品特征)。
这和 LoRA 的 $\Delta W = BA$ 在数学上完全类似——都是用两个小矩阵的乘积来近似一个大矩阵。
主成分分析(PCA) 是机器学习里最经典的降维方法,本质上就是截断 SVD:
$V_k$ 的列向量就是前 $k$ 个主成分方向(数据方差最大的方向)。
PCA 广泛用于数据可视化(把 7168 维 token 嵌入降到 2 维画图)、特征提取、噪声去除。
在 Transformer 里,注意力矩阵 $\text{Attn} = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) \in \mathbb{R}^{n \times n}$($n$ 是序列长度)。
研究发现,这个注意力矩阵往往是低秩的——大多数行都关注同样的少数几个位置。这启发了各种线性注意力近似(Linformer、Performer 等),用低秩近似来把注意力的 $O(n^2)$ 复杂度降到 $O(n)$。
import numpy as np import torch np.random.seed(42) # ===== 1. 矩阵的秩 ===== print("===== 矩阵的秩 =====\n") A_rank1 = np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]], dtype=float) A_rank2 = np.array([[1, 0, 2], [0, 1, 3], [1, 1, 5]], dtype=float) A_full = np.array([[1, 2], [3, 5]], dtype=float) print(f"秩为1的矩阵的秩: {np.linalg.matrix_rank(A_rank1)}") print(f"秩为2的矩阵的秩: {np.linalg.matrix_rank(A_rank2)}") print(f"满秩矩阵的秩: {np.linalg.matrix_rank(A_full)}") # ===== 2. SVD 分解与重建 ===== print("\n===== SVD 分解 =====\n") A = np.array([[3, 1, 1], [1, 3, 1], [1, 1, 3]], dtype=float) U, S, Vt = np.linalg.svd(A) print(f"原矩阵 A:\n{A}\n") print(f"奇异值: {S.round(3)}") print(f"奇异值平方(能量): {(S**2).round(2)}") print(f"各奇异值解释的方差比例: {(S**2 / (S**2).sum()).round(3)}") # 用前 k 个奇异值重建 for k in [1, 2, 3]: A_k = (U[:, :k] * S[:k]) @ Vt[:k, :] err = np.linalg.norm(A - A_k, 'fro') print(f"\n保留前 {k} 个奇异值重建误差(Frobenius范数): {err:.4f}") if k <= 2: print(f"重建矩阵:\n{A_k.round(2)}") # ===== 3. 奇异值衰减:随机矩阵 vs 结构化矩阵 ===== print("\n===== 奇异值衰减对比 =====\n") d = 64 # 随机矩阵 A_random = np.random.randn(d, d) # 低秩结构矩阵(真实 rank = 5) true_rank = 5 A_lowrank = np.random.randn(d, true_rank) @ np.random.randn(true_rank, d) # 添加少量噪声模拟真实情况 A_lowrank_noisy = A_lowrank + 0.1 * np.random.randn(d, d) _, S_random, _ = np.linalg.svd(A_random) _, S_lowrank, _ = np.linalg.svd(A_lowrank_noisy) print("奇异值(前10个):") print(f"{'序号':>4} | {'随机矩阵':>10} | {'低秩结构矩阵':>12}") print("-" * 32) for i in range(10): print(f"{i+1:>4} | {S_random[i]:>10.3f} | {S_lowrank[i]:>12.3f}") # 用前 k 个奇异值能解释多少方差 for k in [1, 5, 10, 20]: var_random = (S_random[:k]**2).sum() / (S_random**2).sum() var_lowrank = (S_lowrank[:k]**2).sum() / (S_lowrank**2).sum() print(f"\n前{k:2d}个奇异值解释的方差: 随机={var_random:.1%}, 低秩结构={var_lowrank:.1%}") # ===== 4. LoRA 参数量对比 ===== print("\n===== LoRA 参数量节省 =====\n") m, n = 7168, 7168 # DeepSeek V3 的 W_Q 维度 print(f"矩阵维度: {m} × {n} = {m*n:,} 参数(全量微调)\n") print(f"{'rank r':>8} | {'LoRA参数量':>12} | {'占全量比例':>10} | {'节省':>8}") print("-" * 48) for r in [1, 2, 4, 8, 16, 32, 64, 128]: lora_params = m * r + r * n # B + A ratio = lora_params / (m * n) print(f"r={r:>5} | {lora_params:>12,} | {ratio:>9.3%} | {1-ratio:>7.3%}") # ===== 5. 模拟 MLA 的压缩与解压 ===== print("\n===== MLA KV Cache 压缩模拟 =====\n") torch.manual_seed(42) d_model = 7168 # 隐藏维度 d_kv = 16384 # 原始 KV 维度 d_c = 512 # MLA 压缩维度 seq_len = 100 # 序列长度 # 模拟权重矩阵(真实 DeepSeek V3 会训练这些权重) W_DKV = torch.randn(d_c, d_model) * 0.01 # 压缩矩阵:7168 → 512 W_UK = torch.randn(d_kv, d_c) * 0.01 # 解压矩阵:512 → 16384 (K) W_UV = torch.randn(d_kv, d_c) * 0.01 # 解压矩阵:512 → 16384 (V) # 模拟一批 token 的隐藏状态 H = torch.randn(seq_len, d_model) # [seq_len, 7168] # 压缩:生成 KV Cache C_KV = H @ W_DKV.T # [seq_len, 512] ← 只需要缓存这个! # 解压:推理时实时计算 K 和 V K = C_KV @ W_UK.T # [seq_len, 16384] V = C_KV @ W_UV.T # [seq_len, 16384] # 计算显存占用(BF16,每个元素 2 字节) bytes_original = seq_len * (d_kv + d_kv) * 2 # K + V,BF16 bytes_mla = seq_len * d_c * 2 # 只缓存压缩表示,BF16 print(f"序列长度: {seq_len} tokens") print(f"原始 KV Cache: {bytes_original / 1024:.1f} KB (K:{d_kv}维 + V:{d_kv}维)") print(f"MLA KV Cache: {bytes_mla / 1024:.1f} KB (压缩表示:{d_c}维)") print(f"压缩率: {bytes_mla/bytes_original:.1%} 节省: {1-bytes_mla/bytes_original:.1%}") print(f"\n推算到 128K token 的上下文(61层):") ctx_len = 128000 layers = 61 gb_orig = ctx_len * (d_kv + d_kv) * 2 * layers / 1e9 gb_mla = ctx_len * d_c * 2 * layers / 1e9 print(f" 标准 MHA KV Cache: {gb_orig:.1f} GB") print(f" MLA KV Cache: {gb_mla:.1f} GB") print(f" 节省: {gb_orig - gb_mla:.1f} GB") # ===== 6. 低秩分解的重建质量验证 ===== print("\n===== 低秩近似质量(类比 MLA 的有效性) =====\n") # 模拟一个真实权重矩阵(有低秩结构) true_r = 50 # 假设真实低秩为 50 W_true = torch.randn(512, 7168) # 类比 W_DKV # 对矩阵做 SVD,用前 k 个奇异值做近似 U, S, Vh = torch.linalg.svd(W_true, full_matrices=False) print(f"矩阵规模: {W_true.shape}, 最大秩: {min(W_true.shape)}") print(f"\n{'保留奇异值数k':>14} | {'重建误差(Frobenius)':>20} | {'误差/原始':>12}") print("-" * 52) orig_norm = torch.linalg.norm(W_true, 'fro').item() for k in [10, 50, 100, 200, 512]: W_approx = (U[:, :k] * S[:k]) @ Vh[:k, :] err = torch.linalg.norm(W_true - W_approx, 'fro').item() print(f"{k:>14} | {err:>20.4f} | {err/orig_norm:>11.2%}")
运行结果解读:
实验3的奇异值衰减对比会清晰地看到:随机矩阵的奇异值缓慢均匀衰减(前10个解释约20%的方差),而低秩结构矩阵的前5个奇异值解释了90%+的方差,其余几乎为零。这正是为什么 LoRA 和 MLA 能工作——真实权重矩阵更像后者。
实验4会看到 LoRA 在 $r=8$ 时只需要全量的 0.22% 参数,而在经验上这已经足够捕捉大多数微调任务所需的信息。
实验5直接模拟了 MLA 的压缩效果:128K 上下文时,标准 MHA 需要约 500 GB KV Cache,MLA 只需约 15 GB,节省 97%,让长上下文推理在单机上变为可能。
这篇文章从矩阵的秩出发,把低秩分解的数学原理和 DeepSeek 的具体应用全部打通了。
第一,矩阵的秩是"真正独立的信息方向数"。秩远小于矩阵维度,说明矩阵有大量冗余,可以被低维表示近似。
第二,SVD 把任意矩阵分解为 $U\Sigma V^T$,奇异值按重要性从大到小排列,展开写成秩 1 矩阵之和 $A = \sum_i \sigma_i \vec{u}_i \vec{v}_i^T$。奇异值衰减越快,矩阵越"低秩",低维近似越精确。
第三,截断 SVD 保留前 $k$ 个奇异值得到 $A_k$,Eckart-Young 定理保证这是所有秩 $k$ 矩阵里最优的近似。真实数据的奇异值快速衰减,让低秩近似既有效又精确。
第四,LoRA 利用"微调更新量 $\Delta W$ 是低秩的"这一假设,把更新量参数化为 $\Delta W = BA$($r \ll d$),把可训练参数从亿级降到万级,同时保持接近全量微调的效果。$r=8$ 时只需全量参数的 0.22%。
第五,MLA 利用"KV 向量分布在低维子空间"这一事实,把 7168 维 KV 向量压缩到 512 维缓存,推理时再解压。128K 上下文的 KV Cache 从约 500 GB 降到约 15 GB,节省 97%,使超长上下文推理成为现实。
第六,低秩分解是一个统一的数学框架,背后的核心洞见是:真实世界的数据和模型权重,实际信息维度远低于名义维度。这个洞见在推荐系统(矩阵分解)、降维可视化(PCA)、高效微调(LoRA)、推理加速(MLA)里都有直接应用。
下一篇预告:
从第11篇开始,我们进入第三阶段:概率与统计。
第11篇讲概率基础——随机变量、期望、方差、常见分布。语言模型的本质是在做条件概率预测,训练目标是最大化数据出现的概率。不懂概率,就没法真正理解语言模型在做什么。
低秩不是"近似",而是对真实结构的"发现"。语言的语义空间、模型的权重矩阵,本来就不需要那么多维度来描述。SVD 和低秩分解,是把这个隐藏的低维结构揭示出来的数学工具。DeepSeek 用它节省了 97% 的推理显存——这不是工程 trick,是数学规律的直接应用。
还没有评论,来抢沙发吧!
博客管理员
40 篇文章
还没有评论,来抢沙发吧!