一次前向计算怎样处理上下文:向量、注意力与位置

成长演进

具备清晰骨架、初步数学推导或工程验证,持续补充论据与实测。

ℹ数字花园

🌿 什么是数字花园与思想演进?

遵循 Maggie Appleton 与 Andy Matuschak 的常青笔记理念:这里不是按时间沉底的流水账,而是持续修剪打磨的知识生态。文章按成熟度经历:

  • 🌱 萌芽 (Seedling): 发散灵感、读书摘录、开放猜想
  • 🌿 演进 (Budding): 结构渐明、初步代码推导、积极修正
  • 🌳 常青 (Evergreen): 逻辑闭环、持久经得起检验的基石
  • 🔬 实验待证 (Speculative): 结合沙盒与仿真探针的理论探索
浏览全站数字花园与笔记全景 →

上一篇把训练任务说清楚了:看见前面的 token,给下一个 token 分配概率。但一个整数编号,怎样利用前面的内容,最后变成整张词表上的分数?

这次不从一张塞满模块名的架构图开始。我们只跟踪一条短序列,问三个问题:每一步手里是什么,信息从哪里来,最后拿它做什么。

本文使用普通 decoder-only Transformer 的教学结构。不同模型会改变归一化、位置编码、前馈层和注意力头的组织;看懂这条主线以后,再读具体模型配置才不会只剩背名字。

先给每个维度一个明确含义#

假设输入两条序列,每条四个 token,词表有十六个条目,隐藏维度为八。这里的数字故意很小,便于核对形状,不是推荐训练配置。

量含义本例
BB一批有几条序列2
TT每条序列占几个位置4
VV词表有多少候选16
dd每个位置用多少个数表示8

输入编号的形状是 [B,T]=[2,4][B,T]=[2,4]。其中的整数是索引,不是模型直接拿来衡量相似度的数值。

我们还必须记住:形状相同不代表含义相同。一份 [2,4][2,4] 的输入编号和一份 [2,4][2,4] 的标签,可能分别指向当前位置和下一个位置。只用形状检查无法发现上一篇的错位错误。

嵌入层只是第一份表示#

嵌入矩阵 EE 有 VV 行、dd 列。遇到编号 7,就取第 7 行向量。全部输入查表后,得到 [B,T,d]=[2,4,8][B,T,d]=[2,4,8]。

同一个 token 在不同句子里,刚查出来的基础向量相同。比如“苹果很好吃”和“苹果发布了产品”,如果其中“苹果”是同一个 token,初始查表不会知道这里分别指水果还是公司。

区别要在后续结合上下文时形成。不要把嵌入向量理解成一张已经存好所有语境的中文词典,也不要假定其中第一个数是“水果程度”、第二个数是“公司程度”。训练并没有预先指定这样的坐标含义。

如果模型使用加法位置嵌入,会在这一阶段把位置向量加进去;使用 RoPE 的模型则通常在注意力里的 query 和 key 上处理位置。两种方式不要在示意图里不加说明地叠成同一套必选流程。

注意力先决定从哪些位置取信息#

对当前序列的表示 HH,一个注意力头通过三组可训练矩阵得到:

Q=HWQ,K=HWK,U=HWU.\begin{aligned}Q&=HW_Q,\\K&=HW_K,\\U&=HW_U.\end{aligned}

这里用 UU 表示通常叫做 value 的向量,避免与词表大小 VV 混淆。

可以把 query 理解为当前位置用于匹配的向量,key 是其他位置被匹配的向量,value 则是实际参与汇总的内容。它们都是数值变换的结果,不是人为写进去的自然语言问题、关键词和答案。

当前位置的 query 与各位置的 key 做点积,形成匹配分数。缩放并加入遮罩后,对可见位置做 softmax,再加权汇总 value:

S=QK⊤dk+M,A=softmax⁡(S),O=AU.\begin{aligned}S&=\frac{QK^\top}{\sqrt{d_k}}+M,\\A&=\operatorname{softmax}(S),\\O&=AU.\end{aligned}

softmax 沿被读取的位置方向进行。MM 对允许读取的地方取零,对禁止读取的地方在数学上取负无穷。这样,被禁止的位置权重为零。原始 Transformer 的缩放点积注意力和解码端因果遮罩是这一机制的基础来源。Attention Is All You Need,第 3 节

缩放因子也不是一个任意装饰。若 query、key 的各分量独立、均值为零、方差为一,dkd_k 项乘积之和的方差为 dkd_k;除以 dk\sqrt{d_k} 后,方差回到一。真实训练表示不严格满足这些假设,这个推导说明设计动机,不保证每层分数始终处于某个固定范围。

手算一次加权,而不是把注意力当作搜索框#

假设某个位置只允许读取前面三个位置。经过点积和缩放,它们的分数恰好是 log⁡2,0,0\log2,0,0,对应权重为:

(2,1,1)/4=(0.5,0.25,0.25).\begin{aligned}(2,1,1)/4\\=(0.5,0.25,0.25).\end{aligned}

三个 value 向量分别是 (2,0)(2,0)、(0,4)(0,4)、(2,2)(2,2),那么输出为:

0.5(2,0)+0.25(0,4)+0.25(2,2)=(1.5,1.5).\begin{aligned}&0.5(2,0)+0.25(0,4)\\&\quad+0.25(2,2)\\&=(1.5,1.5).\end{aligned}

这说明注意力可以混合多个位置的信息,而不必只选一个。分数最高的位置获得最大的加权系数,但实际贡献还取决于 value 向量。本例前两个加权向量分别是 (1,0)(1,0) 和 (0,1)(0,1),虽然权重不同,向量长度却相同。结果也不等于直接复制分数最高的位置。

也别把这组权重直接叫作“模型给出的解释”。后面还有输出投影、残差、前馈网络和其他层;各 value 的幅度也不同。某位置权重大,不自动意味着它对最终答案的因果影响最大。

若要研究一个位置是否真的影响输出,可以做受控替换并观察目标概率变化,但替换本身也可能产生不自然输入。可视化权重是一项观察,因果解释还需要额外证据。

多头并不等于人为分配几种语言能力#

本例设两个头,每头四维。query、key、value 可以整理成 [B,h,T,dh]=[2,2,4,4][B,h,T,d_h]=[2,2,4,4],所有头放在一起的注意力权重形状是 [2,2,4,4][2,2,4,4],最后两个维度分别是查询位置和被读取位置。

每个头汇总之后,把两个四维输出拼回八维,再经过输出投影,形状仍是 [2,4,8][2,4,8]。保持这个形状,方便与输入做残差相加。

头之间有不同的参数,可以学习不同的匹配方式,但我们没有指定“第一头负责语法、第二头负责事实”。这种拟人化描述容易让读者误以为头数对应可数的能力模块。

有些模型让多个 query 头共享较少的 key/value 头。读这类配置时,不能继续假设三者的头数总相等。本文的形状例子只对应普通多头注意力,不试图覆盖所有变体。

为什么位置编码仍然需要单独处理#

“甲帮助乙”和“乙帮助甲”包含相同片段,但关系不同。内容表示需要与顺序结合。

因果遮罩提供了“能看哪些前缀”的结构,不能因此简单说模型完全没有顺序信息;但它也没有直接给每对位置提供可学习的距离表示。位置编码进一步帮助模型使用顺序或相对距离。

RoPE 的一个核心做法是按位置旋转 query 和 key。二维情况下,设 R(θ)R(\theta) 是旋转矩阵,第 mm 个位置旋转 mθm\theta,第 nn 个位置旋转 nθn\theta,则:

(R(mθ)q)⊤R(nθ)k=q⊤R((n−m)θ)k.\begin{aligned}&(R(m\theta)q)^\top R(n\theta)k\\&=q^\top R((n-m)\theta)k.\end{aligned}

点积里出现了相对位置 n−mn-m。高维形式可在不同二维分量对上使用不同频率;实际实现还需要固定维度配对方式和频率配置。RoFormer,第 3 节

这个等式解释了相对位置怎样进入打分,不证明模型能可靠处理无限长上下文。超出训练长度后,位置分布、任务难度和实现限制都可能改变。配置里把最大长度调大,不等于长文能力已经验证。

残差和前馈层把混合后的信息继续加工#

注意力负责跨位置汇总;普通逐位置前馈网络对每个位置的向量分别计算,并共享参数。它可以先投影到更宽维度,经过非线性,再投影回来。

“逐位置”不表示它看不到上下文:它的输入已经经过注意力,含有前面位置的信息。它只是这一层不再直接读取其他位置的向量。

一种常见的 pre-norm 教学写法是:

H′=H+Attention⁡(Norm⁡(H))H'=H+\operatorname{Attention}(\operatorname{Norm}(H))H′′=H′+FFN⁡(Norm⁡(H′))H''=H'+\operatorname{FFN}(\operatorname{Norm}(H'))

残差保留一条直接相加的路径,让子层对现有表示作修改。归一化用于调整表示的尺度。注意,这里采用的是前归一化示意;原始 Transformer 论文展示的归一化位置不同,不能把上面两行当作其逐字复现。

叠加多层后,每个位置的表示经过多轮上下文汇总和非线性变换。接着用输出矩阵从 dd 维映射到词表大小 VV,得到 logits,形状为 [B,T,V]=[2,4,16][B,T,V]=[2,4,16]。这些分数再交给上一篇的目标对齐与交叉熵。

计算变贵发生在哪些地方#

对普通稠密注意力,位置两两打分会形成 T×TT\times T 的关系。如果长度从 1,024 加到 2,048,每个头的这个关系矩阵元素数变成四倍。

但不能由此断言整个训练时间或峰值内存一定四倍:前馈层、词表投影、内存读写和实际算子都会影响结果。有些实现也不会完整保存这张矩阵。

FlashAttention 的研究重点之一就是减少注意力计算中的内存读写,使用分块等方法实现精确注意力,而不是靠删除一部分注意力连接来近似原目标。FlashAttention

因此,比较高效算子时要分开两个问题:数学上是否计算同一目标,硬件上是否更快或更省内存。即使目标相同,浮点运算次序变化也可能产生小幅数值差别。

用三个小实验检查前向计算#

第一,记录形状。对本文的配置,检查编号、隐藏表示、分头表示和 logits 是否分别符合表述。额外检查 softmax 的方向,避免把“每个查询读取哪些位置”写反。

第二,做未来位置扰动。在关闭随机 dropout 的评估模式下,只改变最后一个输入 token,更早位置的 logits 应在合理数值误差内保持不变。若变化明显,优先排查因果遮罩、位置索引或跨样本计算。

第三,核对遮罩接口。不能看到布尔值就认定 True 一律代表“遮掉”。例如 PyTorch 2.9 的 scaled_dot_product_attention 中,布尔遮罩的 True 表示允许参与注意力;浮点遮罩则加到打分上。这个函数还直接依据传入的 dropout 概率执行 dropout,评估时需要正确传零。PyTorch SDPA 文档

这些检查通过,说明计算边界更可信;它们不证明模型已经学会语法或推理。后面的验证集和任务评测仍然不可缺少。

两个检查理解的问题#

第一,注意力输出与输入形状相同,为什么不能据此说“这一层没做什么”?请用权重加权的例子,解释形状与内容变化的区别。

第二,只改序列最后一个 token,前面位置的输出却变化了。你会先检查哪些实现细节?哪些变化可能来自随机性,哪些意味着真的读取了未来?

接下来讨论训练块怎样组成一个 batch。模型内部的因果约束正确,还不够;把两篇文档放在同一条序列里之后,文档边界也要由数据和遮罩共同定义。

文章概念网络

从章节进入正文概念。实线表示所属层级,虚线连接跨章节重复出现的概念,不代表因果关系。

57 / 57 个节点 · 正文标记术语与关键词概念

一次前向计算怎样处理上下文:向量、注意力与位置同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept一次前向计算怎样处理上下文:向量、注意力与位置一次前向计算怎样处理上下文:向量、注意力与位置先给每个维度一个明确含义先给每个维度一个明确含义嵌入层只是第一份表示嵌入层只是第一份表示嵌入矩阵嵌入矩阵注意力先决定从哪些位置取信息注意力先决定从哪些位置取信息queryquerykeykeyvaluevalueAttention Is All You Need,第 3 节Attention Is All You Need,第 3 节手算一次加权,而不是把注意力当作搜索框手算一次加权,而不是把注意力当作搜索框多头并不等于人为分配几种语言能力多头并不等于人为分配几种语言能力为什么位置编码仍然需要单独处理为什么位置编码仍然需要单独处理位置编码位置编码RoPERoPERoFormer,第 3 节RoFormer,第 3 节残差和前馈层把混合后的信息继续加工残差和前馈层把混合后的信息继续加工残差残差归一化归一化计算变贵发生在哪些地方计算变贵发生在哪些地方FlashAttentionFlashAttention用三个小实验检查前向计算用三个小实验检查前向计算TrueTruescaleddotproductattentionscaleddotproductattentionPyTorch SDPA 文档PyTorch SDPA 文档两个检查理解的问题两个检查理解的问题训练块怎样组成一个 batch训练块怎样组成一个 batchtokentokenTransformerTransformer位置编码位置编码注意力注意力归一化归一化利用利用tokentokentokentoken注意力注意力注意力注意力TransformerTransformerAttentionAttention因果因果因果因果注意力注意力多头注意力多头注意力注意力注意力因果因果注意力注意力TransformerTransformerAttentionAttention注意力注意力PyTorchPyTorch评估评估tokentoken注意力注意力验证集验证集推理推理tokentoken注意力注意力因果因果
一次前向计算怎样处理上下文:向量、注意力与位置

一次前向计算怎样处理上下文:向量、注意力与位置

回到原文 ↗
BIBTEX

引用本文

@article{2026-10-20-llm-training-pretrain-03-forward,
  author  = {枫语},
  title   = {一次前向计算怎样处理上下文:向量、注意力与位置},
  journal = {枫语},
  year    = {2026},
  month   = {oct},
  url     = {https://meiluosi.github.io/posts/2026-10-20-llm-training-pretrain-03-forward/}
}
🌐

本篇知识星丛与滑动侧栈

一次前向计算怎样处理上下文:向量、注意力与位置 当前主轴

搜索

正在载入索引…