0
点赞
收藏
分享

微信扫一扫

Transformer中的Position Encoding

小暴龙要抱抱 2022-03-15 阅读 157

主要记录一下Transformer中的Position Encoding,一些理解来自下面大佬的回答。

这里https://www.zhihu.com/question/347678607/answer/864217252

Transformer中的Position Encodeing:

思考:

首先一点,模型中引入位置信息是有必要的,在NLP领域,词序乃至句序稍微的发生改变,整个含义就会发生改变,这种改变就应该体现在最终的Embedding上。在意识到位置信息(不论是相对位置还是绝对位置)的重要性后,就需要考虑如何将位置信息引入,即加入位置编码。一种最简单的方式就是计数,即使用:

PE=pos=0,1,2,\cdots ,T-1

当作文章中每个字的位置编码。这样做是存在问题的,整个序列没有上界。设想一段很长的(比如含有5000个字的)文本,最后一个字的位置编码非常大,会使得位置编码在于字本身嵌入合并之后数值上过大,出现合并后整体上的数值倾斜。另外就是位置编码过大可能导致与字本身嵌入合并之后的表示受位置编码的主导,对模型的结果产生干扰。

从上面的例子可以知道位置编码最好具有一定的值域范围,这样就有了新的想法:使用文本长度对每个位置作归一化,即:

PE=pos/(T-1)

这样固然使得所有位置编码都落入[0,1]的区间内,但是问题也是显著的:在较短的文本中紧紧相邻的两个字的位置编码差异会和长文本中间隔很远的两个字的位置编码差异一致。这显然是不合适的,我们关注的位置信息,最核心的就是相对次序关系,尤其是上下文中的次序关系,如果使用这种方法,那么在长文本中相对次序关系会被「稀释」,例如:

代码实现:

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len=512):
        super(PositionalEncoding, self).__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))#不论分段函数的哪一步,2*i都是偶数
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        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

其中 self.register_buffer('pe', pe)中的 register_buffer()函数是Pytorch的一个函数,这条代码的作用是将pe这个tensor对象注册到模型的 buffers() 属性中,并命名为'pe',在buffers()中的对象不会有梯度回传,但是模型会将这部分值存在state_dict中,相当于通常代码中的常量。在写法上其实等同于self.pe=pe。

举报

相关推荐

0 条评论