仓库地址:github.com/hhk-png/cyc...
本章目标:把 vLLM 服务的那"一堆模型"讲清楚。前几章反复出现的
num_kv_heads、head_dim、GQA、MLA、RoPE、MoE 等术语,在这里一次性系统讲透------它们直接决定 KV cache 大小、attention 内核怎么写、模型能不能塞进一张卡 。读完本章,你会明白"为什么 70B 模型的 KV 只比 8B 大 2.5 倍""为什么 DeepSeek-V3 的 KV cache 那么小""为什么 vLLM 要对不同模型写不同的内核"。这也是一份可以按需查阅的模型架构速查手册。
1. 为什么引擎必须懂模型架构
vLLM 是一个"模型无关"的 serving 引擎:它把调度、KV cache、采样、服务这些机制通用化,而把"模型长什么样"抽象成 ModelConfig 里的一组元信息:
python
@dataclass
class ModelConfig:
vocab_size: int
n_embd: int # hidden size(d_model)
n_layer: int # transformer 层数
n_head: int # query 头数
head_dim: int # 每头维度
# GQA 模型还有:
num_kv_heads: int # KV 头数(MQA/GQA 时 < n_head)
# 可选:
use_sliding_window: bool
rope_theta: float # RoPE 的 base(影响长上下文外推)
这些字段不是摆设,它们直接驱动三件事:
- KV cache 的尺寸 :
每 token KV 字节数 = 2 × num_layers × num_kv_heads × head_dim × dtype。num_kv_heads一改,显存预算全变(详见第 02/17 章)。 - attention 内核的写法 :因果掩码、RoPE 位置编码、滑动窗口、分组 KV,全都发生在
vllm/attention/的 kernel 里。 - 模型加载与切分 :
model_executor根据头数/层数把权重切给张量并行 / 专家并行的各张卡。
一句话:模型架构 = 决定"KV cache 多大、内核怎么算、能不能并行"的三元组。 理解架构,就是理解 vLLM 一切调参背后的物理原因。
2. Transformer 复习(极简)
一个现代 LLM 的解码器层 = 两个子层,各自带残差连接:
css
input (hidden states, 形状 [seq, d_model])
│
├─ LayerNorm/RMSNorm ──► 多头注意力(+ 位置编码) ──► + input(残差)
│
├─ LayerNorm/RMSNorm ──► FFN(升维再降维) ──► + input(残差)
│
└─ 输出到下一层
- 注意力 :
Attention(Q, K, V) = softmax(QK^T / √d) V。Q/K/V 由隐藏状态乘投影矩阵得到,按头切分。 - FFN :两层线性,中间有个非线性激活(
activation(hW1) W2),中间维度通常是4 × d_model。 - embedding 层 :把 token id 映射成
d_model维向量(wte);位置信息由位置编码提供。 - LM head :最后一层把 hidden state 映射回
vocab_size维 logits;很多模型把它与输入 embedding 权值绑定(tied embeddings),省显存------mini-vLLM 的 TinyGPT 就是这么做的。
这一层结构二十年来基本没变。真正变的是三个子模块的"零件":多头注意力的头怎么分、位置编码怎么加、FFN 的激活是什么、FFN 是否稀疏(MoE)。下面逐一看。
3. 注意力头变体:MHA → MQA → GQA → MLA
注意力投影按"头"组织。n_head 个 query 头各自投影,但 K/V 头可以少于 Q 头------这是模型设计者用来压缩 KV cache 的核心手段。
3.1 MHA(Multi-Head Attention)
每个 Q 头都有自己独立的 K/V 头:
css
Q 头数 = n_head,K/V 头数 = n_head(一一对应)
- 代表模型:GPT-2、GPT-3、LLaMA-1(早期)、BERT。
- KV cache:最大。
num_kv_heads = num_heads,每 token 的 KV 直接按全头数算。 - vLLM 处理:最朴素,无特殊内核需求。
3.2 MQA(Multi-Query Attention)
所有 Q 头共享同一个 K/V 头:
css
Q 头数 = n_head,K/V 头数 = 1
- 代表模型:Falcon、PaLM、StarCoder。
- KV cache:缩到
1/n_head,极小。 - 代价:表达能力下降,大模型质量略受影响(学术界后来用 GQA 作为折中)。
3.3 GQA(Grouped-Query Attention)
把 Q 头分成若干组,每组共享一个 K/V 头:
css
Q 头数 = n_head,K/V 头数 = g(g < n_head),每组 n_head / g 个 Q 头共享 1 个 KV 头
- 代表模型:LLaMA-2/3、Mistral、Qwen2/2.5、Gemma、Phi-3.5、Mixtral------主流开源模型的默认选择。
- KV cache:缩到 MHA 的
1/g(例如 8B 模型n_head=32, num_kv_heads=8→ KV 是 MHA 的 1/4)。 - 为什么主流:质量接近 MHA,但 KV 显存省 4 倍------推理成本直接下降。
具体算例(LLaMA-3.1-8B):
ini
n_head = 32, num_kv_heads = 8, head_dim = 128, L = 32 层, fp16
每 token KV = 2 × 32 × 8 × 128 × 2 B = 131,072 B ≈ 128 KB
若用 MHA(num_kv_heads = 32):2 × 32 × 32 × 128 × 2 B = 512 KB ------ 4 倍差距
这正是第 08/17 章容量规划里那个 128 KB/token 的来源。
3.4 MLA(Multi-head Latent Attention,DeepSeek-V2/V3)
GQA 已经是压缩 KV 的成熟方案,DeepSeek 更进一步:把 K/V(甚至 Q)压缩进一个低秩"潜在向量"(latent vector),推理时再动态展开。
ini
传统:每 token 缓存完整 K/V,形状 [num_kv_heads, head_dim]
MLA:每 token 只缓存一个潜在向量 c(维度 d_c << num_kv_heads × head_dim)
需要 K/V 时:K/V = W_kv_up × c(低秩展开)
- 代表模型:DeepSeek-V2 / V3 / R1、MiniMax-01 等。
- KV cache:相比 MHA 可缩到 1/10 甚至更小 。DeepSeek-V3 的
num_kv_heads相关字段很小,超长上下文(128K+)下显存优势巨大。 - 对引擎的挑战:attention 内核必须支持"先读潜在向量 → 展开成 K/V → 再参与注意力"。vLLM 为此实现了 MLA 专用内核 (
vllm/attention/backends/mla/),并针对不同后端(CUDA / Triton / ROCm)分别适配。
3.5 四种变体对比
| 变体 | K/V 头数 | KV 相对 MHA | 代表模型 | vLLM 注意点 |
|---|---|---|---|---|
| MHA | n_head |
1x | GPT-2、LLaMA-1 | 无 |
| MQA | 1 |
1/n_head |
Falcon、PaLM | 广播 K/V |
| GQA | g(1 ≤ g < n_head) |
1/g |
LLaMA-2/3、Mistral、Qwen2 | 按组广播,当前主流 |
| MLA | 潜在向量 | ~1/10 或更小 |
DeepSeek-V2/V3 | 专用内核,低秩展开 |
对读者的意义 :判断一个模型 KV cache 多大,先看
num_kv_heads。GQA/MLA 是"长上下文 + 大并发"的现实基础------没有它们,128K 上下文在单卡上根本放不下。
3.6 对 vLLM 引擎的影响
- KV cache 分配 :
CacheConfig计算 block 数时用num_kv_heads × head_dim,GQA/MLA 直接放大可缓存 token 数。 - attention 内核 :GQA 需要"一个 KV 头服务多个 Q 头"的广播读取;MLA 需要低秩展开路径。vLLM 按
attention_backend分别实现。 - 权重加载 :MQA/GQA 的 K/V 投影权重形状与 MHA 不同(
[num_kv_heads, head_dim]而非[n_head, head_dim]),load_weights要按头数做 reshape/广播------这是"自定义模型接入"(第 11 章)里最常见的坑。 - attention 后端选择 :vLLM 的 attention 后端(
VLLM_ATTENTION_BACKEND,或--attention-backend)决定用哪套内核------FLASH_ATTN(flash-attn 库,prefill 用 varlen、decode 用分页内核)、FLASHINFER、TRITON_ATTN、XFORMERS、TORCH_SDPA。不是所有内核都支持所有架构(如 MLA 需要专用mla后端、SSM 需要专用路径),启动日志会提示"该后端不支持此模型"并自动回退。
3.7 滑动窗口注意力(Sliding Window Attention,Mistral)
Mistral 7B 在 GQA 之上再加一档:每个 token 只 attend 到它之前 W 个 token(Mistral 取 W=4096),而不是整个前缀:
css
window_mask(i, j) = 允许 当 0 ≤ i − j ≤ W,否则设为 −∞
在 kernel 里怎么工作 :窗口掩码只是因果掩码的"左边界"从 0 移到 i − W------score(i,j) 对 j < i − W 直接压成 −∞,其余照常 softmax。vLLM 的 attention kernel 读 Mistral config.json 的 sliding_window 字段,按 use_sliding_window 分支生成这个掩码(vllm/attention/)。
好处:
- 每步注意力复杂度从
O(L)降到O(W)------KV cache 仍按全长度存,但长上下文下的计算与显存读取显著下降; - 窗口固定时,理论上任意长外推不增加单步成本。
局限:
- 长距离依赖被切断:超过 W 的信息只能靠"逐窗口接力"间接传递,深层全局信息衰减;
- KV cache 显存不省 :窗口是"计算掩码",不是"缓存策略"------它仍要存全部前缀的 KV,除非再配 Rolling KV Cache(把窗口之外的 block 直接丢弃),后者才真省显存,但也彻底失去"回看窗口之外"的能力。
与 StreamingLLM 的关系:StreamingLLM 发现"保留开头的 attention sink token + 滚动窗口"能让任意长流式输入稳定生成。Mistral 的滑动窗口是"只留最近 W",StreamingLLM 是"最近 W + 最开头几个 sink"------对长上下文服务的讨论见第 12 章。
3.8 MLA 深入:潜在压缩、缓存布局与内核差异
潜在压缩(Latent Compression) :MLA 不再缓存"每头完整的 K/V",而是缓存一个低秩潜在向量 c:
ini
对第 i 个 token:
c_i = W_dkv × h_i (低秩投影,维度 d_c,DeepSeek 系约几百维)
K_i = W_uk × c_i (用的时候展开回完整 K,[num_kv_heads, head_dim])
V_i = W_uv × c_i (用的时候展开回完整 V)
缓存布局:cache 里只存 c_i(形状 [d_c]),而不是 K_i / V_i([num_kv_heads×head_dim] × 2)
cached_latent vs 展开后的 K/V:
- cached_latent (入缓存的东西):
c_i,低秩潜在向量------这正是--kv-cache-dtype控制精度的那个张量; - decompressed K/V (用时才展开):
K_i / V_i,前向里由c_i乘W_uk / W_uv得到,参与注意力后即弃,不进 cache。
为什么内核不一样:
- 标准 PagedAttention 直接读 K/V block 里的
[num_kv_heads, head_dim];MLA 内核要先读潜在向量 block、再现场低秩展开 (W_uk × c、W_uv × c),多一步矩阵乘; - 同一个
c_i展开的 K/V 会被多个 Q 头复用,展开结果适合"算一次、广播用",内核要为此做共享优化; - 展开矩阵
W_uk / W_uv必须随权重加载并常驻计算侧,内核要能拿到它们;Q 侧同样可能被压缩(DeepSeek-V2 对 Q 也做低秩),所以 MLA 内核还要处理"压缩的 Q → 展开的 Q"。
与 --kv-cache-dtype 的互动 :--kv-cache-dtype fp8 量化的对象是 cached_latent (c_i)而非展开后的 K/V。潜在向量维度小、又是低秩表示,量化误差更容易被"展开 + 注意力"这一整段分摊;因此 MLA 模型上开 KV 量化,收益(KV 减半)与风险(精度损失)都集中在 c_i 一个张量上,实际效果因模型而异,以官方文档与实测为准。
对容量规划的意义:DeepSeek-V3 的每 token KV 只有几百字节(相对 MHA 的几百 KB),这就是第 17 章附录 B 表格里 MLA 一行"远小于 MHA/GQA"的物理来源------也是它能塞下 128K 上下文的原因。
4. 位置编码:绝对位置 → RoPE → ALiBi
自注意力本身不感知 token 顺序(把 token 序列打乱,QK^T 结果不变)。位置信息必须显式注入。位置编码直接决定"模型能理解多长的相对关系""超长输入如何外推"。
4.1 绝对位置编码(learned/正弦)
- Learned 绝对位置 :训练一个位置 embedding 表
wpe[position],与词向量相加。GPT-2 用这个。block_size(最大上下文)受位置表大小限制。 - 正弦位置编码:用固定频率的 sin/cos 函数生成位置向量。早期 Transformer(原版、BERT)用这个,外推能力弱。
mini-vLLM 的 TinyGPT 就是 learned 绝对位置 (wpe[positions])------最简单,适合教学。
4.2 RoPE(Rotary Position Embedding,旋转位置编码)
当前主流 LLM 的默认选择(LLaMA、Mistral、Qwen、Gemma、Phi、DeepSeek......)。
思想:给 Q 和 K 的每个维度对((d, d+1))按位置角 θ = position × base^{-2d/D} 做一个旋转:
css
[q_rot, k_rot] 旋转后,点积 Q·K 会包含 (position_i - position_j) 的相对位置信息
- 实现上不需要显式构造旋转矩阵,而是把 Q/K 交错成对、用
cos/sin做两次旋转相加(vLLM 的rotate_half)。 - 关键参数
rope_theta(LLaMA 用 500000,Qwen 用 1000000+):决定基础频率,theta越大,外推长位置时数值越稳。 - 对引擎的影响 :RoPE 在 Q/K 进入注意力之前 做,位置由
positions数组提供------所以 vLLM 的ModelRunner必须为每个 token 计算positions(slot_mapping/positions张量)。prefill 与 decode 拼接时,位置是绝对位置(全局 token 下标),不能重置。 - 长上下文外推 :直接用 RoPE 训练长度推理更长的序列,位置编码会超出训练分布,质量下降。缓解手段:
- NTK-aware / Dynamic NTK :按实际序列长度缩放
rope_theta(即"调高频分量"),不改权重即可外推。 - YaRN:结合 NTK 缩放 + 注意力温度修正,外推效果更好。
- Context Scaling(context length extension):把位置 embedding 插值后微调几千步(如 Qwen 官方把 32K 扩到 128K)。
- vLLM 侧:
--max-model-len只决定显存预算,不改模型;外推通常靠加载"已扩展上下文"的模型文件 + 在config.json里设置rope_scaling。
- NTK-aware / Dynamic NTK :按实际序列长度缩放
4.3 ALiBi(Attention with Linear Biases)
给注意力分数加一个随距离线性增长(或衰减)的偏置:
css
score(i, j) = q_i · k_j - m × |i - j| (m 是每头一个的斜率)
- 代表模型:MPT、BLOOM(部分)。
- 好处:零参数、天然支持任意长度外推(训练 2048 可直接推 8192);坏处:长距离"看得见但很模糊"。
- 对引擎的影响:attention 内核需要在 causal mask 之外再加一个距离偏置项。
4.4 位置编码速查
| 方案 | 机制 | 相对位置感知 | 长上下文外推 | 代表模型 | vLLM 处理 |
|---|---|---|---|---|---|
| Learned 绝对 | 位置表 + 词向量 | 隐含 | 差(表有界) | GPT-2、早期 GPT | 直接按 position 查表 |
| Sinusoidal | sin/cos 固定函数 | 隐含 | 差 | 原版 Transformer | 少见 |
| RoPE | Q/K 按位置旋转 | 显式 | 中(需 scaling) | LLaMA、Qwen、Mistral | 每 token 算 positions + rope_theta |
| ALiBi | 加距离偏置 | 显式 | 好 | MPT | 内核加偏置 |
| XPos | RoPE 的分辨率外推变体(指数衰减项) | 显式 | 好 | 线性注意力/长上下文模型 | 视模型实现 |
一句话:RoPE 是主流,ALiBi 是"零参数外推"的备选;长上下文靠"改 RoPE 缩放(NTK/YaRN)+ 微调",而不是只调
--max-model-len。 细节见第 12 章《长上下文服务》。
5. 归一化与激活函数
5.1 LayerNorm vs RMSNorm
| 特性 | LayerNorm | RMSNorm |
|---|---|---|
| 计算 | (x - mean) / std × γ + β |
x / sqrt(mean(x²)) × γ(无均值偏移) |
| 参数 | γ 和 β | 仅 γ |
| 速度 | 慢一点(多了均值和 bias) | 快,省内存 |
| 代表模型 | GPT-2、BERT | LLaMA、Mistral、Qwen、Gemma |
RMSNorm 在 2023 年后的开源 LLM 中几乎是标配。对引擎而言只是 kernel 的选择,无显存级影响。
5.2 GELU vs SwiGLU
FFN 的激活决定中间层结构:
- GELU :
x × Φ(x),GPT-2、BERT 用。 - SwiGLU :
SiLU(xW1) ⊙ (xW3) × W2------**门控(gating)**线性单元,多了一组权重,表达能力更强。LLaMA-2/3、Mistral、Qwen、Gemma、PaLM 全用 SwiGLU。
对显存的影响:SwiGLU 的 FFN 有三个权重矩阵(w1/w3/w2),权重文件比"两个矩阵"的 GELU FFN 略大;但这是权重层面的差别,与 KV cache 无关。
6. MoE(Mixture-of-Experts)
6.1 原理
MoE 把 FFN 层从"一个稠密网络"换成"一个路由器 + N 个专家(expert)"。每个 token 经路由选 top-k 个专家(通常 top-1 或 top-2)计算:
csharp
常规 FFN: h ──► 一个大 FFN ──► out
MoE FFN: h ──► router(选 top-2 专家)
├─► expert_3 ─┐
├─► expert_7 ─┼─► 加权求和 ──► out
└─► (其它专家不参与) (被选中的专家权重加权)
- 代表模型:Mixtral-8x7B (8 专家 top-2)、DeepSeek-V2/V3(细粒度专家 + 共享专家)、Qwen2-MoE。
- 关键数字 :
Mixtral-8x7B总参数 47B,但每个 token 只激活约 13B------参数量大、激活参数小。
6.2 对推理的影响
| 维度 | 影响 |
|---|---|
| 权重显存 | 全部专家权重都要加载(47B ≈ 94GB fp16),显存按总参数量算(第 17 章附录 B 明确提醒) |
| 计算量 | 只有被路由到的专家参与,计算量接近"激活参数"规模 |
| 调度 | 同一批 token 路由到不同专家,产生 All-to-All 通信(第 14 章专家并行) |
| KV cache | MoE 只改 FFN,KV cache 公式不变(由注意力头的配置决定) |
6.3 vLLM 的 MoE 支持
vllm/model_executor/layers/fused_moe/:fused MoE kernel,把多个专家的计算融合进一个 kernel,减少启动开销。- 专家并行:
--expert-parallel-size(或由--tensor-parallel-size自动推导),把专家分布到多卡。 - 通信优化:all-to-all 与计算重叠、激活量化、请求分组提升专家亲和(详见第 12 章 §4)。
6.4 循环 / 状态空间(SSM)与混合架构
不是所有新模型都走 attention。状态空间模型(SSM) 用固定大小的"状态"代替随序列增长的 KV cache,复杂度 O(1) 内存、O(n) 时间:
- Mamba / Mamba-2 :selective SSM,把 K/V 的"无限长历史"压缩进一个固定状态向量;推理时没有逐 token 增长的 KV cache,decode 的内存访问模式完全不同;
- 混合架构(Jamba、Zamba、RecurrentGemma、Falcon-Mamba):attention 层与 SSM 层交错堆叠------attention 负责"全局长程",SSM 负责"便宜地消化大部分 token";
- 对引擎的意义 :KV cache 不再是"按 token 增长的数组",容量规划、调度、PagedAttention 内核都不适用------vLLM 为 Mamba 系模型实现了专用执行路径(
vllm/model_executor/models/mamba.py与对应的缓存管理);这类模型上 "KV cache 满" 的经典问题会变成 "状态数量/长度限制" 的另一类约束。
一句话:SSM/混合架构是"KV cache 架构级革命"的另一个方向------它们不需要 PagedAttention,但需要引擎为"固定大小状态"重新设计缓存与调度。新模型(MiniMax-01、RWKV 等线性注意力,Kimi K2 的 MoBA 块稀疏)也在朝"别让 KV 随序列线性增长"的方向演进,详见第 12 章 §3.1。
7. vLLM 支持的主流架构速查表
⚠️ 版本说明:vLLM 对新架构支持很快,下表是 2024--2025 年主流的稳定支持;最新支持清单以官方文档
Supported Models页为准 。加载不支持的架构用--trust-remote-code(有安全风险,见第 13 章)。
| 模型家族 | 架构名(config.json 的 architectures 字段) | 注意力 | 位置编码 | FFN | MoE |
|---|---|---|---|---|---|
| GPT-2 | GPT2LMHeadModel |
MHA | Learned 绝对 | GELU | 否 |
| LLaMA / Llama-2 / Llama-3 | LlamaForCausalLM |
GQA(7B+) | RoPE | SwiGLU | 否 |
| Mistral 7B | MistralForCausalLM |
GQA + 滑动窗口 | RoPE | SwiGLU | 否 |
| Mixtral 8x7B | MixtralForCausalLM |
GQA | RoPE | SwiGLU | 8 专家 top-2 |
| Qwen2 / Qwen2.5 | Qwen2ForCausalLM |
GQA | RoPE | SwiGLU | 否 |
| Qwen2-MoE | Qwen2MoeForCausalLM |
GQA | RoPE | SwiGLU | 是 |
| Qwen2-VL | Qwen2VLForConditionalGeneration |
GQA(多模态) | RoPE(M-RoPE) | SwiGLU | 否 |
| Qwen2.5-VL | Qwen2_5_VLForConditionalGeneration |
GQA(多模态,新视觉编码器 + spatial merge) | RoPE(M-RoPE 三维) | SwiGLU | 否 |
| DeepSeek-V2/V3 | DeepseekV2ForCausalLM / DeepseekV3ForCausalLM |
MLA | RoPE | SwiGLU | 是 |
| Phi-3 / Phi-3.5 / Phi-4 | Phi3ForCausalLM 等 |
GQA | RoPE | SwiGLU | 否 |
| Gemma / Gemma-2 | GemmaForCausalLM / Gemma2ForCausalLM |
GQA | RoPE | SwiGLU | 否 |
| Falcon | FalconForCausalLM |
MQA | ALiBi(部分) | GELU | 否 |
| Baichuan 2 | BaichuanForCausalLM |
GQA | RoPE(ALiBi 可选) | SwiGLU | 否 |
| Yi | YiForCausalLM |
GQA | RoPE | SwiGLU | 否 |
| InternLM / InternLM2 | InternLMForCausalLM |
MHA/GQA | RoPE | SwiGLU | 否 |
| ChatGLM / GLM-4 | ChatGLMModel / ChatGLMForConditionalGeneration |
GQA | RoPE | SwiGLU | 否 |
| DeepSeek-MoE | DeepseekMoeForCausalLM |
GQA | RoPE | SwiGLU | 是 |
| Qwen3 | Qwen3ForCausalLM |
GQA | RoPE | SwiGLU | 否(另有 MoE 版) |
| GLM-4 / GLM-4.5 | GlmForCausalLM / Glm4ForCausalLM |
GQA | RoPE | SwiGLU | 否 |
| MiniMax-01 | MiniMaxM1ForCausalLM |
线性注意力 | RoPE | SwiGLU | 是 |
| Kimi K2 | KimiK2ForCausalLM |
GQA + MoBA 块稀疏 | RoPE | SwiGLU | 是 |
| Llama-4 | Llama4ForCausalLM |
GQA + MoA(多模态对齐) | RoPE | SwiGLU | 是 |
| Gemma-3 | Gemma3ForCausalLM |
GQA(多模态) | RoPE | SwiGLU | 否 |
| Mamba / Jamba / RecurrentGemma | MambaForCausalLM / JambaForCausalLM 等 |
状态空间(SSM)/ 混合 | 无位置编码(SSM) | SwiGLU | 部分 |
怎么用这张表:
- 想跑一个模型 → 查它的
architectures字段 → 对照表中是否已支持(支持就直接vllm serve <model>)。 - 想判断 KV cache 大小 → 看"注意力"列的 GQA/MLA → 找
num_kv_heads代公式。 - 想判断能不能多卡 → 看是否 MoE → MoE 优先专家并行。
- 想改 / 自定义模型 → 在第 11 章"模型注册表"的基础上,按本表列出的架构名注册。
8. 架构知识如何落到 vLLM 调参上
| 模型特点 | 对 vLLM 配置的含义 |
|---|---|
| GQA(如 LLaMA-3.1-8B,KV 头 8) | KV 显存小,--max-model-len / 并发可以放大 |
| MHA(如 GPT-2,KV 头 = Q 头) | KV 显存按全头算,注意容量规划 |
| MLA(如 DeepSeek-V3) | KV 极小,128K 上下文也可行;需内核支持 |
| RoPE + 长上下文 | rope_scaling 决定外推能力;--max-model-len 只决定显存 |
| 滑动窗口(如 Mistral) | attention 内核做窗口掩码,长距离依赖受限 |
| MoE(如 Mixtral) | 权重按总参数量算显存;--tensor-parallel-size 自动推导 EP |
| SwiGLU(三权重 FFN) | 权重文件比 GELU FFN 略大,加载稍慢 |
一个贯穿始终的结论:架构选择决定硬件需求。同样 8B 参数量,GQA + 短上下文可以单卡高并发;MHA + 128K 上下文则必须靠 KV 量化 / 并行 / PD 分离。选模型时先看架构,再谈部署。
9. 架构演进对 vLLM 内核的连锁影响
把本章的架构创新按"出现顺序"排成一张时间线,能看清一个规律:每一代架构创新都先在"模型侧"省成本(KV、计算、参数),然后把这笔账转嫁给"内核侧"的复杂度。
| 阶段 | 架构 | 相对前代的改动 | 对 vLLM 内核的连锁影响 | 代表模型 |
|---|---|---|---|---|
| 基线 | MHA | 无 | 最朴素的因果 PagedAttention | GPT-2、LLaMA-1 |
| 压缩 KV | GQA | 多个 Q 头共享 1 个 KV 头 | 内核按组广播 K/V;load_weights 处理 KV 投影的形状 |
LLaMA-2/3、Mistral、Qwen2/2.5 |
| 压缩 KV(激进) | MLA | KV 压成低秩潜在向量 | 专用 MLA 内核:先读潜在向量再低秩展开;Q 侧同样可能被压缩 | DeepSeek-V2/V3 |
| 位置编码 | RoPE | Q/K 按位置旋转 | 每 token 算 positions,Q/K 进注意力前 rotate_half;支持 rope_scaling 外推 |
几乎所有现代模型 |
| 局部性 | Sliding Window | 掩码再加窗口下界 | kernel 生成窗口掩码;可与 Rolling KV Cache 配合省显存 | Mistral |
| 稀疏 FFN | MoE | FFN 换路由 + N 个专家 | fused MoE kernel + all-to-all 通信 + 专家并行;权重按总参数算显存 | Mixtral、DeepSeek-V3 |
vLLM 的 vllm/attention/(GQA 广播、MLA 展开、滑动窗口掩码)与 vllm/model_executor/layers/fused_moe/ 就是这笔"模型侧省下的成本"在内核侧的兑现处------这也解释了第 11 章"为什么新架构要等适配"。
10. 把架构知识串起来的决策链
给定一个模型的 config.json,按这条链走,就能把架构知识直接翻译成部署参数。以 Qwen2.5-7B-Instruct 的 config.json 节选为例:
json
{
"architectures": ["Qwen2ForCausalLM"],
"hidden_size": 3584,
"num_hidden_layers": 28,
"num_attention_heads": 28,
"num_key_value_heads": 4,
"head_dim": 128,
"max_position_embeddings": 131072,
"rope_theta": 1000000.0,
"rope_scaling": { "type": "yarn", "factor": 4.0 },
"quantization_config": { "quant_method": "gptq", "bits": 4 }
}
决策链(读完 config 要回答的 4 个问题):
-
KV cache 多大?
iniper_token = 2 × num_hidden_layers × num_key_value_heads × head_dim × dtype_bytes = 2 × 28 × 4 × 128 × 2 B = 57,344 B ≈ 56 KB(fp16)比同量级 Llama-3.1-8B 的 128 KB 还小一半------因为 Qwen2.5-7B 只有 4 个 KV 头。→ 代入第 17 章附录 B 的公式算并发上限。
-
要不要 RoPE 外推? 看到
rope_scaling: yarn, factor 4:说明官方已把 32K 扩到 128K。此时--max-model-len 131072只是显存预算 ,外推能力来自"这份 config + 已微调的权重文件",而不是 vLLM 现场改。→ 用--rope-scaling时取值必须与 config 一致(见第 16 章附录 A)。 -
量化可行吗?
quantization_config里gptq, bits 4:权重按 4bit 加载,显存 ≈ 7.6B × 0.5 ≈ 3.8 GB(而非 fp16 的 ~15 GB)。注意要下对量化权重文件 ,并与 vLLM--quantization gptq对齐;KV 侧可另开--kv-cache-dtype fp8。 -
并行怎么定? 非 MoE、28 层:TP2 每卡 14 层,权重 / KV 各摊一半;要更高并发再加
--max-num-seqs。若换成 DeepSeek-V3(MoE + MLA),这一题的答案变成"EP 优先 + MLA 专用内核"。
这就是决策链 :config.json 字段 → per_token / 外推 / 量化位宽 / 并行方式 → vLLM 参数。把本章的架构知识落到这 4 个问题上,选型就从"猜"变成了"查表"。
11. 与 mini-vLLM 的对照
mini-vLLM 的 TinyGPT 故意选了最简单的架构组合,让你看清"最小配置"长什么样:
python
ModelConfig(
vocab_size=128,
n_embd=64, # d_model
n_layer=2, # 层数
n_head=4, # Q 头数 = 4
head_dim=16, # = n_embd // n_head
block_size=64, # 最大上下文(learned 绝对位置表的大小)
)
# 注意:没有 num_kv_heads ------ 意味着 num_kv_heads == n_head,即 MHA
对应关系:
| 架构概念 | mini-vLLM(TinyGPT) | 真实 vLLM |
|---|---|---|
| 注意力头 | MHA(4 头,每头独立 K/V) | 主流是 GQA/MLA |
| 位置编码 | Learned 绝对(wpe[positions]) |
RoPE(LLaMA/Qwen/Mistral) |
| 归一化 | 简单 LN | RMSNorm |
| FFN 激活 | GELU 类 | SwiGLU |
| MoE | 无 | Mixtral/DeepSeek 专用内核 |
如果想给 TinyGPT 加 GQA (体会 num_kv_heads 带来的变化):
ModelConfig增加num_kv_heads: int = 1;model.py的 QKV 投影把 K/V 投影到[num_kv_heads, head_dim],attention 计算时按组广播;kv_cache.py的KVStore形状从(num_blocks, block_size, num_heads, head_dim)改成(num_blocks, block_size, num_kv_heads, head_dim);- KV 显存立刻按
num_kv_heads / n_head缩小------这就是 GQA 的全部意义。
做完这个练习,你就把第 02 章的 KV 公式、第 18 章附录 C 的 API、本节的架构知识串成了一条线。
12. 小结
- 引擎必须懂架构:
num_kv_heads/head_dim/num_layers直接决定 KV cache 尺寸 ;注意力头变体、位置编码、MoE 决定 内核与并行方式。 - 注意力头变体:MHA(全 K/V)→ MQA(1 个 K/V)→ GQA(分组 K/V,主流)→ MLA(低秩潜在向量,DeepSeek)。KV cache 依次大幅缩小。
- 位置编码 :RoPE 是主流(Q/K 旋转注入相对位置);ALiBi 零参数可外推;长上下文靠
rope_scaling缩放 + 微调,而非只调--max-model-len。 - 零件差异:RMSNorm vs LayerNorm、SwiGLU vs GELU,影响权重文件与 kernel,不影响 KV cache 公式。
- MoE:参数量大、激活参数小;权重按总参数算显存,推理靠专家并行 + 通信削减。
- 附录 A(第 16 章)查参数、附录 B(第 17 章)算显存------本节提供了"模型侧"的依据,三张表合起来就是完整的容量规划工具箱。
下一章《20-端到端部署案例.md》把这些知识 + 第 4/8/10/17 章的内容串成一次完整的部署实战。