V4 的 CSA/HCA 不是横空出世。注意力机制过去八年,每一代都在回答上一代留下的问题—— 每一刀都砍向同一个目标:KV cache。
教科书里画的注意力图,永远是一个 N×N 的方阵——每个 Q 和每个 K 互相点积。 但这只对训练阶段(和推理 prefill 阶段)成立。推理 decode 时根本不长这样。
K_1 K_2 ... K_N
Q_1 ✓ ✓ ✓
Q_2 ✓ ✓ ✓
...
Q_N ✓ ✓ ✓
形状: N × N
所有 Q 全部参与
K_1 K_2 ... K_N
Q_N ✓ ✓ ✓ ← 只有这一行
形状: 1 × N
只有最末位 Q 在工作
K_1..K_N 全部要存(KV cache)
每个 head 自己的 W_q / W_k / W_v,128 个 head 各存自己的 K/V。表达力强但 KV cache 巨大。
紫=Q 头,蓝=K 头,绿=V 头。三行一一对齐,每个 token 每层要存 8×2=16 个 head 向量(实际 256 个)。
Q 还是多头,但 K 和 V 全模型只保留一套。128 个 head 共用同一份 K/V。
KV cache 砍到 1/128——但表达力下降,几个基准明显掉点。
把 Q heads 分若干组,每组共享一套 K/V。LLaMA-3、Qwen、Gemma 这一代主流都在用。
在 MHA 和 MQA 之间给了一个旋钮:组数越少越省,越多越接近 MHA。
不砍头数。从另一个维度下刀:砍每条 KV 的"维度"。
把所有 head 的 K/V 压成一份 512 维的 latent,存的时候只存这个 latent。 "还原 K"和"还原 V"的投影矩阵在训练时被吸收进 Q 投影和输出投影里——推理时 latent 直接参与运算,从头到尾不需要还原成 K 和 V。
128 个 Q 头都从这一份 latent "投影"出自己的 K/V——但投影矩阵在训练时被合并进 Q' 和 W_o',推理时永远不显式还原。
原始: score = Q × Kᵀ / √d
output = softmax(score) × V
代入 K = latent × W_uk:
Q × Kᵀ = Q × W_ukᵀ × latentᵀ
= Q' × latentᵀ ← Q' = Q × W_ukᵀ 提前合并
代入 V = latent × W_uv:
output = softmax(score) × latent × W_uv × W_o
= softmax(score) × latent × W_o' ← W_o' = W_uv × W_o
✅ K 和 V 从来没被显式算出来。"还原"操作被合并进固定权重。
配置:head_dim=512,num_key_value_heads=1,num_attention_heads=128。50K 上下文 KV cache 从 81.5 GiB → 2.9 GB。
KV cache 全存,但注意力计算时只看 top-k。装一个轻量 Indexer 打分,挑最相关的 2048 条来做 attention。
50000 → 2048,计算量直接砍到 4%。
蓝色=被 Indexer 选中、参与 attention 的 KV;灰色=存了但不看的 KV。存储没省,只省了计算。
梁文锋署名。两个关键词: Hardware-Aligned(块结构对齐 GPU 内存访问)+ Natively Trainable(从训练第一步就用稀疏,不是套眼镜)。
每层并行三个分支:压缩全看(粗概览)+ 选块精读(top-16 块)+ 滑动窗口(最近 512)+ 门控加权合并。
三分支独立计算,最后门控加权求和。训练时就用稀疏——稀疏成了模型的归纳偏置。
实测:27B 模型在 9 项基准上平均 0.456 vs Full Attn 0.443,稀疏甚至比全注意力强。 AIME 24 数学竞赛 +7.5 分。原因:稀疏在训练时变成了 inductive bias。
NSA 的工程实践:从"层内三分支并行"改成"层间交替分工":
每过两层 = 1 全局粗扫 + 1 精选细节。50K KV cache 从 MLA 的 2.9 GB → 228 MB。
橙=HCA 压缩块, 蓝=CSA 选中块, 灰=CSA 未选中块, 绿=滑窗。 相邻两层组合,既扫了全局又选了细节。
| NSA 论文(2025-02) | V4 CSA/HCA(2026) | |
|---|---|---|
| 三分支怎么放 | 每层并行跑三个,门控加权求和 | 奇偶层交替:CSA / HCA 错开 |
| 压缩块大小 | 统一 32(步长 16,重叠) | CSA 4(重叠)/ HCA 128(不重叠) |
| 选 top-k | 复用压缩分支分数(可微分) | 独立 Lightning Indexer(FP4 全程) |
| 滑窗大小 | 512 | 128(= max(块大小)) |
| 训练阶段就用 | 是 | 是(继承 NSA) |
| 阶段 | 核心想法 | 50K KV cache | vs 上一代 |
|---|---|---|---|
| MHA(标准) | 每头独立 K/V | 81.5 GiB | 基线 |
| GQA (8 组) | 分组共享 K/V | 5.1 GB | ↓ 16× |
| MQA | 全模型 1 组 K/V | 0.64 GB | ↓ 8× |
| MLA (V2) | 维度压缩到 latent 512 | 2.9 GB | 基线 ↓ 28× |
| DSA (V3.2) | 序列稀疏(只省计算) | 2.9 GB | — 存储不省 |
| CSA/HCA (V4) | 序列压缩 + Indexer | 228 MB | ↓ 13× |
从 MHA 到 V4,总共压了 99.7%。注意 DSA → CSA 这一步:从"不改脑子换眼镜" 进化到 "眼镜从一开始就长在脑子里"。
大模型权重存的是"知识"——世界的地图。KV cache 存的不是地图,是当前这段上下文在地图上走过的路径。
路径能被压到 0.3% 而效果不掉,是因为自然语言的有效路径本来就是低维的。 7168 维的隐藏空间里,大部分维度是 MLP 临时展开的"计算脚手架",真正的语义坐标集中在几百维子空间里 (Intrinsic Dimension 实测约占隐藏空间个位数百分比)。
V4 所有压缩——MLA 砍维度、Compressor 砍条数、Indexer 选 top-k——都在压路径,没人动地图。 这不是算法的胜利,是语言本身的低维性质。