Transformer模型中位置编码的实现细节?
Implementation details of positional encoding in transformer model?
这个位置编码究竟是如何计算的?
让我们假设一个机器翻译场景,这些是输入句子,
english_text = [this is good, this is bad]
german_text = [das ist gut, das ist schlecht]
现在我们的输入词汇量为 4,嵌入维度为 4。
#words #embeddings
this - [0.5, 0.2, 0.3, 0.1]
is - [0.1, 0.2, 0.5, 0.1]
good - [0.9, 0.7, 0.9, 0.1]
bad - [0.7, 0.3, 0.4, 0.1]
根据 transformer 论文,我们添加 每个词位置编码 和 每个词嵌入 ,然后将其传递给编码器,如在下图,
就论文而言,他们给出了计算每个单词位置编码的公式,
所以,这就是我认为我可以实现的方式,
d_model = 4 # Embedding dimension
positional_embeddings = np.zeros((max_sentence_length, d_model))
max_sentence_length = 3 # as per my examples above
for position in range(maximum_sentence_length):
for i in range(0, d_model, 2):
positional_embeddings[position, i] = (
sin(position / (10000 ** ( (2*i) / d_model) ) )
)
positional_embeddings[position, i + 1] = (
cos(position / (10000 ** ( (2 * (i + 1) ) / d_model) ) )
)
那么,新的嵌入向量将是
[[0.5, 0.2, 0.3, 0.1],
[0.1, 0.2, 0.5, 0.1],
[0.9, 0.7, 0.9, 0.1]] + positional_embeddings = NEW EMBEDDINGS
## shapes
3 x 4 + 3 x 4 = 3 x 4
请问执行时是这样计算的吗?如果我上面的伪实现有任何错误,请纠正我。
如果一切正确,那么我有三个疑惑希望有人能解惑,
1) 从上面的实现中,我们对偶数位置使用 sin 公式,对奇数位置使用 cos 公式,但我无法理解其背后的原因?我读到它正在使用循环属性,但无法理解。
2) 在公式中选择 10000/(2i/d)
或 10000/(2i+1/d)
作为比例因子是否有原因。
3) 所有句子都不会等于最大句子长度,所以我们可能需要填充句子,所以我们也计算填充标记的位置编码。
你的实现基本正确。典型的实现是预先计算嵌入矩阵,制作一个不可训练的嵌入层,然后对一个范围进行嵌入查找。参见例如HuggingFace's Transformers.
中的实施
有关方程式背后的直觉的一些提示在这些线程中:
但在我看来,几乎所有关于位置编码的决定都是经验选择。
通过循环属性,恕我直言,它们意味着给定嵌入的维度,无论序列中的位置如何,具有恒定偏移量的位置之间的嵌入值的差异是相同的。为此,仅使用正弦或余弦可能就足够了,但某些位置的范数会比其他位置大得多,因此它们交替使用正弦和余弦。
我认为比例因子是根据经验估计的,以涵盖通常的句子长度。
使用填充,您确实还考虑了填充位置的位置编码,但由于它们是预先计算的,这确实意味着更高的计算负载,因为无论如何您都会获得填充符号的嵌入。
这个位置编码究竟是如何计算的?
让我们假设一个机器翻译场景,这些是输入句子,
english_text = [this is good, this is bad]
german_text = [das ist gut, das ist schlecht]
现在我们的输入词汇量为 4,嵌入维度为 4。
#words #embeddings
this - [0.5, 0.2, 0.3, 0.1]
is - [0.1, 0.2, 0.5, 0.1]
good - [0.9, 0.7, 0.9, 0.1]
bad - [0.7, 0.3, 0.4, 0.1]
根据 transformer 论文,我们添加 每个词位置编码 和 每个词嵌入 ,然后将其传递给编码器,如在下图,
就论文而言,他们给出了计算每个单词位置编码的公式,
所以,这就是我认为我可以实现的方式,
d_model = 4 # Embedding dimension
positional_embeddings = np.zeros((max_sentence_length, d_model))
max_sentence_length = 3 # as per my examples above
for position in range(maximum_sentence_length):
for i in range(0, d_model, 2):
positional_embeddings[position, i] = (
sin(position / (10000 ** ( (2*i) / d_model) ) )
)
positional_embeddings[position, i + 1] = (
cos(position / (10000 ** ( (2 * (i + 1) ) / d_model) ) )
)
那么,新的嵌入向量将是
[[0.5, 0.2, 0.3, 0.1],
[0.1, 0.2, 0.5, 0.1],
[0.9, 0.7, 0.9, 0.1]] + positional_embeddings = NEW EMBEDDINGS
## shapes
3 x 4 + 3 x 4 = 3 x 4
请问执行时是这样计算的吗?如果我上面的伪实现有任何错误,请纠正我。
如果一切正确,那么我有三个疑惑希望有人能解惑,
1) 从上面的实现中,我们对偶数位置使用 sin 公式,对奇数位置使用 cos 公式,但我无法理解其背后的原因?我读到它正在使用循环属性,但无法理解。
2) 在公式中选择 10000/(2i/d)
或 10000/(2i+1/d)
作为比例因子是否有原因。
3) 所有句子都不会等于最大句子长度,所以我们可能需要填充句子,所以我们也计算填充标记的位置编码。
你的实现基本正确。典型的实现是预先计算嵌入矩阵,制作一个不可训练的嵌入层,然后对一个范围进行嵌入查找。参见例如HuggingFace's Transformers.
中的实施有关方程式背后的直觉的一些提示在这些线程中:
但在我看来,几乎所有关于位置编码的决定都是经验选择。
通过循环属性,恕我直言,它们意味着给定嵌入的维度,无论序列中的位置如何,具有恒定偏移量的位置之间的嵌入值的差异是相同的。为此,仅使用正弦或余弦可能就足够了,但某些位置的范数会比其他位置大得多,因此它们交替使用正弦和余弦。
我认为比例因子是根据经验估计的,以涵盖通常的句子长度。
使用填充,您确实还考虑了填充位置的位置编码,但由于它们是预先计算的,这确实意味着更高的计算负载,因为无论如何您都会获得填充符号的嵌入。