在 vLLM 这类推理引擎中,模型并不是一个抽象的黑盒。真正决定显存占用、内核实现和并发能力的,是一组写在模型配置里的架构参数:query 头数、KV 头数、每头维度、位置编码方式、FFN 是否稀疏化等。它们共同决定了 KV cache 有多大、attention 内核怎么写,以及一张 GPU 能否承载长上下文推理。
一份围绕 vLLM 模型架构基础的教程材料,将这些术语集中整理为一条主线:从 MHA、MQA、GQA 到 MLA,再到 RoPE、ALiBi 与 MoE,模型架构的变化本质上是在回答同一个问题——如何在推理阶段更省显存、更快计算,同时尽量保留模型能力。
注意力头数设计,直接改写 KV cache 规模
教程首先拆解了现代 LLM 解码器层的基本结构:输入经过 LayerNorm 或 RMSNorm 后,一路进入多头注意力,另一路进入 FFN,两者都带有残差连接。注意力通过 Q、K、V 投影完成,FFN 通常由两层线性变换与中间激活组成。这个基础框架多年未变,真正变化的是注意力头如何组织、位置编码如何加入,以及 FFN 是否引入稀疏专家。
在注意力部分,KV cache 的大小首先取决于 K/V 头的数量。标准 MHA 中,每个 query 头都有对应的 K 头和 V 头,因此缓存开销最大。GPT-2、GPT-3、早期 LLaMA-1 等模型采用这种方式,推理时每个 token 都要保存全部头对应的 K 和 V。
MQA 则走向另一个极端:所有 query 头共享一个 K/V 头,KV cache 可以压缩到 MHA 的 1/n_head。Falcon、PaLM、StarCoder 是代表模型。这种方式显存优势明显,但表达能力会下降,因此后来更常见的折中方案是 GQA。
GQA 将 query 头分成若干组,每组共享一个 K/V 头。以 LLaMA-3.1-8B 为例,模型有 32 个 query 头,但 num_kv_heads 只有 8。按照 head_dim=128、32 层、fp16 计算,每个 token 的 KV 约为 131,072 B,即约 128 KB;如果使用 MHA,则同一模型会达到 512 KB,相差 4 倍。这也是当前主流开源模型普遍采用 GQA 的原因:在质量接近 MHA 的同时,显著降低推理显存成本。
教程给出的判断方法也很直接:估算一个模型的 KV cache,先看 num_kv_heads。对于长上下文和高并发服务来说,GQA 已经成为现实基础。
MLA 与滑动窗口:进一步压缩上下文开销
在 GQA 之外,DeepSeek-V2、DeepSeek-V3、DeepSeek-R1 和 MiniMax-01 等模型采用了更激进的 MLA,即 Multi-head Latent Attention。它不再缓存完整的 K/V,而是缓存一个低维潜在向量。推理时,再根据需要把这个潜在向量展开为 K 和 V。
相比 MHA,MLA 可以把 KV cache 压缩到约 1/10 甚至更低。教程提到,DeepSeek-V3 的相关字段很小,在 128K 以上超长上下文场景中显存优势明显。不过,这也给推理引擎带来更高要求:attention 内核必须先读取潜在向量,再展开为 K/V,最后参与注意力计算。为此,vLLM 为 MLA 实现了专用内核,并针对 CUDA、Triton、ROCm 等不同后端做适配。
教程还解释了 FP8 KV cache 与 MLA 的关系。使用 –kv-cache-dtype fp8 时,被量化的对象是缓存中的潜在向量,而不是展开后的 K/V。由于潜在向量维度更小,量化带来的收益和风险都集中在同一个张量上,最终效果会因模型而异。
另一类降低长序列开销的机制是滑动窗口注意力。Mistral 7B 在 GQA 之上引入窗口设计,每个 token 只关注其之前 4096 个 token,而不是全部历史。实现上,这相当于把因果掩码的左边界从 0 移动到当前位置减去窗口长度,超出范围的注意力分数直接置为负无穷。vLLM 会读取 Mistral config.json 中的 sliding_window 字段,并在 attention 内核中按 use_sliding_window 分支处理。
教程同时提到,StreamingLLM 的思路与滑动窗口不同:它保留开头的 attention sink token,并结合滚动窗口,以支持更长的流式输入。这说明围绕长上下文的优化,并不只是简单扩大窗口,而是在缓存、掩码和生成稳定性之间做工程取舍。
位置编码与 MoE,决定长文本外推和计算形态
注意力本身不感知 token 顺序,因此位置信息必须显式注入。教程梳理了几种常见位置编码方案:learned 绝对位置、Sinusoidal、RoPE、ALiBi 以及 XPos。其中,RoPE 是当前主流 LLM 的默认选择,LLaMA、Mistral、Qwen、Gemma、Phi、DeepSeek 等模型均采用。
RoPE 的做法是对 Q 和 K 的维度对按位置角进行旋转,使点积中包含相对位置信息。实现上不需要构造完整旋转矩阵,而是通过交错维度和 cos、sin 完成。参数 rope_theta 决定基础频率,数值越大,在长位置外推时越稳定。LLaMA 使用 500000,Qwen 使用 1000000 以上。
对推理引擎而言,RoPE 在进入注意力之前完成,因此 vLLM 的 ModelRunner 需要为每个 token 维护准确的位置信息。prefill 与 decode 拼接时,位置也必须是全局绝对位置,不能重置。
当模型要推理超过训练长度的序列时,单纯调大 –max-model-len 并不够。教程列举了几类常见外推方式:NTK-aware 和 Dynamic NTK 通过缩放 rope_theta 改变频率分布,YaRN 结合 NTK 缩放与注意力温度修正,Context Scaling 则通过插值和微调扩展上下文长度。vLLM 侧更多负责加载已扩展上下文的模型文件,并读取 config.json 中的 rope_scaling 配置。
ALiBi 则采用另一种方案:在注意力分数中加入随距离变化的线性偏置。MPT 和部分 BLOOM 模型使用这一方式。它的优势是零参数,并天然支持一定程度的长度外推;不足在于长距离信息虽然可见,但会被明显弱化。对引擎来说,ALiBi 要求 attention 内核在因果掩码之外额外处理距离偏置。
除了注意力与位置编码,教程还提到 FFN 的稀疏化方向,即 MoE。与传统稠密 FFN 不同,MoE 通过多个专家结构,使每个 token 只激活部分参数。这改变的不是 KV cache 大小,而是模型权重组织和计算调度方式,也是大模型推理系统需要单独适配的重要原因。
架构参数成为推理工程的入口
从 vLLM 的视角看,模型架构并不是论文里的概念集合,而是一组直接影响部署成本的工程约束。vocab_size、n_embd、n_layer、n_head、head_dim、num_kv_heads、rope_theta、sliding_window 等字段,共同驱动显存预算、内核选择和并行策略。
这也是为什么同一个推理引擎需要为不同模型写不同处理逻辑:GQA 需要按组广播 K/V,MLA 需要低秩展开内核,RoPE 需要逐 token 位置计算,ALiBi 需要额外偏置,MoE 则涉及专家路由与稀疏计算。
对于开发者而言,这些架构细节决定了模型能不能跑起来、能跑多长上下文、能支持多少并发。对于行业而言,模型架构正在从单纯追求表达能力,转向同时为推理效率服务。GQA、MLA、RoPE scaling 和 MoE 的流行,本质上都是大模型进入部署阶段后,对显存、吞吐和成本做出的工程回应。
原创文章,作者:点点,如若转载,请注明出处:https://www.dian8dian.com/vllm-jiao-cheng-chai-jie-mha-gqa-mla-rope-yu-moe-ru-he-jue