文章目录 展开 收起

笔记

Qwen3系列1-理论基础&真实结构

2026-11-09


Qwen3 系列的第一篇文章,三天时间重新复习了 Transformer 的结构和 Qwen3 的结构。现在主流大模型和老祖论文提出的结构有了很大变化。看模型的结构,弄清楚输入输出还有有趣的,毕竟每天都在用的大模型,知道它是怎么个工作原理,也觉得他没那么神秘了。

Qwen3 的结构图:https://zhanghao0806.github.io/blog/Qwen3-TrueInference/?from=note


从一句话到 Attention:看懂并亲手算出来

D1-1 建立大模型整体地图

  1. 什么是语言模型、什么是 Causal Language Model。

    因果语言模型;自回归生成,只能看到前面的信息;

  2. 为什么 Qwen3-8B 是 Decoder-only Transformer,而不是原版 Encoder–Decoder Transformer。

    要回答这个问题,先清楚 encoder-decoder 中 这两个是干嘛的?(以机器翻译为例:理解+生成)

    各自的注意力区别?前者 decoder 有两种注意力;后者只有一种;

    下面正式回答这个问题:

    1 首先 decoder-only 能做到原来 encoder 做的事情,理解上下文:生成答案的位置可以看到前文所有 token;

    2 结构统一:自然语言生成(对话,数学推理、写代码)就是自回归过程:根据已有的token,输出下一个 token;

  3. 模型一次前向计算给出的是 Token 的预测分数,不是一次性写好完整回答。

    “推理时,模型一次前向计算得到的是词表中所有 token 的预测分数”

  4. 模型参数、激活值/中间张量、输入 Token、输出 Token 分别是什么。

    激活值:前向计算内部产生的浮点张量;

  5. 推理与训练/微调的区别:推理固定权重,训练通过梯度更新权重。

必须回答的问题:

  • Q2:模型为什么能反复生成直到结束?它每一轮额外拿到了什么信息?

  • Q4:参数和中间激活值有什么区别?为什么推理时也会占用显存?

    显存被参数占用、激活值、 KVcache

  • Q5:forward() 与 generate() 概念上有什么不同?

    后者包括很多次前者;

动手产物 :在一页纸上画 input text → token ids → embedding → blocks → logits → next token → loop。

D1-2 最低限度的张量、线性层与 Softmax

  • [B,T,D] 中 B=Batch、T=序列长度、D=隐藏维度 = 词嵌入向量的维度。
  • 矩阵乘法与逐元素乘法、广播;转置后维度如何变化。
  • Linear 在数学上是仿射变换:Y = XW + b;Qwen3 的多数投影不带 bias。
  • Softmax 的意义:将一组分数转换成总和为 1 的正数概率。

核心公式:

softmax⁡(z)i=ezi∑jezj\operatorname{softmax}(z)_i=\frac{e^{z_i}}{\sum_j e^{z_j}}

必须回答:

  • Q5:为什么通常用 softmax(z - max(z)) 进行数值稳定计算?

    防止溢出;

D1-3 Tokenizer、Token ID 与 Embedding

  • Tokenizer : 包括两部分:1先把文本切分成 Token;2把Token 映射为 ID 序列;

    映射用的是词表。词表也是被训练出来的;在模型训练之前词表就固定了;词表可以用别人训练好的;

    大模型里面的 tokenizer.json tokenizer.model 就是这个大模型的词表;

  • Embedding 是用 ID 查一个可学习矩阵的行;

    Embedding 是模型的 block0,训练时参数也会更新;

  • 同一个 Token 的初始 Embedding 通常相同,但不同上下文下,经 Transformer 后表示不同。

    意思是不同的两句话里面相同的 token,他们初始查 embedding 得到的向量是一样的;但是经过Transformer 层之后不一样了;

  • Qwen3 聊天输入先经过 Chat Template,包含角色、边界、生成提示等特殊 Token(在Qwen 系列 2里面可以看到);“你看到的原始一句话”不等于真实送入网络的全部 Token。

必须回答:

  • Q4:Embedding 是怎么训练出来的?本轮需知道哪些、不需要知道哪些?

    Embedding 也算模型参数,跟 Transformer 的权重参数一块更新;

  • Q5:特殊结束 Token(如 EOS)在生成停止中扮演什么角色?

    数据驱动学出来的停止偏好,不是硬编码规则;

D1-4 单头 Scaled Dot-Product Attention

  1. Q=XW_Q, K=XW_K, V=XW_V 三组向量如何产生;这里的 Q/K/V 都来自同一序列时叫 Self-Attention。与之相对是 cross-attention;

    K 用来判断信息的相关性,V 才是信息的实际内容;

  2. 每一行 Softmax,得到针对各 Key 位置的权重。

  3. 权重乘以 V,得到各 Query 位置的加权信息汇总。

softmax⁡(QK⊤dk+M)V\operatorname{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}+M\right)V

必须回答:

  • Q1:Q、K、V 是模型参数吗?真正的可训练参数是什么?

    是激活值;三个投影矩阵才是可训练参数;Wo 输出投影矩阵;

  • Q2:为什么用 QKᵀ,而不是 QK?它的形状为什么与序列长度相关?

  • Q3:Attention 分数矩阵的第 i 行、第 j 列表示什么?

    输入 X 的第 i 个 token 和 输入 X 的第 j 个 token 的注意力分数。(手动做个乘法,就是两个 token 对应的 Q 和 K token 相乘)

    注意力分数矩阵,第 i 行是第 i 个 token 对其他所有 token 的注意力分数;

    softMax 是对每一行做的;

  • Q4:为什么要缩放 sqrt(d_k)?“缩放是为了得到概率”这个说法准确吗?

    为了防止某一个注意力的概率非常大,其他注意力很小,导致梯度消失;

  • Q5:Softmax 沿哪个维度做?每一行的和应该是多少?

    沿着行 Attention 矩阵的行做;把原始权重变成分数;

  • Q6:如果某一行权重为 [0.1, 0.2, 0.7],对应 Value 向量如何合成输出?

    V 矩阵的每一行是一个 token 的 V 向量;

    注意力矩阵乘以 V,用 AB=C 的行向量视角更容易理解;

    AB 的行向量是 B 的行向量线性组合得到,第 i 个行向量的线性组合系数是 A 的第 i 行;

    这样理解的好处:A(注意力矩阵) 第 i 行是第 i 个 token 对所有 token 的注意力权重,权重作为线性组合的系数,决定每个 token 的 V 要关注多少;

    按行向量计算 AV,更容易理解;能把权重系数的意义体现出来;

  • Q8:如果对 Q 乘一个很大的系数,Softmax 分布通常会发生什么变化?

    softmax 的公式是什么?它对大权重很敏感,这样会放大某一个注意力的影响,变成独热编码;后续梯度消失或爆炸;

纸笔任务:

  • 3 个 Token,单头维度 2,显式给出 Q、K、V 的所有数字;
  • 计算全部点积,形成 3×3 分数矩阵;
  • 除以 sqrt(2),加入掩码(可和 D1-5 合并),逐行做 Softmax;

D1-5 Causal Mask、多头注意力、输出投影、残差

  • 自回归 LM 的 Causal Mask:位置 i 只能看到 j≤i 的位置;不能偷看未来 Token。

    为什么加入因果掩码?在训练的时候并行。一次性输入整个序列,让所有位置同时预测下一个 token;

    上三角还是下三角?结合注意力矩阵元素 Aij 的含义;为了不让第 i 个 token 看到它后面的 token 的权重;

    “不能偷看未来 token”在数学上的实现是:softmax 之后对这个 token 的注意力分数为 0;

  • 掩码可理解为在不允许的位置加 −∞,使 Softmax 后对应权重为 0。

  • Multi-Head Attention(MHA):多个 Query/Key/Value 子空间并行计算,合并后再经过输出投影 W_O。

  • 残差连接:y = x + f(x),使主干输入与模块结果相加。

    记住一句话:不让每一层网络都学习完整的结果,只在上一层的基础上,学习需要修改的部分。

  • 为什么模块输入输出必须适配隐藏维度,才能做残差相加。

    残差是逐元素相加;

必须回答:

  • Q1:Causal Mask 矩阵加在哪里?

  • Q3:为什么训练/Prefill 可以把整段输入并行送入模型,仍然满足“不看未来”?

  • Q4:多头合并步骤?

    每个头单独计算注意力、单独 softmax ,然后乘以 V;最后直接拼接,乘以融合头矩阵;

  • Q5:Concat(head_1,head_2) 后为什么还有一个 W_O?

    不同的头关注不同维度的信息,需要按不同权重融合一下;

**纸笔任务:**对 D1-4 的 3×3 分数应用因果掩码;另画一个“两头→拼接→输出投影”的形状示意图,并算一次 x+Attention(x)。


Qwen3 特有结构

D2-1 RMSNorm、Pre-Norm 和残差路径

  • RMS(root mean square,均方根)与 RMSNorm 的计算。

    均方根和 LayerNorm 都是在同一个样本内部做。

    没有中心化,所以计算更省。只涉及放缩;

    会计算 RMS;然后仿射变换每个归一化都有,有两个参数;

    γ也是可训练的;

  • Qwen3 的注意力和 MLP 前使用 RMSNorm,属于 Pre-Norm 风格。

    Pre-Norm是从梯度考虑的,更稳定;

    Pre-Norm 是进入子层之前(在计算 QKV 之前);

    Post-Norm 是计算残差之后;

RMSNorm⁡(x)=γ⊙x1D∑i=1Dxi2+ϵ\operatorname{RMSNorm}(x)=\gamma\odot \frac{x}{\sqrt{\frac{1}{D}\sum_{i=1}^D x_i^2+\epsilon}}

必须回答:

  • Q3:Attention 前的 RMSNorm 与 QK-Norm 是同一处操作吗?

  • Q4:x + Attention(RMSNorm(x)) 与 RMSNorm(x + Attention(x)) 在计算顺序上有何不同?

    就是 Pre-Norm 和 Post-Norm 区别;前者梯度更稳定;

D2-2 QK-Norm 与 RoPE

  • Qwen3 注意力中,Q/K 线性投影后按注意力头归一化,随后对 Q/K 应用 RoPE。

  • QK-Norm 对 Query、Key 使用各自的 RMSNorm;Value 不做同样的 QK-Norm,也不使用 RoPE。

  • RoPE编码:让一个 Token 的 Q K 向量根据相对位置不同,旋转不同的角度;

    原始 transformer 的正余弦位置编码加在哪里?

    RoPE 加在哪里;

    效果:不会改变向量模长;旋转矩阵的转置等于负方向旋转对应角度;不会引入与位置无关的噪声项(正余弦编码会)

    容易迷糊的地方:一个token 对应一个 RoPE 矩阵;每一个 token 对应的向量左乘 RoPE 矩阵,乘以的都是它对应的 RoPE 矩阵;RoPE 矩阵(隐藏层维度 4)

    R(pos)=[cos⁡(posθ0)−sin⁡(posθ0)00sin⁡(posθ0)cos⁡(posθ0)0000cos⁡(posθ1)−sin⁡(posθ1)00sin⁡(posθ1)cos⁡(posθ1)]R(\text{pos}) = \begin{bmatrix} \cos(\text{pos}\theta_0) & -\sin(\text{pos}\theta_0) & 0 & 0 \\ \sin(\text{pos}\theta_0) & \cos(\text{pos}\theta_0) & 0 & 0 \\ 0 & 0 & \cos(\text{pos}\theta_1) & -\sin(\text{pos}\theta_1) \\ 0 & 0 & \sin(\text{pos}\theta_1) & \cos(\text{pos}\theta_1) \end{bmatrix}
  • 2 维 RoPE 可以看成一个旋转矩阵:一个二维向量乘以旋转矩阵等于该向量逆时针旋转θ角度;

必须回答:

  • Q1:QK-Norm 与模型 Block 开头的 RMSNorm 有何位置与作用差别?

    QKNorm 考虑的是,虽然经过一次 Norm 之后,但是乘以投影之后可能依然变得很大,导致注意力高度集中在某个位置(softmax 的特性),同时为了训练时数值稳定;

    每个头内部自己做 Norm;

  • Q2:QK-Norm 对 Q 和 K 使用不同的可训练缩放参数吗?

    Norm 最后一步的仿射变换中 Q 的所有头共享放缩参数;K 的所有头共享放缩参数;

  • Q3:为什么 RoPE 作用在 Q/K,而不是直接把位置编号加到 Softmax 概率上?

    答案应该是会导致梯度爆炸

**纸笔任务:**算一个二维 Query 的 RMSNorm,再取指定角度计算 RoPE(可使用 sin/cos 的近似值),最后与一个 Key 计算点积。

D2-3 从 MHA 理解 GQA【重点】

要学明白:

  • MHA:多头注意力,Query/K/V 的头数通常相同。推理时 KVcache占用大量显存;

  • MQA:多查询注意力,多个 Query 头共享一组 K/V 头。损失模型表达能力;

  • GQA:Group Quary Attention 分组查询注意力。把多个 Query 头分组,每组共享一组 K/V 头。

    减少一些 KV,换取显存开销下降。

  • Qwen3-8B 使用 32 个 Q 头、8 个 K/V 头,因此 每 4 个 Q 头共享 1 个 K/V 头。

    投影的时候只有 Q 保持了向量的维度,KV 都变小了;每个头的内部维度都是一样大的;

  • 为什么 GQA 有助于减少 KV Cache 容量和内存访问,而不是“减少了 Query 头所以一定降低表达能力”。

    • 为什么推理时候需要 KV cache?

      本质是什么?空间换时间;

      缓存之后有用的例子:要生成第 i 个 token,把第 i 个 token 的 Q 乘以它前面所有向量的 K,V 得到输出,预测出下一个token;但是在生成第 i-1 个 token 时,前 i-2个 token 的 KV 已经算过了,可以直接用,这就是要存的 KV;

    • 为什么不需要缓存 Q;

      因为生成新的token,需要拿新 token 的 Q 去和所有能看见的 token 的 KV 做计算;之前 token 的 Q 没用了;

    • KV cache 大致计算公式(和哪些参数有关?)

      每一个 transformer 层都需要 KVcache;

      每一个 KV 头都要缓存自己的 KV;

      KV 的每一行都是128 (Qwen3 分头之后的维度)维度的向量;

      Token 越长,KVcache 越大;KV 的行数增加了;

      2 X BatchSize X Tokens X Head_num X L(Transformer 层数) X d(每个 head 维度) X 字节数

    • 硬件知识,为什么 KV cache 要放在显存不放在内存:

      内存特点,容量大(可达到 1T)但是带宽低(读取速度几十到 100GB);显存容量小,但是带宽高(A100 1.5T~2T/s);KV Cache 要靠近计算单元;

    • 关于 KV Cache 多说一点:在现代大模型真实推理过程中的例子

      • 大模型推理分为两个阶段:Prefill 和 Decode; 前者负责生成 Prompt 的所有 KV Cache 以及第一个预测出的 Token;后续所有 Token 都由 Decode 预测;Decode 阶段每次输入的 Token 只有一个(最新预测出来的);

      • 真实的例子:拿到用户的 Prompt,映射成一个 Token 序列;进入第一次也是唯一一次 Prefill 阶段;在第一层,经过归一化和 QKV 线性投影计算得到 QKV,此时将 KV 写入 Cache(Qwen3 是在计算 Attention 之前);这一层的每一个 KV 头都要建立自己的 Cache;每一层都要建立自己的 KV Cache;

      • 单轮对话,是一次 prefill 和多次 decode;多轮对话,有新的提示词,就有新的 prefill;

      • prefill 要并行计算 KV Cache,计算密集型,瓶颈在计算速度上;decode 涉及很少的计算,只用一个 Token 和KV Cache 做计算,计算量小。但是要从显存读取大量数据到计算单元上,所以带宽是瓶颈;

必须回答:

  • Q1:32 个 Q 头 / 8 个 KV 头,分组倍数是几?

    Q 头要分为 4 组;

  • Q2:为什么 Q 与 K 的总投影宽度可以不相同,但点积仍能做?

    总的不同,但是每个头内部的维度相同;

  • Q5:序列长度增加时,缓存 K/V 主要增长在哪些维度?

    序列长度这一维度:已缓存的 token 数量;

**纸笔任务:**画出 2 个 Q 头、1 个 KV 头、每头 2 维 的微型 GQA。两组 Q 用同一组 K/V,分别算两行注意力权重并拼接输出。

这一步是走完了Attention 计算公式,下面进入前馈神经网络。

FFN 的表达式:

FFN(x)=Wdown[SiLU(Wgatex)⊙(Wupx)]\boxed{ \text{FFN}(x) = W_{\text{down}} [\text{SiLU}(W_{\text{gate}}x) \odot (W_{\text{up}}x)] }

D2-4 SwiGLU:激活函数、门控 MLP【A:重点】

要学明白:

  • 为什么只有线性变换的层叠还不够,需要非线性激活函数。

    Attention 负责融合各个 Token 之间的信息,MLP 负责对融合后的 Token 进行加工;

  • sigmoid(x)、SiLU(x) = x·sigmoid(x) 、ReLU的公式

    sigmoid(x)=11+e−xsigmoid(x) = \frac{1}{1 + e^{-x}}\\
    • SiLU激活函数:处处可导;与门控机制结合

      SiLU(x)=x⋅σ(x)=x1+e−x\text{SiLU}(x) = x \cdot \sigma(x) = \frac{x}{1 + e^{-x}}
    • ReLU:

      ReLU(x)=max⁡(0,x)={x,x>00,x≤0\text{ReLU}(x) = \max(0, x) = \begin{cases} x, & x > 0 \\ 0, & x \le 0 \end{cases}
  • SwiGLU 有 两条向上投影路径:gate_proj 与 up_proj;它们是逐元素相乘,不是相加或拼接。

  • down_proj 再把中间维度映射回隐藏维度,方便残差连接。

MLP⁡(x)=Wdown(SiLU⁡(Wgatex)⊙Wupx)\operatorname{MLP}(x)=W_{\text{down}}\left(\operatorname{SiLU}(W_{\text{gate}}x)\odot W_{\text{up}}x\right)

必须回答:

  • Q2:为什么不能把 SwiGLU 简单理解成“先线性变换一次,再激活一次”?

    一般的前馈神经网络是线性变换+激活函数+线性变换;SwiGLU 有两条路径,左边相当于是门控,右边很像原来的普通神经网络的激活;

  • Q3:gate_proj、up_proj、down_proj 的输入输出尺寸分别是什么?

    前两者 4096 12288;后者 12288 4096;

  • Q4:SwiGLU 中的 * 是点积、矩阵乘法还是逐元素乘法?

    逐元素乘法,相当于给原来每个元素乘以一个权重,调节;

  • Q5:为什么 MLP 最终需要映射回 hidden_size?

    做残差;

**纸笔任务:**对一个长度为 4 的输入,用两组尺寸简化、数字稀疏的权重算出 gate、up、SiLU、逐元素乘法与 down 的完整数值。

D2-5 拼出真实 Qwen3 Decoder Block

简洁公式:

u=h+Attention⁡(RMSNorm⁡1(h)),hnext=u+MLP⁡(RMSNorm⁡2(u)).\begin{aligned} u &= h+\operatorname{Attention}(\operatorname{RMSNorm}_{1}(h)),\\ h_{\text{next}}&=u+\operatorname{MLP}(\operatorname{RMSNorm}_{2}(u)). \end{aligned}

必须回答:

  • Q1:一个 Block 有几个残差加法?加法的两个张量各是哪一个?

  • Q2:Attention 之前、MLP 之前各有一个什么归一化?

  • Q4:Attention 输出之后,MLP 能否跳过?为什么这是两个不同子模块?

    负责不同的功能:Attention 负责融合 token 之间信息;MLP 负责非线性加工。

  • Q5:真实 Qwen3 中,QK-Norm、RoPE 的顺序能否颠倒后仍声称按源码实现?

**纸笔任务:**画出完整 Block,给所有箭头写维度(先用微型模型 D=4,再用真实模型 D=4096)。


映射到真实 Qwen3-8B:结构、算子、源码、实际推理

以后有时间再看。