在transformer的encoder和decoder的输入层中,使用了Positional Encoding,使得最终的输入满足:
input_embedding+=positional_encoding
这里,input_embedding的shape为[n,b,embed_dim],positional_encoding和input_embedding形状一致.
Transformer模型抛弃了RNN、CNN作为序列学习的基本模型。循环神经网络本身就是一种顺序结构,天生就包含了词在序列中的位置信息。当抛弃循环神经网络结构,完全采用Attention取而代之,这些词序信息就会丢失,模型就没有办法知道每个词在句子中的相对和绝对的位置信息。因此,有必要把词序信号加到词向量上帮助模型学习这些信息,位置编码(Positional Encoding)就是用来解决这种问题的方法。
一种自然而然的想法是,给第一个token标记1,给第二个token标记2…,以此类推。
这种方法产生了以下几个主要问题:
(1)模型可能遇见比训练时所用的序列更长的序列。不利于模型的泛化。
(2)模型的位置表示是无界的。随着序列长度的增加,位置值会越来越大。
为了解决无界问题,可以考虑将位置值的范围限制在[0, 1]之内,其中,0表示第一个token,1表示最后一个token。比如有3个token,那么位置信息就表示成[0, 0.5, 1];若有四个token,位置信息就表示成[0, 0.33, 0.69, 1]。
但这样产生的问题是,当序列长度不同时,token间的相对距离是不一样的。例如在序列长度为3时,token间的相对距离为0.5;在序列长度为4时,token间的相对距离就变为0.33。
因此,我们需要这样一种位置表示方式,满足于:
(1)它能用来表示一个token在序列中的绝对位置
(2)在序列长度不同的情况下,不同序列中token的相对位置/距离也要保持一致
(3)可以用来表示模型在训练过程中从来没有看到过的句子长度。
这种编码的问题是有可能重复,最多可以独立表示2^embed_dim中pos,否在就会重复.
通过纵向观察one-hot编码可以知道,从右到左是一个频率逐渐降低的函数.因此考虑使用三角函数去模拟这个过程.
设每一行从左到右为[0,…i…,embed_dim-1],每一列从上到下为[0,…t…,len]
则上述编码过程可以表示为函数:
P
E
=
[
P
E
(
t
,
0
)
,
.
.
.
P
E
(
t
,
i
)
.
.
.
,
P
E
(
t
,
e
m
b
e
d
d
i
m
?
1
)
]
=
[
s
i
n
(
1
2
0
t
)
,
.
.
.
s
i
n
(
1
2
i
t
)
.
.
.
,
s
i
n
(
1
2
e
m
b
e
d
d
i
m
?
1
t
)
]
PE=[PE(t,0),...PE(t,i)...,PE(t,embed_{dim}-1)]=[sin(\frac{1}{2^{0}}t),...sin(\frac{1}{2^{i}}t)...,sin(\frac{1}{2^{embed_{dim}-1}}t)]
PE=[PE(t,0),...PE(t,i)...,PE(t,embeddim??1)]=[sin(201?t),...sin(2i1?t)...,sin(2embeddim??11?t)]
为了使得在固定的embed_dim的范围内,不容易出现重复编码,可以增加sin函数的周期.在transformer的论文中,采用了
P
E
(
t
,
i
)
=
s
i
n
(
w
i
t
)
,
其中
w
i
=
1
1000
0
i
/
(
e
m
b
e
d
d
i
m
?
1
)
PE(t,i)=sin(w_it),其中w_i=\frac{1}{10000^{i/(embed_{dim}-1)}}
PE(t,i)=sin(wi?t),其中wi?=10000i/(embeddim??1)1?
为了可以将某个位置编码通过线性转换到另一个位置编码,即:
P
E
(
t
+
δ
t
)
=
T
(
δ
t
)
?
P
E
(
t
)
PE(t+\delta t)=T(\delta t)*PE(t)
PE(t+δt)=T(δt)?PE(t)
联想向量空间的旋转转换关系:
(
s
i
n
(
t
+
δ
t
)
c
o
s
(
t
+
δ
t
)
)
=
(
c
o
s
(
δ
t
)
s
i
n
(
δ
t
)
?
s
i
n
(
δ
t
)
c
o
s
(
δ
t
)
)
?
(
s
i
n
(
t
)
c
o
s
(
t
)
)
(\begin{matrix} sin(t+\delta t)\\cos(t+\delta t)\end{matrix})=(\begin{matrix} cos(\delta t)&sin(\delta t)\\-sin(\delta t)&cos(\delta t) \end{matrix})*(\begin{matrix} sin(t)\\cos(t)\end{matrix})
(sin(t+δt)cos(t+δt)?)=(cos(δt)?sin(δt)?sin(δt)cos(δt)?)?(sin(t)cos(t)?)
使用正余弦交叉的编码
P
E
=
[
P
E
(
t
,
0
)
,
.
.
.
,
P
E
(
t
,
e
m
b
e
d
d
i
m
?
1
)
]
=
[
s
i
n
(
w
0
t
)
,
c
o
s
(
w
0
t
)
,
.
.
.
.
,
s
i
n
(
w
e
m
b
e
d
d
i
m
2
?
1
t
)
,
c
o
s
(
w
e
m
b
e
d
d
i
m
2
?
1
t
)
]
PE=[PE(t,0),...,PE(t,embed_{dim}-1)]=[sin(w_0t),cos(w_0t),....,sin(w_{\frac{embed_{dim}}{2}-1}t),cos(w_{\frac{embed_{dim}}{2}-1}t)]
PE=[PE(t,0),...,PE(t,embeddim??1)]=[sin(w0?t),cos(w0?t),....,sin(w2embeddim???1?t),cos(w2embeddim???1?t)]
实际使用中可以不用交叉:即
P
E
=
[
P
E
(
t
,
0
)
,
.
.
.
,
P
E
(
t
,
e
m
b
e
d
d
i
m
?
1
)
]
=
[
s
i
n
(
w
0
t
)
,
s
i
n
(
w
e
m
b
e
d
d
i
m
2
?
1
t
)
,
.
.
.
.
,
c
o
s
(
w
0
t
)
,
c
o
s
(
w
e
m
b
e
d
d
i
m
2
?
1
t
)
]
PE=[PE(t,0),...,PE(t,embed_{dim}-1)]=[sin(w_0t),sin(w_{\frac{embed_{dim}}{2}-1}t),....,cos(w_0t),cos(w_{\frac{embed_{dim}}{2}-1}t)]
PE=[PE(t,0),...,PE(t,embeddim??1)]=[sin(w0?t),sin(w2embeddim???1?t),....,cos(w0?t),cos(w2embeddim???1?t)]
因为本质上cos(0)在位置1还是在位置embed_dim/2没有区别,都是可以通过矩阵变换得到
位置编码实现及其可视化代码,请移步手撕代码专栏的博客