在RNN里,句子被分成一个个单词依次送入网络,这样自带句子的输入自带文本顺序。但是Transformer结构将所有位置的单词一起输入网络,这样就会丧失文本的顺序。所以需要Position Embedding 来解决这个问题。
首先会想到的是给不同位置的单词赋予一个[0,1]之间的值。0代表开头的单词,1 代表结尾的单词。但是由于我们无法提前预知下一段话有多少单词,所以相邻单词位置编码数值的差值会随着句子长度的变化而变化。
另外一种想法是线性地为每个位置的单词安排编码值。即第一个单词为1,第二个单词为2,以此类推。这样做的问题有:
- 编码值可能会变得很长
- 测试集里的句子可能出现训练集里没出现过的长度。
这都会影响网络的泛化性。因此位置编码需要满足以下准则。
- 每个位置的编码值应该是唯一的。
- 相邻位置的编码值的差应该是保持不变的,无论句子有多长。
- 编码方法要能轻松泛化到更长的句子。
- 编码方式必须是确定的。
Transformer中的编码方法
Transformer中的Position-Embedding,满足上述所有准则。首先每个位置的编码不是一个数而是一个d-维向量,令 t t t表示输入句子里想要编码的位置。 p t → ∈ R d \overrightarrow{p_{t}} \in \mathbb{R}^{d} pt∈Rd表示该位置的编码向量。 p t → ( i ) = f ( t ) ( i ) : = { sin ( ω k ⋅ t ) , if i = 2 k cos ( ω k ⋅ t ) , if i = 2 k + 1 \overrightarrow{p_{t}}^{(i)}=f(t)^{(i)}:=\left\{\begin{array}{ll} \sin \left(\omega_{k} \cdot t\right), & \text { if } i=2 k \\ \cos \left(\omega_{k} \cdot t\right), & \text { if } i=2 k+1 \end{array}\right. pt(i)=f(t)(i):={