Positional Encoding - 位置編碼

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 dmodeld_\text{model} 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 相加,作爲編碼器和解碼器堆棧底部的輸入。位置編碼和嵌入的維度 dmodeld_\text{model} 相同,所以它們可以相加。有多種位置編碼可以選擇,例如通過學習得到的位置編碼和固定的位置編碼 [9]。

In this work, we use sine and cosine functions of different frequencies:
在這項工作中,我們使用不同頻率的正弦和餘弦函數:

PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel) \begin{aligned} PE(pos, 2i) = \sin(pos/10000^{2i/d_\text{model}}) \\ PE(pos, 2i+1) = \cos(pos/10000^{2i/d_\text{model}}) \\ \end{aligned}

where pospos is the position and ii 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 to 100002π10000 \cdot 2\pi. 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 kk, PEpos+kPE_{pos+k} can be represented as a linear function of PEposPE_{pos}.
其中 pospos 是位置,ii 是維度。也就是說,位置編碼的每個維度對應於一個正弦曲線。這些波長形成一個幾何級數,從 2π2\pi100002π10000 \cdot 2\pi。我們選擇這個函數是因爲我們假設它允許模型很容易學習對相對位置的關注,因爲對任意確定的偏移 kk, PEpos+kPE_{pos+k} 可以表示爲 PEposPE_{pos} 的線性函數。

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 中是沒有區別的。

使用正餘弦函數實現位置編碼不僅可以獲取詞的絕對位置信息,還可以獲取相對位置信息。

PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel) \begin{aligned} PE(pos, 2i) = \sin(pos/10000^{2i/d_\text{model}}) \\ PE(pos, 2i+1) = \cos(pos/10000^{2i/d_\text{model}}) \\ \end{aligned}

pospos 是指詞語在序列中的位置。在偶數位置,使用正弦編碼。在奇數位置,使用餘弦編碼。pospos 表示 token 在 sequence 中的位置,第一個 token 就是 0。dmodel=512d_\text{model} = 5122i2i2i+12i+1 表示 Positional Encoding 的維度 (ii is the dimension)。

PEPE 爲二維矩陣,大小跟輸入 embedding 的維度一樣。行表示 token,列表示詞向量。pospos 表示 token 在句子中的位置,dmodel=512d_\text{model} = 512 表示詞向量的維度,ii 表示詞向量的位置。在每個詞語的詞向量的偶數位置添加 sinsin 變量,奇數位置添加 coscos 變量,以此來填滿整個 PEPE 矩陣,然後加到 input embedding 中去,這樣便完成位置編碼的引入了。

pospos11dmodel=512d_\text{model} = 512 時,對應的 Positional Encoding 可以寫成:
PE(1,2i)=sin(1/100002i/512)PE(1,2i+1)=cos(1/100002i/512) \begin{aligned} PE(1, 2i) = \sin(1/10000^{2i/512}) \\ PE(1, 2i+1) = \cos(1/10000^{2i/512}) \\ \end{aligned}

PE(1)=[sin(1/100000/512),cos(1/100001/512),sin(1/100002/512),cos(1/100003/512)...] \begin{aligned} PE(1) = [\sin(1/10000^{0/512}), \cos(1/10000^{1/512}), \sin(1/10000^{2/512}), \cos(1/10000^{3/512})... ] \end{aligned}

1/512=0.0019531251/512 = 0.001953125
100001/512=1.0181510000^{1/512} = 1.01815
1000001/512=1.02274100000^{1/512} = 1.02274

相對位置信息通過以下公式實現:

sin(α+β)=sinαcosβ+cosαsinβcos(α+β)=cosαcosβsinαsinβ \begin{aligned} \sin(\alpha + \beta) = \sin \alpha \cos \beta + \cos \alpha \sin \beta \\ \cos(\alpha + \beta) = \cos \alpha \cos \beta - \sin \alpha \sin \beta \end{aligned}

因爲對任意確定的偏移 kk (詞彙之間的位置偏移), PEpos+kPE_{pos+k} 可以表示爲 PEposPE_{pos} 的線性函數。PE(pos+k)PE{(pos+k)} 可以表示爲 PE(pos)PE{(pos)}PE(k)PE{(k)} 的組合形式,這就是表達相對位置的能力。

這表明位置 p+kp+k 的向量可以表示成位置 pp 的向量的線性變換,這提供了表達相對位置信息的可能性。

Transformer 利用 Positional Encoding 來獲取詞的絕對位置信息和相對位置信息。如果沒有位置信息將變成 Bag of Words (BoW) 模型。

爲了使模型能夠利用序列的順序,需要插入 token 在序列中相對或絕對位置的信息。Positional Encoding 和 token embedding 相加,作爲 encoder 和 decoder 堆棧底部輸入。Positional Encoding 和 embedding 具有同樣的維度 dmodeld_\text{model},這兩者可以直接相加。

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。
零沒有對數。
在實數範圍內,負數無對數。在虛數範圍內,負數是有對數的。
lnx=logex\text{ln} x = \text{log}_{e}^{x}
exp(x)=exexp(x) = {e}^{x}
ax=elnax=elogeax=exlogeaa^{x} = e^{\text{ln} a^{x}} = e^{\text{log}_{e} a^{x}} = e^{x \text{log}_{e} a}

1/100002i/dmodel=eloge1/100002i/dmodel=eloge100002i/dmodel=e(2i/dmodel)loge10000=e2i((loge10000)/dmodel) \begin{aligned} 1/10000^{2i/d_\text{model}} &= e^{\text{log}_{e}^{1/10000^{2i/d_\text{model}}}} \\ &= e^{\text{log}_{e}^{10000^{-2i/d_\text{model}}}} \\ &= e^{({-2i/d_\text{model}})\text{log}_{e}^{10000}} \\ &= e^{{2i}*((-\text{log}_{e}^{10000})/d_\text{model})} \\ \end{aligned}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章