Transformer实现以及Pytorch源码解读(三)-位置编码Position Encoding——史上最容易理解

位置处理

在进行文本数据处理的时候需要融入嵌入信息的位置信息。目前融入位置信息的方法有两种:位置嵌入(Position Embediings)与位置编码 (Position Encodings)。位置嵌入与我们在(二)中讲解的类似,即为每个位置信息加入一个随机化的多维向量进行区分,弊端也是显而易见的,他们只能区别各自的不同,但是无法表示位置之间的关系,特别是欧氏空间上的关系。二位置编码则能够解决以上的问题。

Position Encodings定义

位置编码的数学定义如下公式所示:
Transformer实现以及Pytorch源码解读(三)-位置编码Position Encoding——史上最容易理解_第1张图片
从公式上可以看出,在对位置信息进行编码的过程中主要涉及到两个索引和一个常量:索引p和i,其中,p表示,某个单次在一个句子中的位置,该位置用索引表示,索引从0开始;其中的i表示,某个数值,在embedding后形成的向量中的位置,该位置也用索引表示,并且索引从0开始。可以搭配下图进行理解。比如0.03这个元素的p=0并且i=0, 而0.07这个元素的p=1,i=1。d是一个常量,表示embedding后每个单词向量的维度。
Transformer实现以及Pytorch源码解读(三)-位置编码Position Encoding——史上最容易理解_第2张图片
从inputs到向量矩阵的表示过程可以参考我前面的博客。

程序实现

通过前面的分析,进行位置编码的过程就很清晰了,我们只需要产生一个位置矩阵就行,这个位置矩阵的行跟句子的长度一致,这个矩阵的列与单次向量化的维度一致。代码如下:

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len=512):
        #d_model是每个词embedding后的维度
        super(PositionalEncoding, self).__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term2 = torch.pow(torch.tensor(10000.0),torch.arange(0, d_model, 2).float()/d_model)
        div_term1 = torch.pow(torch.tensor(10000.0),torch.arange(1, d_model, 2).float()/d_model)
        #高级切片方式,即从0开始,两个步长取一个。即奇数和偶数位置赋值不一样。直观来看就是每一句话的
        pe[:, 0::2] = torch.sin(position * div_term2)
        pe[:, 1::2] = torch.cos(position * div_term1)
        #这里是为了与x的维度保持一致,释放了一个维度
        pe = pe.unsqueeze(0).transpose(0, 1)
        self.register_buffer('pe', pe)
    def forward(self, x):
        x = x + self.pe[:x.size(0), :]
        return x

总结

Position Encoding的本质是为embedding后的每个数值(这个数值指的是向量中的每个数值)给定一个编码。以前理解的误区:一直以为只是给每个单次在句子中的位置进行了编码。恕在下直言,网上大部分把这块讲的云山雾绕,属实难懂。

你可能感兴趣的:(Pytorch,算法,pytorch,transformer,深度学习)