从 O(n²) 到 O(n):DeepSeek NSA 与 Kimi MoBA 稀疏注意力底层原理深度拆解
!封面(https://picsum.photos/seed/17867203758764/800/400)
引言:开源浪潮背后,注意力正在被重写
2026 年 8 月 13 日,DeepSeek V4 Pro 正式版上线;紧接着月之暗面宣布将全球参数规模最大的开源大模型 Kimi K3 全量开源,阿里 Qwen3.8-2.4T-A95B 也把 Max 级旗舰权重开放。这波"中国开源军团"集体冲刺的底气,不只是算力和数据,更来自一套底层架构的成熟------训练时就内建的稀疏注意力机制。
中国信通院《人工智能产业发展研究报告》明确把 DeepSeek 的 NSA(Native Sparse Attention) 和月之暗面的 MoBA(Mixture of Block Attention) 列为提升大模型推理效率的关键技术路径。两者都在 2025 年 2 月 18 日前后脚发布,被戏称为"一时瑜亮"。它们要解决的是同一个痛点:当上下文从 64K 冲向 1M token,传统 Full Attention 的 O(n²) 计算和线性膨胀的 KV Cache,正在把推理成本和显存双双逼到墙角。
本文不聊 API、不聊跑分,只从底层把这两套机制拆开:注意力为什么稀疏、KV Cache 为什么是瓶颈、块级稀疏如何对齐硬件、以及 NSA 和 MoBA 到底差在哪。
一、问题本源:注意力是 O(n²) 的,KV Cache 是吃显存怪兽
先复习一下 Self-Attention 的公式:`Attention(Q,K,V) = softmax(QK^T / √d) · V`。对于长度为 n 的序列,QK^T 是一个 n×n 的矩阵,计算量随序列长度二次方爆炸。
更致命的是 KV Cache。自回归解码时,每个历史 token 的 K、V 都要缓存下来供后续步骤复用,显存占用为:
python
def kv_cache_bytes(seq_len, n_layers=32, n_kv_heads=8, head_dim=128, dtype_bytes=2):
"""估算单条请求的 KV Cache 显存占用(字节)"""
return 2 * n_layers * n_kv_heads * head_dim * seq_len * dtype_bytes
# 一条 128K 上下文的请求,Llama-70B 量级配置:
print(f"{kv_cache_bytes(131072) / 1024**3:.2f} GB") # ≈ 8 GB / 请求
单条请求 8GB、并发 100 条就是 800GB------KV Cache 成了"不参与计算却占满显存"的隐形怪兽。而解码阶段每生成一个 token 都要把整条序列的 KV 读一遍,访存(memory-bound)取代计算成为瓶颈。
二、注意力本质上是稀疏的:5% 的 token 扛起 95% 的注意力
优化之前,先问一个问题:注意力矩阵真的需要"全量"计算吗?
大量研究给出了否定的答案。H2O(Heavy-Hitter Oracle)论文发现,只需保留约 5% 的"重击 token"(Heavy Hitters),就能复现接近全量注意力的输出;CNN/DailyMail 摘要任务上的实验显示,即使 KV Cache 砍掉 50%,关键 token 的累计注意力得分仍能保持 90%~95%。换句话说,绝大多数 token 之间的注意力分数趋近于零------稀疏是注意力的天然属性,Full Attention 是在为大量无意义的计算买单。
但"知道稀疏"和"利用稀疏"之间隔着天堑:如果稀疏模式是数据相关的、动态的,怎么在 GPU 上高效实现?这正是 NSA 与 MoBA 的用武之地。
三、块级稀疏:把"选 token"变成"选块"
早期稀疏方案(如滑动窗口)是静态的:只看邻近的 w 个 token,O(n·w),但对远距离依赖无能为力。动态稀疏(按 token 重要性挑选)理论上更好,但 token 级 gather/scatter 在 GPU 上是灾难------不规则的内存访问会把带宽优势全部磨平。
NSA 和 MoBA 的共识是:以块(block)为单位做稀疏。把序列切成固定大小(如 64/128 token)的 KV 块,先给每个块算一个"代表向量",让 query 和块代表向量打分,选 Top-K 个块,再只在这 K 个块上做细粒度注意力。块级粒度的好处是:内存访问连续、形状规则,能对齐 GPU 的 tile 访存,把稀疏性真正转化为带宽收益。
下面用 PyTorch 写一个最小可运行的块级 Top-K 稀疏注意力(MoBA 的核心思路):
python
import torch
import torch.nn.functional as F
def block_sparse_attention(q, k, v, block_size=64, top_k=8):
"""MoBA 风格块级稀疏注意力:Q 按块路由到 Top-K 个 KV 块"""
B, H, T, D = q.shape
n_blocks = T // block_size
# 1) 把 K 按块压缩成代表向量(块内均值池化)
k_blocks = k.view(B, H, n_blocks, block_size, D).mean(dim=3) # (B,H,nb,D)
# 2) 路由打分:每个 Q 块用代表向量与所有 KV 块打分,选 Top-K
q_blocks = q.view(B, H, n_blocks, block_size, D).mean(dim=3) # (B,H,nb,D)
scores = torch.einsum("bhnk,bhmk->bhnm", q_blocks, k_blocks) # (B,H,nb,nb)
top_idx = scores.topk(top_k, dim=-1).indices.sort(dim=-1).values # 升序便于切片
out = torch.zeros_like(q)
for i in range(n_blocks):
sel = top_idx[:, :, i] # (B,H,K)
k_sel = torch.gather(k_blocks, 2, sel.unsqueeze(-1).expand(-1,-1,-1,D))
# 细粒度注意力只在被选中的块上计算
# (工程实现会用 block-sparse GEMM / cuDNN 加速,这里展示语义)
att = torch.einsum("bhtd,bhkd->bhtk", q[:, :, i*block_size:(i+1)*block_size], k_sel)
att = att / (D ** 0.5)
att = F.softmax(att, dim=-1)
v_sel = torch.gather(v.view(B,H,n_blocks,block_size,D), 2,
sel.unsqueeze(-1).unsqueeze(-1).expand(-1,-1,-1,block_size,D))
out[:, :, i*block_size:(i+1)*block_size] = torch.einsum(
"bhtk,bhkvd->bhtvd", att, v_sel).reshape(B, H, block_size, D)
return out
计算量从 O(n²) 降到 O(n·top_k·block_size),而 top_k·block_size 远小于 n。稀疏注意力是线性复杂度的,序列越长优势越大------这正是长上下文模型(Kimi K3、DeepSeek V4 系列动辄百万 token 上下文)的底气。
四、NSA:三种注意力分兵合击
DeepSeek 的 NSA 是"混合策略"的集大成者,每条 query 同时走三条分支:
-
压缩分支(Compressed):沿序列维度做可学习的 token 压缩,把历史信息压成粗粒度表示,负责捕捉全局语义------这是对远处历史的"速览";
-
滑动窗口分支(Sliding Window):最近的 w 个 token 无条件参与注意力,保证局部精度;
-
选择分支(Selected):块级稀疏,按压缩后的代表性 key 打分,动态挑选 Top-K 个历史块做细粒度计算。
三个分支的结果拼接后统一做 softmax。妙处在于"分治":全局靠压缩、局部靠窗口、关键靠选择。NSA 论文报告在 64K 解码时相比 Full Attention 有显著加速,且训练阶段就引入稀疏,避免了"训练用稠密、推理用稀疏"的分布偏移------这是此前大量推理期剪枝方案效果打折的根本原因。
值得展开的是"硬件对齐"这个容易被忽视的细节。NSA 在实现上借鉴了稀疏矩阵计算的成熟思路:把稀疏模式约束为块内稠密、块间稀疏 的规整结构(类似 NVIDIA 2:4 结构化稀疏的推广),让 GPU 的 Tensor Core 可以按固定 tile 形状批量计算,同时减少对 `torch.nonzero` 之类不规则索引的依赖。论文还专门做了针对长序列解码的 kernel 优化,通过预计算压缩索引、避免重复的索引查找,把"选块"的开销摊薄到可忽略的程度。这也是为什么 NSA 能同时拿到训练加速和推理加速------稀疏结构从第一天起就是为硬件设计的,而不是事后补救。
五、MoBA:把 MoE 的路由思想搬进注意力
MoBA 的思路更"激进"也更优雅:既然 MoE 能用路由让每个 token 只激活部分专家,为什么不让每个 Q 块只路由到自己关心的 KV 块? 它几乎复刻了 MoE 的三件套------块切分、门控打分、Top-K 选择,把"选专家"换成"选 KV 块"。
一个值得注意的工程细节:MoBA 作者在实践中发现,路由自由度高反而可能抵消收益------当每个 Q head 各自为政、选中的块互不相交时,IO 优化会被磨平。MHA(每 Q 独享 KV)下 MoBA 效果最好,GQA 次之,MQA(多 Q 共享一份 KV)下最差。这也解释了为什么 2026 年的长上下文模型在注意力头设计上如此谨慎。
六、NSA vs MoBA:同门师兄弟的三处分歧
虽然内核相似,两套方案在细节上分道扬镳:
全局策略不同。NSA 保留了静态的滑动窗口分支作为"保底",局部上下文永远不被稀疏裁剪,因此对短距离依赖更稳;MoBA 则完全依赖路由,连邻近块都要靠路由选中才会参与计算,纯度更高但更"赌"。
压缩方式不同。NSA 使用可学习的 token 压缩网络(类似低秩投影),压缩表示本身参与训练;MoBA 则用块内 mean-pooling 之类的固定聚合得到块代表向量,简单直接、零额外参数。
兼容性取向不同。NSA 为现代 GQA/MQA 架构做了针对性设计,宣称与这些高效注意力头结构兼容;MoBA 论文则坦言其路由机制在 MHA 下收益最大、MQA 下收益最差,工程上对注意力头数量的选择更敏感。
一句话总结:NSA 是"工程派",用混合策略求稳;MoBA 是"架构派",把 MoE 哲学贯彻到底。二者互相印证了"块级稀疏 + 训练时内建"是长上下文时代绕不开的方向。
七、工程落地:稀疏不只是论文里的故事
对普通开发者,这篇文章的落点很简单:当你的 RAG 知识库、Agent 长记忆、代码仓库级上下文开始"卡显存"时,别再盲目堆卡------先算算 KV Cache 账,再看看你的推理框架(vLLM、SGLang)是否已开启稀疏/量化路径。2026 年的开源旗舰(Kimi K3、DeepSeek V4 系列、Qwen3.8)已经把稀疏注意力做成出厂配置,这意味着同样的显存预算下,你能跑的上下文长度和并发数都比上一代模型高一个量级------这不是渐进优化,而是架构级的重新定价。
最后留一个思考题:如果注意力可以稀疏,那么 KV Cache 的存储结构、显存分配器、甚至 PCIe 带宽规划,是不是都该为"块级稀疏"重新设计?注意力稀疏性,是继 MoE 之后大模型"降本增效"的下一张底牌------牌已经打出来了,读懂它的人将率先吃到长上下文时代的第一波红利。