2026年10月4日
04 注意力:token 理解上下文
注意力机制解决的是一个核心问题:同一个 token 在不同上下文中含义不同,模型需要让每个 token 根据上下文更新自己的含义。 上次提到,"苹果"查表得到的初始向量是固定的,区分"吃苹果"和"苹果手机",靠的就是注意力。
先看一个直观的例子。在"猫没吃鱼,因为它……"这句话里,模型处理到"它"时,需要搞清楚"它"指的是谁:
"它"会去"看"前面每一个 token,给每个 token 分配一个权重,然后按权重把它们的信息混合进自己的向量。权重最高的是"猫",于是"它"的向量就吸收了大量"猫"的信息。下面看具体是怎么算出这些权重的。
Query、Key、Value:一个检索系统
注意力机制借用了检索的思路。可以想象在图书馆查资料:你带着一个问题(Query),每本书的书脊上有一个标签(Key),书里是实际内容(Value)。你拿问题和每个标签比对,越匹配的书,越多地参考它的内容。
在模型中,每个 token 的向量都会被转换出这三样东西:
- Query:我在找什么信息?("它"在找:前面有哪个名词可以被指代?)
- Key:我能提供什么类型的信息?("猫"表示:我是一个可以被指代的名词)
- Value:如果你关注我,我实际传递给你的内容是什么
转换方式很简单:用三个矩阵分别乘以 token 的向量。这三个矩阵是模型参数的一部分,同样是随机初始化、再通过梯度下降学出来的。 模型并没有被教过"代词应该关注名词",这种模式是在大量"预测下一个 token"的训练中自己学会的,因为学会了它,预测就更准。
计算的四个步骤
以"它"为例:
- 打分:用"它"的 Query 分别和每个 token 的 Key 做点积。点积越大,说明两个向量方向越一致,即越匹配。这和之前讲的"用向量距离衡量语义相似度"是同一个道理。
- 缩放:把分数除以一个和 dimension 有关的常数,防止数值过大导致训练不稳定。
- 归一化:用 softmax 函数把分数转成加起来等于 1 的权重,就是图中那组数字。
- 加权求和:用权重对各个 token 的 Value 加权平均,得到的结果就是"它"融合了上下文之后的新向量。
所有 token 同时进行这个过程,而且可以用矩阵运算一次性并行完成。这正是 Transformer 能高效利用 GPU 训练的关键。
几个重要的细节
只能往前看。 注意到图中"它"只连接了前面的 token。生成式模型的任务是预测下一个 token,如果训练时能看到后面的内容就等于作弊了。所以计算时会用一个"遮罩(mask)"把后面的位置屏蔽掉,叫做因果注意力。
多头注意力。 一句话里同时存在很多种关系:指代关系、主谓关系、修饰关系、语气等。只用一组 Q/K/V 很难全部兼顾,于是模型会并行使用几十组,每组叫一个"头",各自学习关注不同类型的关系,最后再把结果拼起来。这也是上次提到的"dimension 需要能被注意力头数整除"的原因:dimension 会被平均分给各个头。
多层叠加。 一个 Transformer 层由注意力和一个前馈网络组成,大模型会叠加几十到上百层。浅层的注意力往往关注语法、相邻词等局部关系,深层则处理更抽象的语义和推理。每经过一层,token 的向量就被上下文修改一次,信息逐层提炼。
它解释了哪些实际现象
为什么 context window 有上限、长文本很贵。 每个 token 都要和其他所有 token 计算一次分数,n 个 token 就要算约 n² 次。context 长度翻倍,注意力的计算量大约翻四倍。
为什么提示缓存能生效。 生成新 token 时,前面每个 token 的 Key 和 Value 不会改变(因为它们只看更前面的内容),所以可以存起来直接复用,叫做 KV Cache。上次讲的 prompt caching,本质上就是把固定前缀的 KV Cache 保存下来,跨请求复用。这也解释了为什么前缀改一个字,后面的缓存全部失效:后面所有 token 都"看"过那个字,它们的 K 和 V 都变了。
为什么长上下文中间的信息容易被忽略。 注意力权重是"分配"出来的,加起来等于 1。context 越长,可分配的对象越多,模型对每部分的关注就越分散。研究也发现,模型往往对开头和结尾的内容关注更多,中间部分更容易被忽视。做 agent 开发时,这就是为什么关键指令最好放在 system prompt 中,或在长上下文的末尾重申一次。