从注意力机制到自注意力与位置编码:Transformer 核心机制详解

在这篇文章中,我们从一个非常自然的问题出发:为什么深度学习在处理序列信息时,会从 RNN 逐渐走向 Attention,再进一步走向 Transformer 中的 Self-Attention?我们会把注意力机制的直觉、数学公式、工程意义,以及它与 RNN 的本质区别讲清楚,并进一步讨论 Self-Attention 中非常关键的缩放因子、位置信息丢失和位置编码问题。

为了避免概念混乱,本文会特别澄清几个常见误解:Attention 不是简单地把 RNN 的隐藏状态替换成 Q、K、V;W_Q、W_K、W_V 也不是所谓“三层感知机”;早期 Attention 与现代大模型中常见的 Self-Attention 之间既有历史联系,也有本质差别。

一、从 RNN 的串行记忆到注意力机制的直觉

1. RNN 的串行记忆为什么容易遗忘

在 RNN 中,序列通常是一个词一个词地被处理。假设当前时刻的输入是 $x_t$,上一时刻的隐藏状态是 $h_{t-1}$,那么当前时刻的隐藏状态可以写成:

$$ h_t = \tanh(W_{hh}h_{t-1} + W_{xh}x_t+b) $$

这里最重要的角色是隐藏状态 $h_t$。它可以被理解为 RNN 的“记忆胶囊”:模型把之前看到的信息压缩进这个向量里,然后带着这个向量继续往下走。

这种方式在短序列上很自然,但问题也很明显。

首先,信息必须一站一站传递。第 1 个词的信息要先影响第 2 个词,再影响第 3 个词,一直传到第 100 个词。换句话说,如果第 100 个词想知道第 1 个词说了什么,它只能依赖前面经过 99 次传递后残留下来的状态。

其次,隐藏状态通常是固定维度的向量。无论句子有多长,所有历史信息都要被压缩进同一个有限空间里。这样一来,早期信息很容易被后来的信息覆盖、稀释或干扰。

这就是 RNN 长距离依赖困难的根源:不是模型“不想记住”,而是它的结构决定了信息必须通过一条很长、很窄、不断被压缩的通路传递。

2. 人类阅读中的选择性关注

我们来看一个很常见的英文句子:

The animal didn’t cross the street because it was too tired.

当读到 “it” 这个词时,人脑通常不会平等地回顾前面所有词,而是会自动寻找最有可能解释 “it” 的线索。我们会很快注意到 “animal”,因为 “animal” 是一个有生命的名词,而后面的 “tired” 又更常用来形容有生命的事物。于是我们自然会把 “it” 理解为 “animal”,而不是 “street”。

这个过程说明了一个重要事实:人在理解语言时,并不是机械地按顺序扫描所有信息,而是会根据当前需要,动态地把注意力分配到最相关的上下文上。

Attention 机制的核心直觉正是如此:在处理当前信息时,不再平等地看待所有历史内容,而是根据当前需求,主动计算当前内容与各个上下文位置之间的相关程度,再有侧重地提取信息。

如果 RNN 是“把信息一路传下去”,那么 Attention 就是“需要的时候直接回头找”。

二、Q、K、V:注意力机制的核心抽象

1. 图书馆检索系统的比喻

理解 Attention,最关键的是理解三个概念:Query、Key、Value,通常简写为 Q、K、V。

我们可以把注意力机制想象成一个图书馆检索系统。

假设你来到图书馆,想找一本关于深度学习入门的书。此时,你的需求就是 Query,也就是“查询”。

图书馆里每本书都有一个标签,例如“数学”“深度学习”“小说”“历史”。这些标签就是 Key,也就是“”。

当你根据标签找到匹配度较高的书之后,真正被你阅读的是书的内容。这个内容就是 Value,也就是“”。

在这个比喻里,Attention 的工作流程非常清晰:

  • 你带着自己的需求 Query;
  • 用它去和所有书的标签 Key 做匹配;
  • 匹配度高的书获得更高关注度;
  • 最后根据关注程度,从对应的内容 Value 中提取信息。

把这个过程翻译成模型语言,就是:当前要处理的内容带着一个查询向量,去和上下文中每个位置的键向量比较相似度,再根据相似度对值向量做加权求和。

2. Query、Key、Value 分别代表什么

在序列模型中,一个词可以同时承担不同角色。

当它作为“提问者”时,它需要表达“我想找什么信息”,这对应 Query。

当它作为“被检索者”时,它需要表达“我能提供什么类型的信息”,这对应 Key。

当它作为“信息提供者”时,它需要表达“我实际包含的内容是什么”,这对应 Value。

同一个词,在不同角色下应该有不同的表示。因此,模型通常会用三个不同的可学习矩阵 $W_Q$、$W_K$、$W_V$,把原始词向量投影到三个不同的空间中。

这样做的好处是:模型可以分别学习“如何提问”“如何被匹配”“如何提供内容”,而不是强迫一个向量同时完成所有任务。

3. 一个帮助理解的小例子

假设我们在做一个外卖推荐系统。当前用户的需求被编码成一个 Query 向量,大致含义是:“我很饿,想吃辣的,预算 50 元。”

系统里有三道菜:

  • 菜 A:Key 是“清淡、昂贵”,Value 是“清蒸鲈鱼,100 元”;
  • 菜 B:Key 是“辛辣、便宜”,Value 是“麻婆豆腐,20 元”;
  • 菜 C:Key 是“辛辣、昂贵”,Value 是“麻辣龙虾,150 元”。

用 Query 去和三个 Key 做匹配时,菜 B 的标签“辛辣、便宜”与用户需求最接近,因此它的相似度分数最高。经过 Softmax 归一化后,菜 B 对应的注意力权重也最大。最终推荐结果不会完全等于某一道菜,而是所有菜的 Value 按权重加权融合,但整体会明显更接近菜 B。

这个例子说明:Attention 的输出并不是简单地“选中某一个对象”,而是根据匹配程度,对所有候选内容做一次加权融合。

三、Soft Attention 的数学表达

1. 从词向量到 q、k、v

假设一个句子有 $N$ 个词,每个词的初始向量维度为 $d$。我们把整个句子的输入记成矩阵 $X$,其形状为:$X \in \mathbb{R}^{N \times d}$

对于其中第 $i$ 个词,它的初始向量是 $x_i$。模型通过三个不同的权重矩阵,将它分别映射成 Query、Key、Value:$q_i = x_i W_Q$,$k_i = x_i W_K$,$v_i = x_i W_V$。

这里需要注意:无论第 1 个词还是第 100 个词,使用的都是同一套 $W_Q$、$W_K$、$W_V$。也就是说,参数在所有位置上共享,但每个词经过投影后得到的 $q_i$、$k_i$、$v_i$ 通常不同,因为输入 $x_i$ 不同。

2. 用点积计算相似度

在得到 Query 和 Key 之后,下一步是计算当前词与其他词之间的匹配程度。

如果当前要处理第 $i$ 个词,它的 Query 是 $q_i$。我们要看它应该多关注第 $j$ 个词,就用 $q_i$ 和第 $j$ 个词的 Key $k_j$ 计算点积:

$$ \text{score}_{i,j} = q_i \cdot k_j^T $$

点积越大,通常表示两个向量在当前语义空间中越接近,也就说明第 $i$ 个词越应该关注第 $j$ 个词。

这里使用点积并不是随意选择。点积计算简单,适合大规模矩阵运算,同时也能很好地表达向量之间的相似程度。

3. Softmax 归一化:把分数变成权重

原始 score 只是实数,可能很大,也可能很小。我们希望得到一组权重,使得这些权重满足两个条件:

  • 每个权重都在 0 到 1 之间;
  • 当前词对所有位置分配出去的权重总和为 1。

为此,我们对 score 做 Softmax:

$$ \alpha_{i,j}

\frac{\exp(\text{score}{i,j})} {\sum{m=1}^{N}\exp(\text{score}_{i,m})} $$

这里的 $\alpha_{i,j}$ 就是第 $i$ 个词对第 $j$ 个词的注意力权重。

可以把它理解为:第 $i$ 个词在观察整个句子时,决定把多少比例的注意力分配给第 $j$ 个词。

4. 加权求和:为什么一定要乘以 Value

很多初学者理解到 Softmax 为止,会觉得 Attention 已经完成了。其实还差最关键的一步:加权求和。

对于第 $i$ 个词,它最终得到的新表示为:

$$ z_i = \sum_{j=1}^{N} \alpha_{i,j} v_j $$

也就是说,先用 Query 和 Key 算出“应该关注哪里”,再用这些权重对 Value 做加权求和,得到真正融合上下文后的表示。

为什么要乘以 Value?因为 Query 和 Key 计算出来的只是“关注比例”。如果只算到 Softmax,那么模型手里只有一组比例,例如 30% 看这里、70% 看那里,但还没有拿到任何实际内容。

Value 才是真正承载信息的部分。用注意力权重去组合 Value,才是把上下文信息提取出来。

因此可以这样记忆:

  • Q 和 K 负责计算“去哪里找”;
  • V 负责提供“找到了什么”。

一句话概括:

$$ \text{Attention}(Q,K,V)

\text{Softmax}(QK^T)V $$

如果加上后面会详细讲的缩放因子,则更完整的形式是:

$$ \text{Attention}(Q,K,V)

\text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V $$

5. Attention 的任务不是直接输出答案,而是重塑表示

这里有一个非常重要的认识:Attention 层本身通常并不直接给出最终任务答案。

例如在文本分类任务中,Attention 不会直接告诉你这句话是正面还是负面;在翻译任务中,它也不会单独决定下一个词一定是什么。它更基础的作用是特征重塑,也就是把原本相对孤立的词向量,变成融合了上下文信息的新词向量。

换句话说,Attention机制本身是一种“根据上下文动态调整的word2Vec"操作。

比如“苹果”这个词,孤立地看,可能是水果,也可能是手机品牌。但当它出现在“我想买一部苹果”或者“这个苹果很甜”这样的上下文中时,模型需要通过 Attention 让“苹果”的表示吸收周围词的信息,从而变得更明确。

因此,Attention 的输出仍然是一组向量,形状通常还是 $N \times d$。只不过这些向量已经不再是单纯的初始词嵌入,而是带有上下文语义的情境化表示。

四、矩阵化的 Attention:一次为整个序列生成上下文表示

1. 单样本公式如何扩展成矩阵计算

前面我们从单个词的角度写出了公式:

$$ z_i = \sum_{j=1}^{N} \alpha_{i,j} v_j $$

但在实际深度学习框架中,我们不会一个词一个词地循环计算,而是把整个句子打包成矩阵,一次性完成计算。

假设输入矩阵为:$X \in \mathbb{R}^{N \times d}$,那么:$Q = XW_Q$,$K = XW_K$,$V = XW_V$。它们的形状通常都是:$Q, K, V \in \mathbb{R}^{N \times d}$。

如果我们使用的是 Key 向量维度 $d_k$,那么 $Q$ 和 $K$ 的最后一维就是 $d_k$。为了叙述简单,这里先不区分 $d$ 和 $d_k$。

2. $QK^T$ 的物理意义

接下来计算:$QK^T$,形状变化是:$[N \times d] \times [d \times N] = [N \times N]$。

这个 $N \times N$ 的矩阵非常关键。它被称为注意力分数矩阵,其中第 $i$ 行第 $j$ 列的元素表示:第 $i$ 个词对第 $j$ 个词的原始关注分数。也就是说,这个矩阵一次性算出了句子内部所有词两两之间的匹配程度。

随后对每一行做 Softmax:$\text{Softmax}(QK^T)$,形状仍然是:$[N \times N]$,但此时每一行的数值都被归一化成概率分布。第 $i$ 行表示第 $i$ 个词分配给所有词的注意力权重。

3. 最后乘以 V:为每个词并行做一次全局融合

最后计算:

$$ \text{Output}

\text{Softmax}(QK^T)V $$

形状变化是:$[N \times N] \times [N \times d] = [N \times d]$

输出矩阵仍然是 $N$ 个词的向量表示,每个词的维度仍然是 $d$。但每个词的内容已经发生了变化。

我们来看输出矩阵的第 $i$ 行是怎么得到的。它实际上是注意力权重矩阵的第 $i$ 行,与 Value 矩阵的每一行做加权求和:

$$ \text{output}_i

\sum_{j=1}^{N} \alpha_{i,j} v_j $$

这正是我们前面写的单词公式。

因此,矩阵形式的 Attention 本质上是在做一件事:同时为句子中的每一个词,独立地做一次全局信息加权融合。

这也是 Attention 与 RNN 的重要区别之一。RNN 是按时间步一步一步推进,而 Attention 是把整个序列看成一个空间对象,通过矩阵运算一次性完成所有位置之间的交互。

4. 为什么说 Attention 是一种“空间化”的序列建模方式

RNN 的核心计算发生在时间轴上。为了得到 $h_{100}$,模型必须先计算 $h_1$、$h_2$、……、$h_{99}$。这是一种时间递归结构。

Attention 的核心计算发生在序列空间上。它不要求先处理第 1 个词,再处理第 2 个词。所有词的 Query、Key、Value 可以同时计算,所有词之间的相似度也可以通过矩阵乘法一次性得到。

因此,我们可以这样理解:

  • RNN 是在时间维度上逐步传递信息;
  • Attention 是在空间维度上直接比较和聚合信息。

这是计算逻辑上的根本变化。

五、Attention 相比 RNN 到底解决了什么问题

1. 长距离依赖:任意两个词之间的路径长度变成 1

在 RNN 中,第 1 个词要影响第 100 个词,必须经过 99 次状态传递。路径很长,信息容易衰减,梯度也容易消失。

在 Attention 中,第 100 个词如果想知道第 1 个词的信息,会直接计算自己的 Query 与第 1 个词 Key 之间的相似度。也就是说,任意两个词之间都有一条直接通路。

从图结构的角度看,RNN 像一条链,而 Attention 更像一张全连接图。任意两个节点之间都可以直接建立联系。

这就是为什么 Attention 在处理长距离依赖时具有天然优势:它不是靠“记忆胶囊”把信息艰难地传下去,而是靠内容匹配直接找到相关信息。

2. 并行计算:GPU 可以被充分利用

RNN 的计算具有强时序依赖。计算 $h_t$ 之前,必须先得到 $h_{t-1}$。因此,即使硬件有很多并行计算单元,RNN 也很难在序列维度上充分并行。

Attention 则不同。对于一句话中的所有词,计算它们的 Query、Key、Value 时彼此没有依赖关系。计算 $QK^T$ 时,也是对所有词对的相似度做批量矩阵运算。

这意味着 Attention 可以写成高度并行的大矩阵乘法,非常适合 GPU 训练。对于大规模语料和大模型来说,这一点具有决定性意义。

3. Attention 也有代价:复杂度从线性变成平方

为了保持严谨,还需要补充一点:Attention 并不是没有代价。

RNN 处理长度为 $N$ 的序列时,计算量大致为$O(N)$。而标准 Self-Attention 需要计算 $N \times N$ 的注意力矩阵,因此计算量和显存占用大致为 $O(N^2)$ 。

当序列非常长时,这个平方复杂度会成为瓶颈。这也是后来许多长序列优化方法出现的重要原因。

不过,在常见文本长度和现代硬件条件下,Attention 的并行优势和表达能力通常远大于其复杂度代价。

六、常见误解的澄清:Attention 不是简单替换 RNN 的 h

1. Attention 不是把 h 换成 Q、K、V 这么简单

有一种常见说法:Attention 本质上就是把 RNN 里的隐藏状态 $h$ 替换成 Q、K、V。这个说法虽然抓住了“架构演进”的表面线索,但严重低估了二者之间的本质差异。

RNN 的 $h$ 是一个被不断压缩的历史状态。它试图用一个向量承载过去所有信息,并且必须按时间顺序更新。

Attention 的 Q、K、V 则是一个检索系统。它不要求把历史压缩成一个向量,而是让当前位置直接面向所有上下文,通过相似度决定应该吸收哪些信息。

因此,RNN 更像一条流水线,而 Attention 更像一个检索引擎。

2. $W_Q$、$W_K$、$W_V$ 不是三层感知机

还有一个很容易出现的误解:既然有 $W_Q$、$W_K$、$W_V$ 三个矩阵,那 Attention 是不是一个“三层感知机”?

不是。

深度学习中所说的多层感知机,也就是 MLP,通常具有两个重要特征:

  • 层与层之间是串行堆叠的;
  • 通常包含非线性激活函数,例如 ReLU、GELU 等。

而 Attention 中的 $W_Q$、$W_K$、$W_V$ 是三个并行的线性投影层。它们之间不是“第一层、第二层、第三层”的关系,而是输入 $X$ 同时经过三个不同投影,得到三个不同结果。

更准确地说:$Q = XW_Q$、$K = XW_K$、$V = XW_V$这三个操作是并行的,而且这些投影本身通常不带激活函数。Attention 的关键能力不是来自这三层线性投影本身,而是来自后续的 $QK^T$、Softmax 和与 $V$ 的加权求和。

甚至可以说,即使极端地令 $Q=K=V=X$,完全不使用可学习投影,Attention 仍然可以计算词与词之间的原始相似度。只是这样做通常不够灵活,表达能力不如学习出的 $W_Q$、$W_K$、$W_V$。

因此,$W_Q$、$W_K$、$W_V$ 更像是三个不同视角的透镜,而不是三层感知机。

七、从广义 Attention 到 Self-Attention:概念层级与历史脉络

1. 早期 Attention:RNN 时代的辅助机制

Attention 机制最早被广泛使用,是在 RNN 主导序列建模的时期。尤其是在机器翻译任务中,Encoder-Decoder 架构经常需要处理较长句子,而 RNN 的固定状态很容易丢失信息。

于是研究者提出:在解码时,不要只依赖一个最终隐藏状态,而是让 Decoder 在每一步都回头看 Encoder 留下的所有状态,并有选择地关注其中最相关的部分。

用今天的 Q、K、V 视角来概括:

  • Encoder 在各个时间步产生的隐藏状态,可以被视为 Key 和 Value;
  • Decoder 当前时刻的隐藏状态,可以被视为 Query;
  • Decoder 用自己的 Query 去匹配 Encoder 的 Key,得到注意力权重;
  • 再根据权重对 Encoder 的 Value 加权求和,得到一个上下文向量,用于生成当前词。

因此,从历史角度看,Attention 最初确实更像 RNN 的一个增强组件,而不是一个独立架构。

2. Encoder 和 Decoder 的分工

在机器翻译这样的任务中,Encoder 和 Decoder 有非常直观的分工。

Encoder 负责理解输入。例如输入是中文句子“我爱你”,Encoder 会读取这些词,并生成一组包含上下文信息的表示。

Decoder 负责生成输出。例如目标是英文句子“I love you”。Decoder 通常一个词一个词地生成:先生成“I”,再根据已有内容生成“love”,再根据已有内容生成“you”。

在原始 Transformer 架构中,Decoder 除了包含带掩码的 Self-Attention,还会包含 Cross-Attention。此时:

  • Q 来自 Decoder;
  • K 来自 Encoder;
  • V 来自 Encoder。

这就是典型的交叉注意力。它的作用是让 Decoder 在生成每个目标词时,知道应该重点参考源句子的哪个部分。

例如翻译“我爱你”时,当 Decoder 要生成“I”,它的 Query 应该更容易匹配 Encoder 中“我”的 Key,从而从对应 Value 中获取信息。

3. Self-Attention:Q、K、V 来自同一个输入序列

Self-Attention 是 Attention 的一个特例,我们现在一旦提到Attention,默认就是Self-Attention。

所谓“自”,指的是 Query、Key、Value 都来自同一个输入序列 $X$:$Q = XW_Q$,$K = XW_K$,$V = XW_V$

也就是说,句子中的每个词既是提问者,也是被检索者,也是信息提供者。就是我们之前讲的内容。

Self-Attention 的核心作用是建立句子内部词与词之间的直接关联。它不依赖时间顺序传递信息,而是通过语义相似度直接建立长距离联系。

4. Cross-Attention 与 Self-Attention 的区别

区分 Attention 与 Self-Attention,最关键的问题是:Q 和 K、V 是否来自同一个来源。

可以用下面的方式理解:

  • 如果 Q 来自一个序列,K 和 V 来自另一个序列,这是 Cross-Attention;
  • 如果 Q、K、V 都来自同一个序列,这是 Self-Attention。

Cross-Attention 常用于两个不同对象之间的对齐,例如:

  • 源语言和目标语言之间的对齐;
  • 文本和图像之间的对齐;
  • 用户指令和检索文档之间的对齐。

Self-Attention 则用于同一个对象内部的关系建模,例如:

  • 一个句子内部词与词之间的关系;
  • 一段代码内部 token 之间的关系;
  • 一张图像被切分成 patch 后,patch 之间的关系。

在现代大语言模型中,尤其是 Decoder-only 架构中,模型通常没有 Encoder,也不使用 Cross-Attention,主要使用带掩码的 Self-Attention。因此,很多资料里提到的 Attention,实际指的就是 Self-Attention。

5. 原始 Attention 中的 K、V 并不天然具有“自注意力”

这里需要特别澄清一个容易出现的误解。

在早期 RNN + Attention 架构中,Encoder 的隐藏状态被用作 Key 和 Value,但这并不意味着这些 Key 和 Value 之间已经存在 Self-Attention。

更准确地说,那时的 Encoder 如果是 RNN,它只是按时间步生成一系列隐藏状态。这些隐藏状态之后被 Attention 机制拿来供 Decoder 查询,但它们彼此之间并没有通过 $KK^T$ 这样的注意力矩阵进行内部交互。

因此,不能说“原始 Attention 中 K 和 V 已经有自注意力,只是后来把 Q 合并进去了”。

更合理的历史理解是:

  • 早期 Attention 提出了“用查询去匹配外部信息”的思想;
  • Self-Attention 则进一步把查询、键、值全部放回同一个输入序列内部;
  • Transformer 则彻底摆脱 RNN,把 Self-Attention 作为核心结构。

6. Decoder 中的掩码:防止模型看到未来

在生成式任务中,Decoder 通常需要逐词生成。当模型生成第 3 个词时,它不应该看到第 4 个词及之后的内容,否则训练时就会“作弊”。

为了解决这个问题,Decoder 中的 Self-Attention 会使用掩码机制。具体来说,在计算注意力分数时,把未来位置对应的分数设置成极小值,例如负无穷。经过 Softmax 后,这些位置的注意力权重就接近 0。

这样,模型在计算当前位置时,就只能看到它之前的位置。

这部分内容会在多头注意力和 Transformer Block 中更完整地展开,但它的基本思想可以先记住:掩码不是改变 Attention 的数学本质,而是限制注意力能够看到的位置范围。

八、Self-Attention 的完整公式再解释

现代 Self-Attention 的标准公式通常写作:

$$ \text{Attention}(Q,K,V)

\text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V $$

其中 $d_k$ 是 Key 向量的维度。在单头 Attention 中,它常常等于输入向量维度;在多头 Attention 中,它通常是每个头的维度。

很多初学者第一次看到这个公式时会问:为什么不能直接写我们前文就提到的$\text{Softmax}(QK^T)V$,而要除以一个 $\sqrt{d_k}$?实际上,我们可以简单的理解成这是一个“数据调整”工具,为了让结果向量的方差不那么大

假设 $q$ 和 $k$ 都是 $d_k$ 维向量,并且它们的各个分量近似独立,均值为 0,方差为 1。

点积为:

$$ q \cdot k = \sum_{i=1}^{d_k} q_i k_i $$

对于每一项 $q_i k_i$,由于 $q_i$ 和 $k_i$ 独立且均值为 0,有:

$$ \mathbb{E}[q_i k_i] = \mathbb{E}[q_i]\mathbb{E}[k_i] = 0 $$

其方差为:

$$ \text{Var}(q_i k_i)

\mathbb{E}[q_i^2 k_i^2] - \mathbb{E}[q_i k_i]^2 $$

由于独立性:

$$ \mathbb{E}[q_i^2 k_i^2]

\mathbb{E}[q_i^2]\mathbb{E}[k_i^2]

1 \times 1

1 $$

所以:

$$ \text{Var}(q_i k_i) = 1 $$

如果各项之间相互独立,那么和的方差等于方差之和:

$$ \text{Var}(q \cdot k)

\sum_{i=1}^{d_k} \text{Var}(q_i k_i)

d_k $$

也就是说,点积结果的方差会随着维度 $d_k$ 线性增长。

如果 $d_k = 512$,点积结果的方差就是 512,标准差约为 22.6。这意味着点积值可能变得很大。

如果这些很大的数值直接进入 Softmax,会发生什么?

Softmax 的形式是:

$$ \text{Softmax}(z_i)

\frac{\exp(z_i)}{\sum_j \exp(z_j)} $$

当某些 $z_i$ 非常大,而另一些 $z_j$ 非常小时,指数函数会迅速放大这种差异。结果就是 Softmax 输出接近 one-hot 分布,例如:

$$ [0.9999, 0.0001, 0.0000, \dots] $$

这种极端分布不仅让注意力变得非常尖锐,还会带来严重的梯度问题。

Softmax 的梯度与输出概率有关。当某个位置的概率接近 1,其他位置接近 0 时,梯度会变得非常小。模型很难再通过反向传播有效调整参数,训练就会变得缓慢甚至停滞。

为了解决这个问题,我们在点积之后除以 $\sqrt{d_k}$。根据方差性质:

$$ \text{Var}(aX) = a^2 \text{Var}(X) $$

如果原始点积方差是 $d_k$,乘以 $1/\sqrt{d_k}$ 后,方差变成:

$$ \left(\frac{1}{\sqrt{d_k}}\right)^2 d_k = 1 $$

这样可以让点积结果保持在一个相对稳定的数值范围,使 Softmax 处于更适合学习的区域。

因此,缩放因子的本质是控制点积尺度,防止 Softmax 饱和,保证训练稳定。

九、位置信息的丢失与位置编码

1. Self-Attention 本身没有顺序感

Self-Attention 虽然可以建模任意两个词之间的关系,但它有一个天然缺陷:它不关心词序。

例如两个句子:

  • 狗咬人;
  • 人咬狗。

这两个句子包含的词完全相同,只是顺序不同。如果仅仅计算 Self-Attention,那么模型主要看到的是“狗”“咬”“人”这些词之间的相似度关系,而无法自然区分谁在前、谁在后。

更严格地说,标准 Self-Attention 对输入具有置换等变性:如果把输入矩阵的行顺序打乱,输出矩阵的行也会以相同方式被重新排列,但每个位置对应的计算规则本身并不知道原始顺序。

对于语言来说,这是致命问题。因为顺序往往决定语义。为了解决这个问题,必须在输入中显式加入位置信息。

2. 绝对位置编码:用正弦和余弦表示位置

原始 Transformer 采用的方法是在词向量上加入位置编码:

$$ \text{input}

\text{token embedding} + \text{positional encoding} $$

也就是说,模型看到的输入不再只是词义向量,而是“词义向量 + 位置向量”。

原论文中使用的是正弦和余弦函数:

$$ PE_{(pos, 2i)}

\sin\left(\frac{pos}{10000^{2i/d}}\right) $$

$$ PE_{(pos, 2i+1)}

\cos\left(\frac{pos}{10000^{2i/d}}\right) $$

这里 $pos$ 表示 token 在序列中的位置,$i$ 表示维度索引,$d$ 是模型维度。

这种设计有几个优点。

第一,每个位置都会得到一个确定的编码向量。不同位置的编码不会完全相同。

第二,正弦和余弦具有周期性,数值不会随着位置增大而无限膨胀。相比直接加入 1、2、3 这样的整数,它更容易保持数值稳定。

第三,三角函数之间具有线性组合关系。对于固定偏移 $k$,$PE_{pos+k}$ 可以近似由 $PE_{pos}$ 的线性变换得到。这使模型更容易学习相对位置关系。

3. RoPE:把位置信息变成旋转角度

现代大语言模型中,RoPE,也就是旋转位置编码,被广泛使用。它的思想非常优雅:不是简单地把位置向量加到词向量上,而是根据 token 的位置,对 Query 和 Key 向量进行旋转。

直观理解是:如果某个 token 在位置 $m$,就让它的 Query 或 Key 旋转一个与 $m$ 相关的角度;如果另一个 token 在位置 $n$,也让它旋转一个与 $n$ 相关的角度。

当两个旋转后的向量做点积时,绝对位置 $m$ 和 $n$ 的影响会被部分消去,保留下来的是它们之间的相对距离 $m-n$。

这正是 RoPE 的关键优势:它让注意力分数天然包含相对位置信息。

在语言中,相对位置通常比绝对位置更重要。例如,一个词是否修饰它前面最近的词,往往比它到底处于全句第几个位置更有意义。

RoPE 还有几个工程上的优点:

  • 旋转不改变向量长度,因此不容易破坏原有语义空间;
  • 实现可以通过张量操作高效完成;
  • 对长度外推通常有较好表现。

一个简化的 RoPE 伪代码可以写成:

1
2
3
4
5
6
7
8
9
# q, k 的形状通常为 [batch, seq_len, head_dim]
# position_ids 为每个 token 的位置编号

cos, sin = get_rope_cos_sin(position_ids)

q_rotated = q * cos + rotate_half(q) * sin
k_rotated = k * cos + rotate_half(k) * sin

attention_scores = q_rotated @ k_rotated.transpose(-1, -2)

这里的 rotate_half 可以理解为一种实现复数旋转思想的张量操作。它把向量的不同维度组合起来,模拟二维平面上的旋转。

通过这种方式,位置信息不再是一个额外拼接的向量,而是被嵌入到 Query 和 Key 的几何关系中。这也是很多现代模型偏爱 RoPE 的原因。

十、总结:Attention 的核心线索

1. 从 RNN 到 Attention,是信息流动方式的根本变化

RNN 依靠隐藏状态在时间上传递信息。它的优点是自然适合序列,缺点是串行、路径长、容易遗忘。

Attention 则依靠 Query、Key、Value 构建检索机制。它让每个位置可以直接观察其他位置,通过相似度分配权重,再对 Value 做加权求和。

因此,Attention 的核心不是简单替换某个变量,而是把“顺序传递”变成了“全局检索”。

2. Attention 的数学主线非常清晰

Attention 的基本流程可以概括为四步:

  • 用输入生成 Q、K、V;
  • 用 Q 和 K 的点积计算相似度;
  • 用 Softmax 把相似度变成权重;
  • 用权重对 V 加权求和,得到新的表示。

完整公式为:

$$ \text{Attention}(Q,K,V)

\text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V $$

其中,除以 $\sqrt{d_k}$ 是为了控制点积尺度,避免 Softmax 进入梯度极小的饱和区域。

3. Attention 的输出是上下文融合后的表示

Attention 本身通常不是最终预测器。它的主要作用是把孤立的 token 表示变成上下文化表示。

单层 Attention 更像一次高级特征融合。多层 Attention 与其他模块堆叠之后,模型才能逐步构造更复杂的语言理解和生成能力。

4. Self-Attention 是 Attention 的特例,也是现代模型的主力

广义 Attention 允许 Q 与 K、V 来自不同来源,例如 Decoder 查询 Encoder,这是 Cross-Attention。

Self-Attention 则要求 Q、K、V 都来自同一个输入序列。现代大语言模型中,尤其是 Decoder-only 架构中,主要使用的就是 Self-Attention。因此,很多语境下人们说 Attention,实际指的就是 Self-Attention。

5. 位置编码补上了 Attention 缺失的顺序信息

Self-Attention 本身无法区分词序。为了解决这个问题,需要引入位置编码。

原始 Transformer 使用正弦余弦绝对位置编码,将位置信息加到 token embedding 上。现代模型则更常使用 RoPE,通过旋转 Query 和 Key,让点积自然包含相对位置信息。

6. 后续还可以继续展开的内容

到这里,我们已经讲清楚了 Attention、Self-Attention、缩放因子和位置编码的核心思想。但 Transformer 还有几个同样重要的部分:

  • 多头注意力:为什么要把一个 Attention 分成多个头;
  • 残差连接:如何让深层网络更容易训练;
  • LayerNorm/RMSNorm:如何稳定中间激活的分布;
  • FFN:Transformer 中真正进行逐位置非线性加工的重要模块;
  • Decoder-only 模型中的因果掩码:如何在生成任务中防止看到未来。

这些内容会在后续篇章中继续展开。