Skip to content
团子云技术 Lite 1.048596
Go back

Qwen3 推理全流程解析:基于 vLLM 源码的逐层拆解

团团虾声明:基于 vLLM v0.20.1 源码中 qwen3.pyqwen2.py 的实现,逐层梳理 Qwen3 从 token 输入到 logits 输出的完整推理计算流程。

Qwen3 推理全流程解析

基于 vLLM v0.20.1qwen3.py + qwen2.py 的实现

  • vllm/model_executor/models/qwen3.py
  • vllm/model_executor/models/qwen2.py

总览:端到端数据流

graph TD
    A["input_ids<br/>(token 序列)"] --> B["Embedding<br/>VocabParallelEmbedding"]
    B --> C["hidden_states<br/>(seq_len, hidden_size)"]
    C --> D["Decoder Layer × N"]
    D --> E["Final RMSNorm"]
    E --> F["LM Head<br/>(线性映射到 vocab_size)"]
    F --> G["Logits → 采样 → 输出 token"]
    
    subgraph "每个 Decoder Layer"
        D1["Input RMSNorm"] --> D2["Self-Attention<br/>(QKV + QK-Norm + RoPE + Attention + O_proj)"]
        D2 --> D3["Residual Add"]
        D3 --> D4["Post-Attention RMSNorm"]
        D4 --> D5["MLP (SwiGLU)"]
        D5 --> D6["Residual Add"]
    end

第 1 步:Embedding(词嵌入)

入口: Qwen3ForCausalLM.forwardQwen2Model.forward

# qwen2.py L429-L433
hidden_states = self.embed_input_ids(input_ids)  # VocabParallelEmbedding
residual = None

计算: input_ids: (seq_len,) → 查表 → hidden_states: (seq_len, hidden_size)

每个 token ID 被映射成一个 hidden_size 维的向量。例如 Qwen3-8B 的 hidden_size=4096


第 2 步:N 个 Decoder Layer 循环

入口: Qwen2Model.forward

# qwen2.py L441-L444
for idx, layer in enumerate(islice(self.layers, self.start_layer, self.end_layer)):
    hidden_states, residual = layer(positions, hidden_states, residual)

每个 layer 是 Qwen3DecoderLayer,包含两个子模块:Self-AttentionMLP,各自前面有一个 RMSNorm,各自后面有一个 残差连接


2.1 Input RMSNorm + 残差管理

入口: Qwen3DecoderLayer.forward

# qwen3.py L223-L227
if residual is None:            # 第一层
    residual = hidden_states
    hidden_states = self.input_layernorm(hidden_states)
else:                           # 后续层: fused add + norm
    hidden_states, residual = self.input_layernorm(hidden_states, residual)

计算: RMSNorm 对每个 token 向量做归一化:

RMSNorm(x)=x1di=1dxi2+ϵγ\text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^d x_i^2 + \epsilon}} \cdot \gamma

[!TIP] vLLM 的 RMSNorm 支持 fused add + norm:hidden_states, residual = norm(hidden_states, residual) 会先做 residual += hidden_states,再对结果做 norm。这减少了一次显存读写,是性能优化。


2.2 Self-Attention(自注意力)

入口: Qwen3Attention.forward

这是 Qwen3 与 Qwen2 的关键区别所在。整个注意力计算分为 5 步:

步骤 ①:QKV 线性投影

# qwen3.py L150-L151
qkv, _ = self.qkv_proj(hidden_states)  # (seq_len, q_size + 2*kv_size)
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)

计算: 一次矩阵乘法 hidden_states @ W_qkv,然后切分成 Q、K、V 三个张量。

[!IMPORTANT] Qwen3 的 QKV 没有 biasqkv_bias=False,见 L195),而 Qwen2 的 QKV 有 biasbias=True)。这是两代架构的一个重要差异。

GQA (Grouped-Query Attention): Qwen3 使用 GQA,即 num_kv_heads < num_attention_heads。例如 Qwen3-8B 有 32 个 Q 头,但只有 8 个 KV 头,每 4 个 Q 头共享一组 KV。

步骤 ②:QK-Norm(🆕 Qwen3 新增)

# qwen3.py L152-L158
q_by_head = q.view(*q.shape[:-1], q.shape[-1] // self.head_dim, self.head_dim)
q_by_head = self.q_norm(q_by_head)     # 对每个头独立做 RMSNorm
q = q_by_head.view(q.shape)

k_by_head = k.view(*k.shape[:-1], k.shape[-1] // self.head_dim, self.head_dim)
k_by_head = self.k_norm(k_by_head)     # 对每个头独立做 RMSNorm
k = k_by_head.view(k.shape)

计算: 将 Q/K reshape 成 (seq_len, num_heads, head_dim),对每个头的 head_dim 维度做 RMSNorm,再 reshape 回去。

[!IMPORTANT] QK-Norm 是 Qwen3 相对于 Qwen2 的核心架构改进。 Qwen2 没有这一步(Qwen2 的 qk_norm 默认为 False,是后来为 BAGEL 等模型才加的可选项)。QK-Norm 的作用是稳定注意力分数,防止 Q·K 点积值过大导致训练不稳定。

步骤 ③:RoPE 旋转位置编码

# qwen3.py L159
q, k = self.rotary_emb(positions, q, k)

计算: 对 Q 和 K 施加旋转位置编码。将每个头的 head_dim 维度两两配对,按照 position 乘以旋转矩阵:

q2i+iq2i+1=(q2i+iq2i+1)eiθiposq'_{2i} + i \cdot q'_{2i+1} = (q_{2i} + i \cdot q'_{2i+1}) \cdot e^{i \cdot \theta_i \cdot \text{pos}}

这使得 Q·K 的点积只依赖于两个 token 的相对位置

步骤 ④:Attention 计算

# qwen3.py L160
attn_output = self.attn(q, k, v)

计算(标准 scaled dot-product attention):

Attention(Q,K,V)=softmax(QKTdk+mask)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}} + \text{mask}\right)V

其中 mask 是因果掩码(causal mask),确保每个 token 只能看到它之前的 token。

[!NOTE] vLLM 中 self.attn 的实际实现取决于后端(FlashAttention、PagedAttention 等),此处封装在 Attention 类中。推理时,KV 会被缓存(KV Cache),新 token 只需计算自己的 Q 并与已缓存的 KV 做注意力。

步骤 ⑤:输出投影

# qwen3.py L161
output, _ = self.o_proj(attn_output)  # RowParallelLinear

计算: attn_output @ W_o,将注意力输出映射回 hidden_size 维度。


2.3 残差连接 + Post-Attention RMSNorm

# qwen3.py L234
hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)

计算: residual = residual + attn_output,然后 hidden_states = RMSNorm(residual)


2.4 MLP(SwiGLU)

入口: Qwen2MLP(被 Qwen3 直接复用)

# qwen2.py L113-L117
def forward(self, x):
    gate_up, _ = self.gate_up_proj(x)    # (seq_len, 2 * intermediate_size)
    x = self.act_fn(gate_up)              # SiluAndMul → (seq_len, intermediate_size)
    x, _ = self.down_proj(x)              # (seq_len, hidden_size)
    return x

计算(SwiGLU 激活):

MLP(x)=Wdown[SiLU(Wgatex)(Wupx)]\text{MLP}(x) = W_{\text{down}} \cdot \left[\text{SiLU}(W_{\text{gate}} \cdot x) \odot (W_{\text{up}} \cdot x)\right]

分解为三步:

  1. gate_up_proj: 一次矩阵乘法同时计算 gate 和 up 两路:x @ [W_gate; W_up],输出 2 * intermediate_size
  2. SiluAndMul: 前半部分过 SiLU 激活函数,与后半部分逐元素相乘
  3. down_proj: 降维回 hidden_size

[!NOTE] SwiGLU 中的 intermediate_size 通常是 hidden_size 的 ~2.67 倍(例如 Qwen3-8B: hidden=4096, intermediate=11008)。


2.5 残差连接

# qwen3.py L235-L236
hidden_states = self.mlp(hidden_states)
return hidden_states, residual
# 注意:这里的残差连接在下一层的 input_layernorm 中以 fused 方式完成

第 3 步:Final RMSNorm

# qwen2.py L454
hidden_states, _ = self.norm(hidden_states, residual)

计算: 最后一次残差加和 + RMSNorm,得到最终的 hidden_states。


第 4 步:LM Head → Logits → 采样

入口: Qwen3ForCausalLM.compute_logits

# qwen3.py L331-L332
logits = self.logits_processor(self.lm_head, hidden_states)

计算:

  1. LM Head: hidden_states @ W_lm_head(seq_len, vocab_size) 的 logits
  2. 采样: 在 logits 上做 top-k / top-p / temperature 采样,得到下一个 token

[!NOTE] 如果 tie_word_embeddings=TrueL295-L296),LM Head 直接复用 Embedding 层的权重矩阵,不额外占用显存。


Qwen3 vs Qwen2:关键架构差异

特性Qwen2Qwen3
QKV bias✅ 有 bias❌ 无 bias
QK-Norm❌ 默认无必选(每个头独立 RMSNorm)
MLPSwiGLUSwiGLU(完全相同,直接复用)
Layer NormRMSNormRMSNorm(相同)
位置编码RoPE(默认 θ=1M)RoPE(默认 θ=1M)

这个文件能理解到什么程度?

✅ 能理解的

层面说明
模型整体架构Embedding → N × DecoderLayer → Norm → LM Head 的完整流程
每层的计算步骤RMSNorm → QKV → QK-Norm → RoPE → Attention → O_proj → RMSNorm → MLP 的精确顺序
数据流和张量形状每一步输入输出的维度变化
Qwen3 的独特设计QK-Norm、无 QKV bias 等区别于其他模型的选择
并行化策略Tensor Parallel (TP) 和 Pipeline Parallel (PP) 的切分逻辑

⚠️ 不能完全理解的(需要看其他文件)

层面需要看的文件
Attention 具体内核vllm/attention/backends/ — FlashAttention / PagedAttention 的实际 CUDA 实现
KV Cache 管理vllm/v1/core/ — PagedAttention 的内存管理和块分配
采样策略vllm/model_executor/layers/sampler.py — top-k, top-p, temperature 等
调度和批处理vllm/v1/core/scheduler.py — continuous batching 和请求调度
RoPE 实现细节vllm/model_executor/layers/rotary_embedding.py — 旋转位置编码的具体数学
量化vllm/model_executor/layers/quantization/ — GPTQ, AWQ, FP8 等量化逻辑

Share this post on:

Previous Post
大模型的物理定律:Scaling Laws 从何而来,又往哪去
Next Post
【转载】迈克尔·伯里后成名时代的投资逻辑、交易质量与盈亏全景推演