赞
踩
在进行文本数据处理的时候需要融入嵌入信息的位置信息。目前融入位置信息的方法有两种:位置嵌入(Position Embediings)与位置编码 (Position Encodings)。位置嵌入与我们在Transformer实现以及Pytorch源码解读(二)-embedding源码分析中讲解的类似,即为每个位置信息加入一个随机化的多维向量进行区分,弊端也是显而易见的,他们只能区别各自的不同,但是无法表示位置之间的关系,特别是欧氏空间上的关系。二位置编码则能够解决以上的问题。
位置编码的数学定义如下公式所示:
从公式上可以看出,在对位置信息进行编码的过程中主要涉及到两个索引和一个常量:索引p和i,其中,p表示,某个单次在一个句子中的位置,该位置用索引表示,索引从0开始;其中的i表示,某个数值,在embedding后形成的向量中的位置,该位置也用索引表示,并且索引从0开始。可以搭配下图进行理解。比如0.03这个元素的p=0并且i=0, 而0.07这个元素的p=1,i=1。d是一个常量,表示embedding后每个单词向量的维度。
从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后的每个数值(这个数值指的是向量中的每个数值)给定一个编码。以前理解的误区:一直以为只是给每个单次在句子中的位置进行了编码。恕在下直言,网上大部分把这块讲的云山雾绕,属实难懂。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。