English interface selected · this article is currently written in Chinese.
一次前向计算怎样处理上下文:向量、注意力与位置
English overview →具备清晰骨架、初步数学推导或工程验证,持续补充论据与实测。
数字花园
🌿 什么是数字花园与思想演进?
遵循 Maggie Appleton 与 Andy Matuschak 的常青笔记理念:这里不是按时间沉底的流水账,而是持续修剪打磨的知识生态。文章按成熟度经历:
- 🌱 萌芽 (Seedling): 发散灵感、读书摘录、开放猜想
- 🌿 演进 (Budding): 结构渐明、初步代码推导、积极修正
- 🌳 常青 (Evergreen): 逻辑闭环、持久经得起检验的基石
- 🔬 实验待证 (Speculative): 结合沙盒与仿真探针的理论探索
上一篇把训练任务说清楚了:看见前面的 token,给下一个 token 分配概率。但一个整数编号,怎样利用前面的内容,最后变成整张词表上的分数?
这次不从一张塞满模块名的架构图开始。我们只跟踪一条短序列,问三个问题:每一步手里是什么,信息从哪里来,最后拿它做什么。
本文使用普通 decoder-only Transformer 的教学结构。不同模型会改变归一化、位置编码、前馈层和注意力头的组织;看懂这条主线以后,再读具体模型配置才不会只剩背名字。
先给每个维度一个明确含义
假设输入两条序列,每条四个 token,词表有十六个条目,隐藏维度为八。这里的数字故意很小,便于核对形状,不是推荐训练配置。
| 量 | 含义 | 本例 |
|---|---|---|
| 一批有几条序列 | 2 | |
| 每条序列占几个位置 | 4 | |
| 词表有多少候选 | 16 | |
| 每个位置用多少个数表示 | 8 |
输入编号的形状是 。其中的整数是索引,不是模型直接拿来衡量相似度的数值。
我们还必须记住:形状相同不代表含义相同。一份 的输入编号和一份 的标签,可能分别指向当前位置和下一个位置。只用形状检查无法发现上一篇的错位错误。
嵌入层只是第一份表示
嵌入矩阵 有 行、 列。遇到编号 7,就取第 7 行向量。全部输入查表后,得到 。
同一个 token 在不同句子里,刚查出来的基础向量相同。比如“苹果很好吃”和“苹果发布了产品”,如果其中“苹果”是同一个 token,初始查表不会知道这里分别指水果还是公司。
区别要在后续结合上下文时形成。不要把嵌入向量理解成一张已经存好所有语境的中文词典,也不要假定其中第一个数是“水果程度”、第二个数是“公司程度”。训练并没有预先指定这样的坐标含义。
如果模型使用加法位置嵌入,会在这一阶段把位置向量加进去;使用 RoPE 的模型则通常在注意力里的 query 和 key 上处理位置。两种方式不要在示意图里不加说明地叠成同一套必选流程。
注意力先决定从哪些位置取信息
对当前序列的表示 ,一个注意力头通过三组可训练矩阵得到:
这里用 表示通常叫做 value 的向量,避免与词表大小 混淆。
可以把 query 理解为当前位置用于匹配的向量,key 是其他位置被匹配的向量,value 则是实际参与汇总的内容。它们都是数值变换的结果,不是人为写进去的自然语言问题、关键词和答案。
当前位置的 query 与各位置的 key 做点积,形成匹配分数。缩放并加入遮罩后,对可见位置做 softmax,再加权汇总 value:
softmax 沿被读取的位置方向进行。 对允许读取的地方取零,对禁止读取的地方在数学上取负无穷。这样,被禁止的位置权重为零。原始 Transformer 的缩放点积注意力和解码端因果遮罩是这一机制的基础来源。Attention Is All You Need,第 3 节
缩放因子也不是一个任意装饰。若 query、key 的各分量独立、均值为零、方差为一, 项乘积之和的方差为 ;除以 后,方差回到一。真实训练表示不严格满足这些假设,这个推导说明设计动机,不保证每层分数始终处于某个固定范围。
手算一次加权,而不是把注意力当作搜索框
假设某个位置只允许读取前面三个位置。经过点积和缩放,它们的分数恰好是 ,对应权重为:
三个 value 向量分别是 、、,那么输出为:
这说明注意力可以混合多个位置的信息,而不必只选一个。分数最高的位置获得最大的加权系数,但实际贡献还取决于 value 向量。本例前两个加权向量分别是 和 ,虽然权重不同,向量长度却相同。结果也不等于直接复制分数最高的位置。
也别把这组权重直接叫作“模型给出的解释”。后面还有输出投影、残差、前馈网络和其他层;各 value 的幅度也不同。某位置权重大,不自动意味着它对最终答案的因果影响最大。
若要研究一个位置是否真的影响输出,可以做受控替换并观察目标概率变化,但替换本身也可能产生不自然输入。可视化权重是一项观察,因果解释还需要额外证据。
多头并不等于人为分配几种语言能力
本例设两个头,每头四维。query、key、value 可以整理成 ,所有头放在一起的注意力权重形状是 ,最后两个维度分别是查询位置和被读取位置。
每个头汇总之后,把两个四维输出拼回八维,再经过输出投影,形状仍是 。保持这个形状,方便与输入做残差相加。
头之间有不同的参数,可以学习不同的匹配方式,但我们没有指定“第一头负责语法、第二头负责事实”。这种拟人化描述容易让读者误以为头数对应可数的能力模块。
有些模型让多个 query 头共享较少的 key/value 头。读这类配置时,不能继续假设三者的头数总相等。本文的形状例子只对应普通多头注意力,不试图覆盖所有变体。
为什么位置编码仍然需要单独处理
“甲帮助乙”和“乙帮助甲”包含相同片段,但关系不同。内容表示需要与顺序结合。
因果遮罩提供了“能看哪些前缀”的结构,不能因此简单说模型完全没有顺序信息;但它也没有直接给每对位置提供可学习的距离表示。位置编码进一步帮助模型使用顺序或相对距离。
RoPE 的一个核心做法是按位置旋转 query 和 key。二维情况下,设 是旋转矩阵,第 个位置旋转 ,第 个位置旋转 ,则:
点积里出现了相对位置 。高维形式可在不同二维分量对上使用不同频率;实际实现还需要固定维度配对方式和频率配置。RoFormer,第 3 节
这个等式解释了相对位置怎样进入打分,不证明模型能可靠处理无限长上下文。超出训练长度后,位置分布、任务难度和实现限制都可能改变。配置里把最大长度调大,不等于长文能力已经验证。
残差和前馈层把混合后的信息继续加工
注意力负责跨位置汇总;普通逐位置前馈网络对每个位置的向量分别计算,并共享参数。它可以先投影到更宽维度,经过非线性,再投影回来。
“逐位置”不表示它看不到上下文:它的输入已经经过注意力,含有前面位置的信息。它只是这一层不再直接读取其他位置的向量。
一种常见的 pre-norm 教学写法是:
残差保留一条直接相加的路径,让子层对现有表示作修改。归一化用于调整表示的尺度。注意,这里采用的是前归一化示意;原始 Transformer 论文展示的归一化位置不同,不能把上面两行当作其逐字复现。
叠加多层后,每个位置的表示经过多轮上下文汇总和非线性变换。接着用输出矩阵从 维映射到词表大小 ,得到 logits,形状为 。这些分数再交给上一篇的目标对齐与交叉熵。
计算变贵发生在哪些地方
对普通稠密注意力,位置两两打分会形成 的关系。如果长度从 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 个节点 · 正文标记术语与关键词概念
一次前向计算怎样处理上下文:向量、注意力与位置
回到原文 ↗最后更新于 2026-10-20
引用本文
@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/}
}