Positional Encoding - 位置編碼
1. Positional Encoding
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 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 相加,作爲編碼器和解碼器堆棧底部的輸入。位置編碼和嵌入的維度 相同,所以它們可以相加。有多種位置編碼可以選擇,例如通過學習得到的位置編碼和固定的位置編碼 [9]。
In this work, we use sine and cosine functions of different frequencies:
在這項工作中,我們使用不同頻率的正弦和餘弦函數:
where is the position and is the dimension. That is, each dimension of the positional encoding corresponds to a sinusoid. The wavelengths form a geometric progression from to . 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 , can be represented as a linear function of .
其中 是位置, 是維度。也就是說,位置編碼的每個維度對應於一個正弦曲線。這些波長形成一個幾何級數,從 到 。我們選擇這個函數是因爲我們假設它允許模型很容易學習對相對位置的關注,因爲對任意確定的偏移 , 可以表示爲 的線性函數。
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. 假設,假定
2. End-to-End Speech Recognition on Pytorch
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 中是沒有區別的。
使用正餘弦函數實現位置編碼不僅可以獲取詞的絕對位置信息,還可以獲取相對位置信息。
是指詞語在序列中的位置。在偶數位置,使用正弦編碼。在奇數位置,使用餘弦編碼。 表示 token 在 sequence 中的位置,第一個 token 就是 0。, 和 表示 Positional Encoding 的維度 ( is the dimension)。
爲二維矩陣,大小跟輸入 embedding 的維度一樣。行表示 token,列表示詞向量。 表示 token 在句子中的位置, 表示詞向量的維度, 表示詞向量的位置。在每個詞語的詞向量的偶數位置添加 變量,奇數位置添加 變量,以此來填滿整個 矩陣,然後加到 input embedding 中去,這樣便完成位置編碼的引入了。
當 爲 , 時,對應的 Positional Encoding 可以寫成:
相對位置信息通過以下公式實現:
因爲對任意確定的偏移 (詞彙之間的位置偏移), 可以表示爲 的線性函數。 可以表示爲 和 的組合形式,這就是表達相對位置的能力。
這表明位置 的向量可以表示成位置 的向量的線性變換,這提供了表達相對位置信息的可能性。
Transformer 利用 Positional Encoding 來獲取詞的絕對位置信息和相對位置信息。如果沒有位置信息將變成 Bag of Words (BoW) 模型。
爲了使模型能夠利用序列的順序,需要插入 token 在序列中相對或絕對位置的信息。Positional Encoding 和 token embedding 相加,作爲 encoder 和 decoder 堆棧底部輸入。Positional Encoding 和 embedding 具有同樣的維度 ,這兩者可以直接相加。
3. OpenNMT-py: Open-Source Neural Machine Translation
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。
零沒有對數。
在實數範圍內,負數無對數。在虛數範圍內,負數是有對數的。