前言
在前面的文章中,我们已经逐步解决了 Transformer 中一个非常核心的问题。
首先,我们在第一篇中讨论了为什么传统的 RNN、LSTM 在处理长距离依赖和并行计算时存在局限,因此需要一种能够直接建立任意 Token 之间联系的机制。
随后,我们进入 Transformer 的核心。
我们先理解了 Self-Attention,知道了 Query、Key、Value 是如何计算的:
Q
=
X
W
Q
Q=XW_Q
Q=XWQ
K
=
X
W
K
K=XW_K
K=XWK
V
=
X
W
V
V=XW_V
V=XWV
然后通过 Scaled Dot-Product Attention 计算 Token 之间的关联:
Attention
(
Q
,
K
,
V
)
=
softmax
(
Q
K
T
d
k
)
V
\\operatorname{Attention}(Q,K,V)= \\operatorname{softmax} \\left( \\frac{QK^T}{\\sqrt{d_k}} \\right)V
Attention(Q,K,V)=softmax(dk
QKT)V
在上一篇文章中,我们又进一步理解了 Multi-Head Attention。
如果只有一个 Attention,模型只能在一个表示空间中建立 Token 之间的关系,而 Multi-Head Attention 则把这种能力扩展到了多个不同的表示子空间:
MultiHead
(
Q
,
K
,
V
)
=
Concat
(
head
1
,
…
,
head
h
)
W
O
\\operatorname{MultiHead}(Q,K,V)= \\operatorname{Concat} \\left( \\operatorname{head}_1, \\ldots, \\operatorname{head}_h \\right) W^O
MultiHead(Q,K,V)=Concat(head1,…,headh)WO
到这里,看起来 Transformer 已经能够很好地理解一句话了。
但是仔细想一想,我们会发现一个非常奇怪的问题:
Transformer 真的知道一个 Token 在句子中的位置吗?
例如:
我 喜欢 你
和:
你 喜欢 我
两个句子使用的是完全相同的三个 Token,只是顺序发生了变化。
但是它们表达的意思显然不同。
如果模型不知道 Token 的位置,那么它又怎么知道谁在前面、谁在后面、谁是第一个词、谁是第二个词?
这就是这一篇我们要解决的问题。
答案就是:
Positional Encoding,位置编码。
目录
- 一、为什么 Transformer 需要位置编码?
- 1. Self-Attention 本身不知道顺序
- 2. 一个非常直观的例子
- 二、位置编码到底是什么?
- 1. 给每一个位置一个向量
- 2. 为什么可以直接相加?
- 三、Transformer 原论文中的位置编码
- 1.
p
o
s
pos
pos 是什么? - 2.
i
i
i 是什么? - 3. 为什么要使用正弦和余弦?
- 1.
- 四、为什么不同维度要使用不同频率?
- 五、正弦位置编码为什么能够帮助模型理解相对位置?
- 六、绝对位置编码和可学习位置编码
- 1. Sinusoidal Encoding:固定位置编码
- 2. Learned Positional Embedding:可学习位置编码
- 3. 两种方法有什么区别?
- 七、从绝对位置到相对位置
- 八、RoPE:现代大语言模型中的重要位置表示方法
- 九、位置编码放在哪里?
- 十、把前面五篇文章串起来
- 十一、这一篇最应该记住的内容
- 结语:Attention 有了,顺序也有了,那么一个 Transformer Block 还缺什么?
一、为什么 Transformer 需要位置编码?
1. Self-Attention 本身不知道顺序
这是理解位置编码最关键的一点。
我们之前讲 Self-Attention 的时候,一直在讨论:
一个 Token 应该关注哪些 Token?
例如:
小明 喜欢 吃 苹果
在 Self-Attention 中,“吃”可以关注“小明”,也可以关注“苹果”。
但是 Attention 做的事情本质上是计算 Token 之间的相关程度。
它看到的是:
Token 1
Token 2
Token 3
Token 4
以及它们对应的向量。
它本身并没有一个天然的机制告诉自己:
Token 1 是第 1 个
Token 2 是第 2 个
Token 3 是第 3 个
Token 4 是第 4 个
这和 RNN 有一个非常明显的区别。
RNN 天然按照顺序处理:
第一个 Token
↓
第二个 Token
↓
第三个 Token
↓
第四个 Token
因此“前后顺序”本身就存在于 RNN 的计算过程里。
而 Transformer 的 Self-Attention 更像是:
Token 1 ─┐
Token 2 ─┤
Token 3 ─┼──→ 同时进行关系计算
Token 4 ─┤
Token 5 ─┘
所有 Token 可以并行参与计算。
这带来了 Transformer 非常重要的并行计算能力,但也带来了一个新的问题:
既然所有 Token 可以同时处理,那么顺序信息从哪里来?
2. 一个非常直观的例子
假设我们有两个句子:
我 喜欢 你
和:
你 喜欢 我
假设暂时不考虑位置,只看 Token:
句子 A:
我
喜欢
你
句子 B:
你
喜欢
我
两个句子包含完全相同的 Token。
如果模型只有 Token Embedding,而没有任何位置信息,那么模型很难仅仅根据这些 Token 向量知道它们的排列顺序。
我们可以把这个问题理解成:
Token Embedding:
我 → [向量]
喜欢 → [向量]
你 → [向量]
这些向量只描述“这个 Token 是谁”,并没有描述:
它现在位于什么位置?
所以我们还需要额外的信息:
我 + 位置 1
喜欢 + 位置 2
你 + 位置 3
这样模型才能同时知道:
Token 是什么
+
Token 在哪里
这就是位置编码要解决的问题。
下面用一张图对比 RNN 与 Transformer 在处理顺序信息时的本质区别:
#mermaid-svg-XY9xtX0QpmkeuUDk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XY9xtX0QpmkeuUDk .error-icon{fill:#552222;}#mermaid-svg-XY9xtX0QpmkeuUDk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XY9xtX0QpmkeuUDk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XY9xtX0QpmkeuUDk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XY9xtX0QpmkeuUDk .marker.cross{stroke:#333333;}#mermaid-svg-XY9xtX0QpmkeuUDk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XY9xtX0QpmkeuUDk p{margin:0;}#mermaid-svg-XY9xtX0QpmkeuUDk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-XY9xtX0QpmkeuUDk .cluster-label text{fill:#333;}#mermaid-svg-XY9xtX0QpmkeuUDk .cluster-label span{color:#333;}#mermaid-svg-XY9xtX0QpmkeuUDk .cluster-label span p{background-color:transparent;}#mermaid-svg-XY9xtX0QpmkeuUDk .label text,#mermaid-svg-XY9xtX0QpmkeuUDk span{fill:#333;color:#333;}#mermaid-svg-XY9xtX0QpmkeuUDk .node rect,#mermaid-svg-XY9xtX0QpmkeuUDk .node circle,#mermaid-svg-XY9xtX0QpmkeuUDk .node ellipse,#mermaid-svg-XY9xtX0QpmkeuUDk .node polygon,#mermaid-svg-XY9xtX0QpmkeuUDk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XY9xtX0QpmkeuUDk .rough-node .label text,#mermaid-svg-XY9xtX0QpmkeuUDk .node .label text,#mermaid-svg-XY9xtX0QpmkeuUDk .image-shape .label,#mermaid-svg-XY9xtX0QpmkeuUDk .icon-shape .label{text-anchor:middle;}#mermaid-svg-XY9xtX0QpmkeuUDk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XY9xtX0QpmkeuUDk .rough-node .label,#mermaid-svg-XY9xtX0QpmkeuUDk .node .label,#mermaid-svg-XY9xtX0QpmkeuUDk .image-shape .label,#mermaid-svg-XY9xtX0QpmkeuUDk .icon-shape .label{text-align:center;}#mermaid-svg-XY9xtX0QpmkeuUDk .node.clickable{cursor:pointer;}#mermaid-svg-XY9xtX0QpmkeuUDk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XY9xtX0QpmkeuUDk .arrowheadPath{fill:#333333;}#mermaid-svg-XY9xtX0QpmkeuUDk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XY9xtX0QpmkeuUDk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XY9xtX0QpmkeuUDk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XY9xtX0QpmkeuUDk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XY9xtX0QpmkeuUDk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XY9xtX0QpmkeuUDk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XY9xtX0QpmkeuUDk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XY9xtX0QpmkeuUDk .cluster text{fill:#333;}#mermaid-svg-XY9xtX0QpmkeuUDk .cluster span{color:#333;}#mermaid-svg-XY9xtX0QpmkeuUDk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XY9xtX0QpmkeuUDk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XY9xtX0QpmkeuUDk rect.text{fill:none;stroke-width:0;}#mermaid-svg-XY9xtX0QpmkeuUDk .icon-shape,#mermaid-svg-XY9xtX0QpmkeuUDk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XY9xtX0QpmkeuUDk .icon-shape p,#mermaid-svg-XY9xtX0QpmkeuUDk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XY9xtX0QpmkeuUDk .icon-shape .label rect,#mermaid-svg-XY9xtX0QpmkeuUDk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XY9xtX0QpmkeuUDk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XY9xtX0QpmkeuUDk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XY9xtX0QpmkeuUDk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Transformer:并行处理
Token 1
同时进行关系计算
Token 2
Token 3
Token 4
RNN:串行处理
第一个 Token
第二个 Token
第三个 Token
第四个 Token
顺序信息从哪里来?
需要引入位置编码
二、位置编码到底是什么?
1. 给每一个位置一个向量
最直观的理解就是:
给句子中的每一个位置分配一个位置向量。
例如:
第 0 个位置 → P0
第 1 个位置 → P1
第 2 个位置 → P2
第 3 个位置 → P3
…
与此同时,每个 Token 本身也有一个 Embedding:
我 → E我
喜欢 → E喜欢
你 → E你
于是我们把两种信息结合起来:
Token Embedding
+
Position Encoding
↓
Transformer 输入
数学上可以简单表示为:
X
=
E
+
P
X=E+P
X=E+P
其中:
-
E
E
E 表示 Token Embedding -
P
P
P 表示 Position Encoding -
X
X
X 表示最终输入 Transformer 的表示
对于第
i
i
i 个 Token:
X
i
=
E
i
+
P
i
X_i=E_i+P_i
Xi=Ei+Pi
这样 Transformer 看到的就不再只是“我”这个 Token,而是一个同时携带 Token 内容和位置信息的向量。
2. 为什么可以直接相加?
可能有人会产生一个疑问:
Token Embedding 和 Position Encoding 是两种完全不同的信息,为什么可以直接相加?
关键就在于:
它们通常具有相同的维度。
假设模型的隐藏维度是:
d
model
=
512
d_{\\text{model}}=512
dmodel=512
那么:
Token Embedding:
[512 维]
位置编码:
[512 维]
于是可以直接进行逐元素相加:
X
i
=
E
i
+
P
i
X_i=E_i+P_i
Xi=Ei+Pi
得到的仍然是一个 512 维向量。
这样做的好处是非常简单。
我们不需要把:
Token Embedding
+
Position Encoding
拼成一个更大的向量。
否则维度会变成:
512
+
512
=
1024
512+512=1024
512+512=1024
而直接相加之后:
512
+
512
→
512
512+512\\rightarrow512
512+512→512
模型的整体维度保持不变。
更重要的是,经过后续的线性层和 Attention 计算,模型可以逐渐学会如何从这个组合向量中利用 Token 信息和位置信息。
下面这张图展示了位置编码与 Token Embedding 融合后输入 Transformer 的完整过程:
#mermaid-svg-QHRXFg3XJ7Zty5Jn{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .error-icon{fill:#552222;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .marker.cross{stroke:#333333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn p{margin:0;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .cluster-label text{fill:#333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .cluster-label span{color:#333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .cluster-label span p{background-color:transparent;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .label text,#mermaid-svg-QHRXFg3XJ7Zty5Jn span{fill:#333;color:#333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .node rect,#mermaid-svg-QHRXFg3XJ7Zty5Jn .node circle,#mermaid-svg-QHRXFg3XJ7Zty5Jn .node ellipse,#mermaid-svg-QHRXFg3XJ7Zty5Jn .node polygon,#mermaid-svg-QHRXFg3XJ7Zty5Jn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .rough-node .label text,#mermaid-svg-QHRXFg3XJ7Zty5Jn .node .label text,#mermaid-svg-QHRXFg3XJ7Zty5Jn .image-shape .label,#mermaid-svg-QHRXFg3XJ7Zty5Jn .icon-shape .label{text-anchor:middle;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .rough-node .label,#mermaid-svg-QHRXFg3XJ7Zty5Jn .node .label,#mermaid-svg-QHRXFg3XJ7Zty5Jn .image-shape .label,#mermaid-svg-QHRXFg3XJ7Zty5Jn .icon-shape .label{text-align:center;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .node.clickable{cursor:pointer;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .arrowheadPath{fill:#333333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QHRXFg3XJ7Zty5Jn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QHRXFg3XJ7Zty5Jn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QHRXFg3XJ7Zty5Jn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .cluster text{fill:#333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .cluster span{color:#333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QHRXFg3XJ7Zty5Jn rect.text{fill:none;stroke-width:0;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .icon-shape,#mermaid-svg-QHRXFg3XJ7Zty5Jn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .icon-shape p,#mermaid-svg-QHRXFg3XJ7Zty5Jn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .icon-shape .label rect,#mermaid-svg-QHRXFg3XJ7Zty5Jn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QHRXFg3XJ7Zty5Jn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QHRXFg3XJ7Zty5Jn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QHRXFg3XJ7Zty5Jn :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Token 输入
Token Embedding
Position Encoding
逐元素相加
X = E + P
Transformer
Multi-Head Attention
三、Transformer 原论文中的位置编码
讲到位置编码,就绕不开 Transformer 原论文中的经典方案:
Sinusoidal Positional Encoding,正弦位置编码。
它使用正弦函数和余弦函数为不同位置构造向量。
具体公式为:
P
E
(
p
o
s
,
2
i
)
=
sin
(
p
o
s
10000
2
i
d
model
)
PE_{(pos,2i)}= \\sin \\left( \\frac{pos}{10000^{\\frac{2i}{d_{\\text{model}}}}} \\right)
PE(pos,2i)=sin(10000dmodel2ipos)
P
E
(
p
o
s
,
2
i
+
1
)
=
cos
(
p
o
s
10000
2
i
d
model
)
PE_{(pos,2i+1)}= \\cos \\left( \\frac{pos}{10000^{\\frac{2i}{d_{\\text{model}}}}} \\right)
PE(pos,2i+1)=cos(10000dmodel2ipos)
第一次看到这个公式可能会觉得非常复杂。
不要急。
我们把它一点一点拆开。
1.
p
o
s
pos
pos 是什么?
p
o
s
pos
pos 就是 Token 所在的位置。
例如:
我 喜欢 学习 Transformer
0 1 2 3
那么:
我 → pos = 0
喜欢 → pos = 1
学习 → pos = 2
Transformer → pos = 3
这里的编号从 0 开始还是从 1 开始并不是最重要的。
真正重要的是:
不同位置必须能够产生不同的位置表示。
2.
i
i
i 是什么?
i
i
i 可以理解成位置向量中的维度索引。
假设:
d
model
=
8
d_{\\text{model}}=8
dmodel=8
那么一个位置编码可以写成:
[PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7]
这里的
i
i
i 就是在控制我们正在计算哪一组维度。
正弦函数负责偶数维:
P
E
(
p
o
s
,
2
i
)
PE_{(pos,2i)}
PE(pos,2i)
余弦函数负责奇数维:
P
E
(
p
o
s
,
2
i
+
1
)
PE_{(pos,2i+1)}
PE(pos,2i+1)
所以可以简单理解成:
偶数维 → sin
奇数维 → cos
3. 为什么要使用正弦和余弦?
我们当然可以直接写:
位置 0 → [0, 0, 0, 0, …]
位置 1 → [1, 1, 1, 1, …]
位置 2 → [2, 2, 2, 2, …]
但是这种方式比较简单粗暴。
Transformer 原论文选择了一组具有不同频率的正弦和余弦函数。
你可以把它理解成:
位置
↓
多个不同频率的波
↓
每个位置得到一个独特的组合
↓
形成位置向量
例如:
维度 0:变化比较快
维度 1:变化比较快
维度 2:变化慢一些
维度 3:变化慢一些
维度 4:变化更慢
维度 5:变化更慢
…
最终一个位置会得到一组正弦、余弦值。
虽然每一维都只是一个简单的周期函数,但是把很多不同频率组合起来以后,就能够形成丰富的位置表示。
下面这张图展示了位置编码在 Transformer 中的具体位置:
#mermaid-svg-r7xaFm7XtBakr4O2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r7xaFm7XtBakr4O2 .error-icon{fill:#552222;}#mermaid-svg-r7xaFm7XtBakr4O2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r7xaFm7XtBakr4O2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r7xaFm7XtBakr4O2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r7xaFm7XtBakr4O2 .marker.cross{stroke:#333333;}#mermaid-svg-r7xaFm7XtBakr4O2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r7xaFm7XtBakr4O2 p{margin:0;}#mermaid-svg-r7xaFm7XtBakr4O2 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-r7xaFm7XtBakr4O2 .cluster-label text{fill:#333;}#mermaid-svg-r7xaFm7XtBakr4O2 .cluster-label span{color:#333;}#mermaid-svg-r7xaFm7XtBakr4O2 .cluster-label span p{background-color:transparent;}#mermaid-svg-r7xaFm7XtBakr4O2 .label text,#mermaid-svg-r7xaFm7XtBakr4O2 span{fill:#333;color:#333;}#mermaid-svg-r7xaFm7XtBakr4O2 .node rect,#mermaid-svg-r7xaFm7XtBakr4O2 .node circle,#mermaid-svg-r7xaFm7XtBakr4O2 .node ellipse,#mermaid-svg-r7xaFm7XtBakr4O2 .node polygon,#mermaid-svg-r7xaFm7XtBakr4O2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r7xaFm7XtBakr4O2 .rough-node .label text,#mermaid-svg-r7xaFm7XtBakr4O2 .node .label text,#mermaid-svg-r7xaFm7XtBakr4O2 .image-shape .label,#mermaid-svg-r7xaFm7XtBakr4O2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-r7xaFm7XtBakr4O2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r7xaFm7XtBakr4O2 .rough-node .label,#mermaid-svg-r7xaFm7XtBakr4O2 .node .label,#mermaid-svg-r7xaFm7XtBakr4O2 .image-shape .label,#mermaid-svg-r7xaFm7XtBakr4O2 .icon-shape .label{text-align:center;}#mermaid-svg-r7xaFm7XtBakr4O2 .node.clickable{cursor:pointer;}#mermaid-svg-r7xaFm7XtBakr4O2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r7xaFm7XtBakr4O2 .arrowheadPath{fill:#333333;}#mermaid-svg-r7xaFm7XtBakr4O2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r7xaFm7XtBakr4O2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r7xaFm7XtBakr4O2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r7xaFm7XtBakr4O2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r7xaFm7XtBakr4O2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r7xaFm7XtBakr4O2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r7xaFm7XtBakr4O2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r7xaFm7XtBakr4O2 .cluster text{fill:#333;}#mermaid-svg-r7xaFm7XtBakr4O2 .cluster span{color:#333;}#mermaid-svg-r7xaFm7XtBakr4O2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-r7xaFm7XtBakr4O2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r7xaFm7XtBakr4O2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-r7xaFm7XtBakr4O2 .icon-shape,#mermaid-svg-r7xaFm7XtBakr4O2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r7xaFm7XtBakr4O2 .icon-shape p,#mermaid-svg-r7xaFm7XtBakr4O2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r7xaFm7XtBakr4O2 .icon-shape .label rect,#mermaid-svg-r7xaFm7XtBakr4O2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r7xaFm7XtBakr4O2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r7xaFm7XtBakr4O2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r7xaFm7XtBakr4O2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
输入 Token
Token Embedding
Position Encoding
相加
Transformer
Multi-Head Attention
FFN
下一层
四、为什么不同维度要使用不同频率?
现在我们再回头看公式:
P
E
(
p
o
s
,
2
i
)
=
sin
(
p
o
s
10000
2
i
d
model
)
PE_{(pos,2i)}= \\sin \\left( \\frac{pos}{10000^{\\frac{2i}{d_{\\text{model}}}}} \\right)
PE(pos,2i)=sin(10000dmodel2ipos)
P
E
(
p
o
s
,
2
i
+
1
)
=
cos
(
p
o
s
10000
2
i
d
model
)
PE_{(pos,2i+1)}= \\cos \\left( \\frac{pos}{10000^{\\frac{2i}{d_{\\text{model}}}}} \\right)
PE(pos,2i+1)=cos(10000dmodel2ipos)
最值得注意的就是:
10000
2
i
d
model
10000^{\\frac{2i}{d_{\\text{model}}}}
10000dmodel2i
它会随着维度
i
i
i 的变化而变化。
于是不同维度对应不同的变化频率。
可以粗略理解成:
低维:
变化快
~~~~~~~
高维:
变化慢
~~~~~~~~~~~~~~
这样做有什么好处?
假设所有维度都使用完全相同的频率:
维度 0 → 快
维度 1 → 快
维度 2 → 快
维度 3 → 快
…
那么很多维度实际上是在重复表达类似的信息。
而使用不同频率之后:
维度 0 → 高频
维度 1 → 高频
维度 2 → 中高频
维度 3 → 中高频
维度 4 → 中低频
维度 5 → 中低频
维度 6 → 低频
维度 7 → 低频
不同维度就能够从不同尺度描述位置信息。
可以把它理解成:
有些维度负责观察位置的细微变化,有些维度负责观察更大范围的位置变化。
多个不同频率组合起来,就形成了一个更加丰富的位置表示。
五、正弦位置编码为什么能够帮助模型理解相对位置?
这是理解 Sinusoidal Positional Encoding 时非常值得注意的地方。
假设我们有:
P
E
(
p
o
s
)
PE(pos)
PE(pos)
和:
P
E
(
p
o
s
+
k
)
PE(pos+k)
PE(pos+k)
我们希望模型不仅能够知道:
这是第 10 个位置
这是第 20 个位置
还能够在某种程度上学习:
两个位置之间相差 10
正弦和余弦函数具有非常好的数学性质。
利用三角函数的和角公式:
sin
(
a
+
b
)
=
sin
a
cos
b
+
cos
a
sin
b
\\sin(a+b)= \\sin a\\cos b + \\cos a\\sin b
sin(a+b)=sinacosb+cosasinb
以及:
cos
(
a
+
b
)
=
cos
a
cos
b
−
sin
a
sin
b
\\cos(a+b)= \\cos a\\cos b- \\sin a\\sin b
cos(a+b)=cosacosb−sinasinb
可以发现:
如果知道某个位置的正弦和余弦表示,那么移动固定距离
k
k
k 后的位置,可以通过固定的变换关系得到。
这意味着:
位置编码不仅可以表示绝对位置,还能够让模型比较容易地学习位置之间的相对关系。
这里不需要把数学推导全部展开。
真正需要理解的是:
位置 10
↓
一组波形值
位置 11
↓
另一组波形值
位置 12
↓
继续变化
由于这些变化是连续的,所以相邻位置的表示也具有连续变化的特征。
六、绝对位置编码和可学习位置编码
讲到这里,我们需要区分两个概念。
一种是:
固定的位置编码。
另一种是:
可学习的位置编码。
1. Sinusoidal Encoding:固定位置编码
Transformer 原论文使用的就是前面介绍的正弦余弦位置编码。
它的特点是:
位置
↓
数学公式计算
↓
得到位置向量
这些位置向量并不是通过训练得到的。
也就是说,模型训练过程中不会去更新这些位置编码本身。
它们由公式直接计算出来。
2. Learned Positional Embedding:可学习位置编码
另一种方式则更加直接。
我们可以像 Token Embedding 一样,创建一个位置 Embedding 表:
Position 0 → 可学习向量
Position 1 → 可学习向量
Position 2 → 可学习向量
Position 3 → 可学习向量
…
例如:
位置 0 → P0
位置 1 → P1
位置 2 → P2
…
这些
P
i
P_i
Pi 本身就是模型参数。
训练过程中,它们会随着梯度下降不断更新。
于是:
Token Embedding
+
可学习 Position Embedding
↓
Transformer
这种方式非常直观。
模型可以自己学习什么样的位置表示对于当前任务最有用。
3. 两种方法有什么区别?
可以简单整理成:
| Sinusoidal | 数学公式计算 | 否 | 固定、简单、具有连续变化特征 |
| Learned | 参数表 | 是 | 灵活,由模型自己学习 |
这里不能简单说哪一种“绝对更好”。
因为不同模型、不同架构会选择不同的位置表示方式。
而且随着 Transformer 不断发展,位置编码本身也出现了很多新的方案。
例如现代大语言模型中经常出现的:
RoPE
Relative Position
ALiBi
…
它们都在尝试解决一个更深层的问题:
模型到底应该如何表示 Token 之间的位置关系?
七、从绝对位置到相对位置
我们前面讨论的经典位置编码,本质上是在告诉模型:
Token A:
我位于第 10 个位置
这属于绝对位置。
但是对于语言模型来说,很多时候真正重要的并不是:
你是第 10 个 Token
而是:
你和我之间距离是多少?
例如:
小明 喜欢 吃 苹果
如果当前 Token 是:
苹果
那么:
吃
就在它前面一个位置。
而:
喜欢
就在它前面两个位置。
对于很多语言关系来说:
相对距离
可能比:
绝对编号
更加重要。
因此后来的很多 Transformer 结构开始研究:
能不能直接把相对位置信息融入 Attention?
这就形成了相对位置编码等一系列方法。
八、RoPE:现代大语言模型中的重要位置表示方法
如果我们继续学习 GPT 一类的大语言模型,就会经常遇到一个名字:
RoPE,Rotary Positional Embedding,旋转位置编码。
这里先不急着深入数学推导,因为它涉及二维旋转以及复数表示等内容。
我们现在只需要建立一个基本认识:
传统位置编码更像是:
Token Embedding
+
Position Encoding
↓
输入 Transformer
而 RoPE 的思路更加特别。
它不是简单地把一个位置向量加到 Token Embedding 上,而是:
根据 Token 所处的位置,对 Query 和 Key 的表示进行旋转。
可以简单理解为:
Query / Key
↓
根据位置进行旋转
↓
带有位置信息的 Q / K
↓
计算 Attention
这样做的一个重要目的,就是让 Query 和 Key 的点积关系能够自然地包含相对位置信息。
这也是为什么当我们后面学习现代 GPT 类模型时,RoPE 会成为一个非常重要的概念。
现在暂时记住:
位置编码的发展路线,并不是简单地从“没有位置”到“加一个位置向量”就结束了,而是在不断探索如何让 Attention 更自然地理解相对位置。
下面这张图对比了传统位置编码与 RoPE 的核心区别:
#mermaid-svg-ks5hGsIYEUbpXr8K{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ks5hGsIYEUbpXr8K .error-icon{fill:#552222;}#mermaid-svg-ks5hGsIYEUbpXr8K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ks5hGsIYEUbpXr8K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ks5hGsIYEUbpXr8K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ks5hGsIYEUbpXr8K .marker.cross{stroke:#333333;}#mermaid-svg-ks5hGsIYEUbpXr8K svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ks5hGsIYEUbpXr8K p{margin:0;}#mermaid-svg-ks5hGsIYEUbpXr8K .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ks5hGsIYEUbpXr8K .cluster-label text{fill:#333;}#mermaid-svg-ks5hGsIYEUbpXr8K .cluster-label span{color:#333;}#mermaid-svg-ks5hGsIYEUbpXr8K .cluster-label span p{background-color:transparent;}#mermaid-svg-ks5hGsIYEUbpXr8K .label text,#mermaid-svg-ks5hGsIYEUbpXr8K span{fill:#333;color:#333;}#mermaid-svg-ks5hGsIYEUbpXr8K .node rect,#mermaid-svg-ks5hGsIYEUbpXr8K .node circle,#mermaid-svg-ks5hGsIYEUbpXr8K .node ellipse,#mermaid-svg-ks5hGsIYEUbpXr8K .node polygon,#mermaid-svg-ks5hGsIYEUbpXr8K .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ks5hGsIYEUbpXr8K .rough-node .label text,#mermaid-svg-ks5hGsIYEUbpXr8K .node .label text,#mermaid-svg-ks5hGsIYEUbpXr8K .image-shape .label,#mermaid-svg-ks5hGsIYEUbpXr8K .icon-shape .label{text-anchor:middle;}#mermaid-svg-ks5hGsIYEUbpXr8K .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ks5hGsIYEUbpXr8K .rough-node .label,#mermaid-svg-ks5hGsIYEUbpXr8K .node .label,#mermaid-svg-ks5hGsIYEUbpXr8K .image-shape .label,#mermaid-svg-ks5hGsIYEUbpXr8K .icon-shape .label{text-align:center;}#mermaid-svg-ks5hGsIYEUbpXr8K .node.clickable{cursor:pointer;}#mermaid-svg-ks5hGsIYEUbpXr8K .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ks5hGsIYEUbpXr8K .arrowheadPath{fill:#333333;}#mermaid-svg-ks5hGsIYEUbpXr8K .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ks5hGsIYEUbpXr8K .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ks5hGsIYEUbpXr8K .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ks5hGsIYEUbpXr8K .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ks5hGsIYEUbpXr8K .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ks5hGsIYEUbpXr8K .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ks5hGsIYEUbpXr8K .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ks5hGsIYEUbpXr8K .cluster text{fill:#333;}#mermaid-svg-ks5hGsIYEUbpXr8K .cluster span{color:#333;}#mermaid-svg-ks5hGsIYEUbpXr8K div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ks5hGsIYEUbpXr8K .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ks5hGsIYEUbpXr8K rect.text{fill:none;stroke-width:0;}#mermaid-svg-ks5hGsIYEUbpXr8K .icon-shape,#mermaid-svg-ks5hGsIYEUbpXr8K .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ks5hGsIYEUbpXr8K .icon-shape p,#mermaid-svg-ks5hGsIYEUbpXr8K .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ks5hGsIYEUbpXr8K .icon-shape .label rect,#mermaid-svg-ks5hGsIYEUbpXr8K .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ks5hGsIYEUbpXr8K .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ks5hGsIYEUbpXr8K .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ks5hGsIYEUbpXr8K :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
RoPE 旋转位置编码
Query / Key
根据位置进行旋转
带有位置信息的 Q / K
计算 Attention
传统位置编码
Token Embedding
+ Position Encoding
输入 Transformer
核心区别
RoPE 让点积关系自然包含相对位置信息
九、位置编码放在哪里?
我们现在知道位置编码要加入 Transformer。
但是它到底是在什么时候加入?
最经典的做法可以理解成:
输入 Token
↓
Token Embedding
↓
Position Encoding
↓
相加
↓
Transformer
↓
Multi-Head Attention
↓
FFN
↓
下一层
数学上:
X
=
E
+
P
X=E+P
X=E+P
其中:
E
∈
R
n
×
d
model
E\\in\\mathbb{R}^{n\\times d_{\\text{model}}}
E∈Rn×dmodel
表示 Token Embedding。
位置编码则可以表示为:
P
∈
R
n
×
d
model
P\\in\\mathbb{R}^{n\\times d_{\\text{model}}}
P∈Rn×dmodel
所以:
X
∈
R
n
×
d
model
X\\in\\mathbb{R}^{n\\times d_{\\text{model}}}
X∈Rn×dmodel
这样就可以直接把
X
X
X 送入 Transformer。
十、把前面五篇文章串起来
到这里,我们已经可以把 Transformer 前面的几个核心概念真正串起来了。
最开始,我们遇到了 RNN 的问题:
序列计算
↓
难以完全并行
↓
长距离依赖困难
于是 Transformer 使用 Attention:
Token
↓
Self-Attention
↓
直接建立 Token 之间的关系
但是一个 Attention 可能不足以从多个角度表达复杂关系。
于是:
Self-Attention
↓
Multi-Head Attention
↓
多个表示空间
↓
从多个角度建立关系
可是我们又发现:
Attention 本身不知道顺序
于是:
Token Embedding
+
Position Encoding
↓
包含 Token 信息和位置信息
↓
Multi-Head Attention
所以到这里,一个非常重要的 Transformer 输入过程就已经出现了:
Token
↓
Token Embedding
↓
┌────────┴────────┐
│ │
│ Position │
│ Encoding │
│ │
└────────┬────────┘
↓
+
↓
Transformer
↓
Multi-Head Attention
现在我们终于可以回答最开始的问题:
Transformer 是如何理解顺序的?
答案是:
Self-Attention 本身没有天然的顺序概念,因此 Transformer 需要额外引入位置信息。经典 Transformer 使用正弦和余弦函数构造位置编码,并将它与 Token Embedding 融合,让后续的 Attention 在处理 Token 时能够同时利用内容信息和位置信息。
下面这张图把前面几篇文章的核心概念完整串联起来:
#mermaid-svg-rWM86DAdmzZCOogn{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rWM86DAdmzZCOogn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rWM86DAdmzZCOogn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rWM86DAdmzZCOogn .error-icon{fill:#552222;}#mermaid-svg-rWM86DAdmzZCOogn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rWM86DAdmzZCOogn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rWM86DAdmzZCOogn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rWM86DAdmzZCOogn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rWM86DAdmzZCOogn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rWM86DAdmzZCOogn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rWM86DAdmzZCOogn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rWM86DAdmzZCOogn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rWM86DAdmzZCOogn .marker.cross{stroke:#333333;}#mermaid-svg-rWM86DAdmzZCOogn svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rWM86DAdmzZCOogn p{margin:0;}#mermaid-svg-rWM86DAdmzZCOogn .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-rWM86DAdmzZCOogn .cluster-label text{fill:#333;}#mermaid-svg-rWM86DAdmzZCOogn .cluster-label span{color:#333;}#mermaid-svg-rWM86DAdmzZCOogn .cluster-label span p{background-color:transparent;}#mermaid-svg-rWM86DAdmzZCOogn .label text,#mermaid-svg-rWM86DAdmzZCOogn span{fill:#333;color:#333;}#mermaid-svg-rWM86DAdmzZCOogn .node rect,#mermaid-svg-rWM86DAdmzZCOogn .node circle,#mermaid-svg-rWM86DAdmzZCOogn .node ellipse,#mermaid-svg-rWM86DAdmzZCOogn .node polygon,#mermaid-svg-rWM86DAdmzZCOogn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rWM86DAdmzZCOogn .rough-node .label text,#mermaid-svg-rWM86DAdmzZCOogn .node .label text,#mermaid-svg-rWM86DAdmzZCOogn .image-shape .label,#mermaid-svg-rWM86DAdmzZCOogn .icon-shape .label{text-anchor:middle;}#mermaid-svg-rWM86DAdmzZCOogn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rWM86DAdmzZCOogn .rough-node .label,#mermaid-svg-rWM86DAdmzZCOogn .node .label,#mermaid-svg-rWM86DAdmzZCOogn .image-shape .label,#mermaid-svg-rWM86DAdmzZCOogn .icon-shape .label{text-align:center;}#mermaid-svg-rWM86DAdmzZCOogn .node.clickable{cursor:pointer;}#mermaid-svg-rWM86DAdmzZCOogn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rWM86DAdmzZCOogn .arrowheadPath{fill:#333333;}#mermaid-svg-rWM86DAdmzZCOogn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rWM86DAdmzZCOogn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rWM86DAdmzZCOogn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rWM86DAdmzZCOogn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rWM86DAdmzZCOogn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rWM86DAdmzZCOogn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rWM86DAdmzZCOogn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rWM86DAdmzZCOogn .cluster text{fill:#333;}#mermaid-svg-rWM86DAdmzZCOogn .cluster span{color:#333;}#mermaid-svg-rWM86DAdmzZCOogn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rWM86DAdmzZCOogn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rWM86DAdmzZCOogn rect.text{fill:none;stroke-width:0;}#mermaid-svg-rWM86DAdmzZCOogn .icon-shape,#mermaid-svg-rWM86DAdmzZCOogn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rWM86DAdmzZCOogn .icon-shape p,#mermaid-svg-rWM86DAdmzZCOogn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rWM86DAdmzZCOogn .icon-shape .label rect,#mermaid-svg-rWM86DAdmzZCOogn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rWM86DAdmzZCOogn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rWM86DAdmzZCOogn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rWM86DAdmzZCOogn :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
RNN 的局限
难以完全并行
长距离依赖困难
Transformer 使用 Attention
Self-Attention
Multi-Head Attention
Attention 不知道顺序
Token Embedding
Position Encoding
相加
包含 Token 信息和位置信息
Multi-Head Attention
十一、这一篇最应该记住的内容
如果这一篇只记住几个核心知识点,可以记住下面这些。
第一,Self-Attention 本身并不天然包含 Token 的顺序信息。
它擅长回答:
谁应该关注谁?
但并不会自动知道:
谁在第几个位置?
第二,因此 Transformer 需要额外加入位置表示。
最经典的输入形式是:
X
=
E
+
P
X=E+P
X=E+P
其中
E
E
E 是 Token Embedding,
P
P
P 是 Position Encoding。
第三,Transformer 原论文使用正弦和余弦构造位置编码:
P
E
(
p
o
s
,
2
i
)
=
sin
(
p
o
s
10000
2
i
d
model
)
PE_{(pos,2i)}= \\sin \\left( \\frac{pos}{10000^{\\frac{2i}{d_{\\text{model}}}}} \\right)
PE(pos,2i)=sin(10000dmodel2ipos)
P
E
(
p
o
s
,
2
i
+
1
)
=
cos
(
p
o
s
10000
2
i
d
model
)
PE_{(pos,2i+1)}= \\cos \\left( \\frac{pos}{10000^{\\frac{2i}{d_{\\text{model}}}}} \\right)
PE(pos,2i+1)=cos(10000dmodel2ipos)
它通过不同频率的周期函数,为不同位置构造不同的向量表示。
第四,位置编码不仅仅是为了告诉模型:
“我是第几个 Token”
还希望让模型能够理解:
“两个 Token 之间相隔多远”
因此后来出现了越来越多关注相对位置信息的方法。
第五,现代 Transformer 和大语言模型已经不局限于最初的 Sinusoidal Positional Encoding,RoPE 等位置表示方法也成为现代架构中的重要组成部分。
结语:Attention 有了,顺序也有了,那么一个 Transformer Block 还缺什么?
到现在,我们已经把 Transformer 中最核心的一条主线建立起来了:
为什么需要 Transformer?
↓
Self-Attention
↓
Multi-Head Attention
↓
Position Encoding
现在 Transformer 已经可以:
看到 Token
↓
理解 Token 之间的关系
↓
从多个角度提取信息
↓
同时知道 Token 的位置信息
但是如果你回头看看 Transformer 的完整结构,就会发现还有几个非常重要的模块没有真正展开。
尤其是:
Multi-Head Attention
↓
?
↓
下一层 Transformer
Attention 得到的信息,究竟还要经过什么处理?
为什么 Transformer 中一定还有一个 Feed Forward Network,前馈神经网络?
为什么 Attention 后面总是能够看到:
Residual Connection
Layer Normalization
Feed Forward Network
这些模块到底分别在做什么?
如果说 Attention 负责的是:
让不同 Token 之间进行信息交流。
那么下一篇文章,我们就要继续研究:
Attention 得到信息之后,Transformer 又是如何对这些信息进行进一步加工的?
下一篇:
第六篇:FFN、残差连接与 LayerNorm——Attention 之后发生了什么?

