transformer中的位置嵌入pytorch代碼


class PositionalEncoding(nn.Module): 
  "Implement the PE function." 
  def __init__(self, d_model, dropout, max_len=5000): 
    #d_model=512,dropout=0.1,
    #max_len=5000代表事先准備好長度為5000的序列的位置編碼,其實沒必要,
    #一般100或者200足夠了。
    super(PositionalEncoding, self).__init__() 
    self.dropout = nn.Dropout(p=dropout) 

    # Compute the positional encodings once in log space. 
    pe = torch.zeros(max_len, d_model) 
    #(5000,512)矩陣,保持每個位置的位置編碼,一共5000個位置,
    #每個位置用一個512維度向量來表示其位置編碼
    position = torch.arange(0, max_len).unsqueeze(1) 
    # (5000) -> (5000,1)
    div_term = torch.exp(torch.arange(0, d_model, 2) * 
      -(math.log(10000.0) / d_model)) 
      # (0,2,…, 4998)一共准備2500個值,供sin, cos調用
    pe[:, 0::2] = torch.sin(position * div_term) # 偶數下標的位置
    pe[:, 1::2] = torch.cos(position * div_term) # 奇數下標的位置
    pe = pe.unsqueeze(0) 
    # (5000, 512) -> (1, 5000, 512) 為batch.size留出位置
    self.register_buffer('pe', pe) 
  def forward(self, x): 
    x = x + Variable(self.pe[:, :x.size(1)], requires_grad=False) 
    # 接受1.Embeddings的詞嵌入結果x,
    #然后把自己的位置編碼pe,封裝成torch的Variable(不需要梯度),加上去。
    #例如,假設x是(30,10,512)的一個tensor,
    #30是batch.size, 10是該batch的序列長度, 512是每個詞的詞嵌入向量;
    #則該行代碼的第二項是(1, min(10, 5000), 512)=(1,10,512),
    #在具體相加的時候,會擴展(1,10,512)為(30,10,512),
    #保證一個batch中的30個序列,都使用(疊加)一樣的位置編碼。
    return self.dropout(x) # 增加一次dropout操作
# 注意,位置編碼不會更新,是寫死的,所以這個class里面沒有可訓練的參數。

注意,位置編碼不會更新,是寫死的,所以這個class里面沒有可訓練的參數。

為了計算這個公式,上面的代碼寫的比較風騷,以2i為偶數為例子:

將字編碼和位置嵌入聯合使用:

nn.Sequential(Embeddings(d_model,src_vocab),
  PositionalEncoding(d_model,dropout)) 
# 例如,d_model=512, src_vocab=源語言的詞表大小, 
#dropout=0.1即 dropout rate

摘自:https://zhuanlan.zhihu.com/p/107889011

補充另外一種實現:

import torch
import torch.nn as nn
import numpy as np


class PositionalEncoding(nn.Module):
    
    def __init__(self, d_model, max_seq_len):
        """初始化。
        
        Args:
            d_model: 一個標量。模型的維度,論文默認是512
            max_seq_len: 一個標量。文本序列的最大長度
        """
        super(PositionalEncoding, self).__init__()
        
        # 根據論文給的公式,構造出PE矩陣
        position_encoding = np.array([
          [pos / np.power(10000, 2.0 * (j // 2) / d_model) for j in range(d_model)]
          for pos in range(max_seq_len)])
        # 偶數列使用sin,奇數列使用cos
        position_encoding[:, 0::2] = np.sin(position_encoding[:, 0::2])
        position_encoding[:, 1::2] = np.cos(position_encoding[:, 1::2])
        position_encoding = torch.from_numpy(position_encoding)

        # 在PE矩陣的第一行,加上一行全是0的向量,代表這`PAD`的positional encoding
        # 在word embedding中也經常會加上`UNK`,代表位置單詞的word embedding,兩者十分類似
        # 那么為什么需要這個額外的PAD的編碼呢?很簡單,因為文本序列的長度不一,我們需要對齊,
        # 短的序列我們使用0在結尾補全,我們也需要這些補全位置的編碼,也就是`PAD`對應的位置編碼
        pad_row = torch.zeros([1, d_model])
        position_encoding = torch.cat((pad_row, position_encoding))
        print(position_encoding)
        # 嵌入操作,+1是因為增加了`PAD`這個補全位置的編碼,
        # Word embedding中如果詞典增加`UNK`,我們也需要+1。看吧,兩者十分相似
        self.position_encoding = nn.Embedding(max_seq_len + 1, d_model)
        self.position_encoding.weight = nn.Parameter(position_encoding,requires_grad=False)
    def forward(self, input_len):
        """神經網絡的前向傳播。

        Args:
          input_len: 一個張量,形狀為[BATCH_SIZE, 1]。每一個張量的值代表這一批文本序列中對應的長度。

        Returns:
          返回這一批序列的位置編碼,進行了對齊。
        """
        
        # 找出這一批序列的最大長度
        max_len = torch.max(input_len)
        tensor = torch.cuda.LongTensor if input_len.is_cuda else torch.LongTensor
        # 對每一個序列的位置進行對齊,在原序列位置的后面補上0
        # 這里range從1開始也是因為要避開PAD(0)的位置
        input_pos = tensor(
          [list(range(1, len + 1)) + [0] * (max_len - len) for len in input_len])
        return self.position_encoding(input_pos)

pe = PositionalEncoding(10, 5)
input_lens = torch.tensor([[4],[3],[2]], dtype=torch.long)
pe(input_lens)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM