transformer模型是当前大红大热的语言模型,今天要讲解的是transformer中的positional encoding(位置编码).
我们知道,transformer模型的attention机制并没有包含位置信息,即一句话中词语在不同的位置时在transformer中是没有区别的,这当然是不符合实际的。因此,在transformer中引入位置信息相比CNN, RNN等模型有更加重要的作用。
作者添加位置编码的方法是:构造一个跟输入embedding维度一样的矩阵,然后跟输入embedding相加得到multi-head attention 的输入。
在paper中,作者使用的positional encoding如下:
在这里插入图片描述
其中,PE为二维矩阵,大小跟输入embedding的维度一样,行表示词语,列表示词向量;pos 表示词语在句子中的位置; d m o d e l d_{model} dmodel表示词向量的维度;i表示词向量的位置。因此,上述公式表示在每个词语的词向量的偶数位置添加sin变量,奇数位置添加cos变量,以此来填满整个PE矩阵,然后加到input embedding中去,这样便完成位置编码的引入了。
使用sin编码和cos编码的原因是可以得到词语之间的相对位置,因为:
s i n ( α + β ) = s i n α c o s β + c o s α s i n β sin(\alpha+\beta)= sin\alpha cos\beta + cos\alpha sin\beta sin(α+β)=sinαcosβ+cosαsinβ
c o s ( α + β ) = c o s α c o s β − s i n α s i n β cos(\alpha+\beta)= cos\alpha cos\beta - sin\alpha sin\beta cos(α+β)=cosαcosβsinαsinβ
即由 s i n ( p o s + k ) sin(pos+k) sin(pos+k)可以得到,通过线性变换获取后续词语相对当前词语的位置关系。
positional encoding 的源代码如下:

class PositionalEncoding(nn.Module):
    "Implement the PE function."

    def __init__(self, d_model, dropout, max_len=5000):
        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)
        position = torch.arange(0, max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2) *
                             -(math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)

    def forward(self, x):
        x = x + Variable(self.pe[:, :x.size(1)],
                         requires_grad=False)
        return self.dropout(x)

其中,div_term是上述公式经过简单的数学变换得到的,具体如下:
1 / 1000 0 2 i / d m o d e l = e l o g 1000 0 − 2 i / d m o d e l = e − 2 i / d m o d e l ∗ l o g 10000 = e 2 i ∗ ( − l o g 10000 / d m o d e l ) 1/10000^{2i/d_{model}}=e^{log^{10000^{-2i/d_{model}}}}=e^{{-2i/d_{model}}*{log^{10000}}}=e^{{2i}*(-log^{10000}/d_{model})} 1/100002i/dmodel=elog100002i/dmodel=e2i/dmodellog10000=e2i(log10000/dmodel)
我在执行过程中,出现了如下错误:
RuntimeError: “exp” not implemented for ‘torch.LongTensor’
估计原因是pytorch的版本问题,解决方法为将torch.arange(0, d_model, 2)中的整型改为浮点型,即torch.arange(0.0, d_model, 2)
在这里插入图片描述

添加于2020.10.22:
我在实验中尝试将position_embeddings这个向量注释掉,重新跑一遍生成模型,效果下降很明显,具体表现为容易生成重复的字/词。所以,这个位置编码相对于transformer来说是非常重要的。

参考:
attention is all you need
自然语言处理N天-使用Pytorch实现Transformer第一节
RuntimeError: “exp” not implemented for ‘torch.LongTensor’

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐