赞
踩
Since our model contains no recurrence and no convolution, in order for the model to make use of the order of the sequence, we must inject some information about the relative or absolute position of the tokens in the sequence. To this end, we add “positional encodings” to the input embeddings at the bottoms of the encoder and decoder stacks. The positional encodings have the same dimension
d
model
d_\text{model}
dmodel as the embeddings, so that the two can be summed. There are many choices of positional encodings, learned and fixed [9].
由于我们的模型不包含循环和卷积,为了让模型利用序列的顺序,我们必须注入序列中关于 token
相对或者绝对位置的一些信息。为此,我们将位置编码 (positional encodings) 和 embeddings 相加,作为编码器和解码器堆栈底部的输入。位置编码和嵌入的维度
d
model
d_\text{model}
dmodel 相同,所以它们可以相加。有多种位置编码可以选择,例如通过学习得到的位置编码和固定的位置编码 [9]。
In this work, we use sine and cosine functions of different frequencies:
在这项工作中,我们使用不同频率的正弦和余弦函数:
P
E
(
p
o
s
,
2
i
)
=
sin
(
p
o
s
/
1000
0
2
i
/
d
model
)
P
E
(
p
o
s
,
2
i
+
1
)
=
cos
(
p
o
s
/
1000
0
2
i
/
d
model
)
where
p
o
s
pos
pos is the position and
i
i
i is the dimension. That is, each dimension of the positional encoding corresponds to a sinusoid. The wavelengths form a geometric progression from
2
π
2\pi
2π to
10000
⋅
2
π
10000 \cdot 2\pi
10000⋅2π. We chose this function because we hypothesized it would allow the model to easily learn to attend by relative positions, since for any fixed offset
k
k
k,
P
E
p
o
s
+
k
PE_{pos+k}
PEpos+k can be represented as a linear function of
P
E
p
o
s
PE_{pos}
PEpos.
其中
p
o
s
pos
pos 是位置,
i
i
i 是维度。也就是说,位置编码的每个维度对应于一个正弦曲线。这些波长形成一个几何级数,从
2
π
2\pi
2π 到
10000
⋅
2
π
10000 \cdot 2\pi
10000⋅2π。我们选择这个函数是因为我们假设它允许模型很容易学习对相对位置的关注,因为对任意确定的偏移
k
k
k,
P
E
p
o
s
+
k
PE_{pos+k}
PEpos+k 可以表示为
P
E
p
o
s
PE_{pos}
PEpos 的线性函数。
We also experimented with using learned positional embeddings [9] instead, and found that the two versions produced nearly identical results (see Table 3 row (E)). We chose the sinusoidal version because it may allow the model to extrapolate to sequence lengths longer than the ones encountered during training.
我们还尝试使用学习的位置嵌入 [9] 进行实验,发现这两个版本产生了几乎相同的结果 (see Table 3 row (E))。我们选择正弦曲线版本是因为它可以使模型外推到比训练过程中遇到的序列长度更长的序列长度。
sinusoidal [ˌsɪnə'sɔɪdl]:adj. 血窦,曲形,正弦,正弦曲线的
extrapolate [ɪk'stræpəleɪt]:v. 推断,推知,判定,推测
hypothesize [haɪ'pɒθəsaɪz]:v. 假设,假定
models/common_layers.py
https://github.com/gentaiscool/end2end-asr-pytorch/blob/master/models/common_layers.py
class PositionalEncoding(nn.Module): """ Positional Encoding class """ def __init__(self, dim_model, max_length=2000): super(PositionalEncoding, self).__init__() pe = torch.zeros(max_length, dim_model, requires_grad=False) position = torch.arange(0, max_length).unsqueeze(1).float() exp_term = torch.exp(torch.arange(0, dim_model, 2).float() * -(math.log(10000.0) / dim_model)) pe[:, 0::2] = torch.sin(position * exp_term) # take the odd (jump by 2) pe[:, 1::2] = torch.cos(position * exp_term) # take the even (jump by 2) pe = pe.unsqueeze(0) self.register_buffer('pe', pe) def forward(self, input): """ args: input: B x T x D output: tensor: B x T """ return self.pe[:, :input.size(1)]
不同于 CNN 和 RNN,Transformer 模型对于序列没有编码,导致无法获取每个词之间的关系,即无法构成有意义的语句。Positional Encoding 就是对序列中的词语出现的位置进行编码。如果对位置进行编码,我们的模型就可以捕捉顺序信息。
Transformer 模型的 attention 机制并没有包含位置信息,一句话中词语在不同的位置时在 Transformer 中是没有区别的。
使用正余弦函数实现位置编码不仅可以获取词的绝对位置信息,还可以获取相对位置信息。
P
E
(
p
o
s
,
2
i
)
=
sin
(
p
o
s
/
1000
0
2
i
/
d
model
)
P
E
(
p
o
s
,
2
i
+
1
)
=
cos
(
p
o
s
/
1000
0
2
i
/
d
model
)
p o s pos pos 是指词语在序列中的位置。在偶数位置,使用正弦编码。在奇数位置,使用余弦编码。 p o s pos pos 表示 token 在 sequence 中的位置,第一个 token 就是 0。 d model = 512 d_\text{model} = 512 dmodel=512, 2 i 2i 2i 和 2 i + 1 2i+1 2i+1 表示 Positional Encoding 的维度 ( i i i is the dimension)。
P E PE PE 为二维矩阵,大小跟输入 embedding 的维度一样。行表示 token,列表示词向量。 p o s pos pos 表示 token 在句子中的位置, d model = 512 d_\text{model} = 512 dmodel=512 表示词向量的维度, i i i 表示词向量的位置。在每个词语的词向量的偶数位置添加 s i n sin sin 变量,奇数位置添加 c o s cos cos 变量,以此来填满整个 P E PE PE 矩阵,然后加到 input embedding 中去,这样便完成位置编码的引入了。
当
p
o
s
pos
pos 为
1
1
1,
d
model
=
512
d_\text{model} = 512
dmodel=512 时,对应的 Positional Encoding 可以写成:
P
E
(
1
,
2
i
)
=
sin
(
1
/
1000
0
2
i
/
512
)
P
E
(
1
,
2
i
+
1
)
=
cos
(
1
/
1000
0
2
i
/
512
)
P
E
(
1
)
=
[
sin
(
1
/
1000
0
0
/
512
)
,
cos
(
1
/
1000
0
1
/
512
)
,
sin
(
1
/
1000
0
2
/
512
)
,
cos
(
1
/
1000
0
3
/
512
)
.
.
.
]
1
/
512
=
0.001953125
1/512 = 0.001953125
1/512=0.001953125
1000
0
1
/
512
=
1.01815
10000^{1/512} = 1.01815
100001/512=1.01815
10000
0
1
/
512
=
1.02274
100000^{1/512} = 1.02274
1000001/512=1.02274
相对位置信息通过以下公式实现:
sin
(
α
+
β
)
=
sin
α
cos
β
+
cos
α
sin
β
cos
(
α
+
β
)
=
cos
α
cos
β
−
sin
α
sin
β
因为对任意确定的偏移 k k k (词汇之间的位置偏移), P E p o s + k PE_{pos+k} PEpos+k 可以表示为 P E p o s PE_{pos} PEpos 的线性函数。 P E ( p o s + k ) PE{(pos+k)} PE(pos+k) 可以表示为 P E ( p o s ) PE{(pos)} PE(pos) 和 P E ( k ) PE{(k)} PE(k) 的组合形式,这就是表达相对位置的能力。
这表明位置 p + k p+k p+k 的向量可以表示成位置 p p p 的向量的线性变换,这提供了表达相对位置信息的可能性。
Transformer 利用 Positional Encoding 来获取词的绝对位置信息和相对位置信息。如果没有位置信息将变成 Bag of Words (BoW) 模型。
为了使模型能够利用序列的顺序,需要插入 token 在序列中相对或绝对位置的信息。Positional Encoding 和 token embedding 相加,作为 encoder 和 decoder 堆栈底部输入。Positional Encoding 和 embedding 具有同样的维度 d model d_\text{model} dmodel,这两者可以直接相加。
onmt/modules/embeddings.py
https://github.com/OpenNMT/OpenNMT-py/blob/master/onmt/modules/embeddings.py
class PositionalEncoding(nn.Module): """Sinusoidal positional encoding for non-recurrent neural networks. Implementation based on "Attention Is All You Need" :cite:`DBLP:journals/corr/VaswaniSPUJGKP17` Args: dropout (float): dropout parameter dim (int): embedding size """ def __init__(self, dropout, dim, max_len=5000): if dim % 2 != 0: raise ValueError("Cannot use sin/cos positional encoding with " "odd dim (got dim={:d})".format(dim)) pe = torch.zeros(max_len, dim) position = torch.arange(0, max_len).unsqueeze(1) div_term = torch.exp((torch.arange(0, dim, 2, dtype=torch.float) * -(math.log(10000.0) / dim))) pe[:, 0::2] = torch.sin(position.float() * div_term) pe[:, 1::2] = torch.cos(position.float() * div_term) pe = pe.unsqueeze(1) super(PositionalEncoding, self).__init__() self.register_buffer('pe', pe) self.dropout = nn.Dropout(p=dropout) self.dim = dim def forward(self, emb, step=None): """Embed inputs. Args: emb (FloatTensor): Sequence of word vectors ``(seq_len, batch_size, self.dim)`` step (int or NoneType): If stepwise (``seq_len = 1``), use the encoding for this position. """ emb = emb * math.sqrt(self.dim) if step is None: emb = emb + self.pe[:emb.size(0)] else: emb = emb + self.pe[step] emb = self.dropout(emb) return emb
exp_term = torch.exp(torch.arange(0, dim_model, 2).float() * -(math.log(10000.0) / dim_model))
div_term = torch.exp((torch.arange(0, dim, 2, dtype=torch.float) * -(math.log(10000.0) / dim)))
以 10 为底的对数叫做常用对数 (common logarithm),并记为 lg。
以无理数 e (e=2.71828…) 为底的对数称为自然对数 (natural logarithm),并记为 ln。
零没有对数。
在实数范围内,负数无对数。在虚数范围内,负数是有对数的。
ln
x
=
log
e
x
\text{ln} x = \text{log}_{e}^{x}
lnx=logex
e
x
p
(
x
)
=
e
x
exp(x) = {e}^{x}
exp(x)=ex
a
x
=
e
ln
a
x
=
e
log
e
a
x
=
e
x
log
e
a
a^{x} = e^{\text{ln} a^{x}} = e^{\text{log}_{e} a^{x}} = e^{x \text{log}_{e} a}
ax=elnax=elogeax=exlogea
1
/
1000
0
2
i
/
d
model
=
e
log
e
1
/
1000
0
2
i
/
d
model
=
e
log
e
1000
0
−
2
i
/
d
model
=
e
(
−
2
i
/
d
model
)
log
e
10000
=
e
2
i
∗
(
(
−
log
e
10000
)
/
d
model
)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。