Transformer P5 位置编码层直观版代码实现
跟传统的 RNN 序列模型不同,在 Transformer 编码结构中,并没有针对词汇位置信息的处理,因为纯粹的 Attention 模块是无法捕捉输入顺序的,因此需要在 Embedding 层后加入位置编码器,将词汇位置不同,可能会产生不同语义信息,加入到词嵌入张量中。
Transformer 采用的是正余弦的绝对位置编码,这种编码方式可以保证,不同位置在所有维度上不会被编码到完全一样的值,从而使每个位置都获得独一无二的编码。通俗理解,就是将位置信息编码成向量后,每个位置对应的向量都是不同的。
公式解读
对于一个位置来说,pos 是固定的,d_model 也是常量,变化的就是 2i 值。
x 0 1 2 3 4 5 6 7 sin 0 2 4 6 cos 0 2 4 6 (x//2)*2 0 0 2 2 4 4 6 6
代码示例
1、封装位置编码层
这个项目中,大家要建立层的意识。位置编码层,是接受上一层(文本嵌入层)的结果,加上位置编码之后,再输出给下一层。
class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len=5000): super().__init__() def forward(self, x): pass
2、计算位置编码值
self.pe = torch.zeros(max_len, d_model) for pos in range(max_len): for j in range(d_model): angle = pos / math.pow(10000, (j//2)*2 / d_model) if j % 2 == 0: self.pe[pos][j] = math.sin(angle) else: self.pe[pos][j] = math.cos(angle)
3、词向量和位置编码相加
def forward(self, x): return x + self.pe[:x.size(1)] pe = PositionalEncoding(8) output2 = pe(output1) print(output2)
4、加入Dropout
def __init__(self, d_model, dropout=0.1, max_len=5000): super(PositionalEncoding, self).__init__() self.dropout = nn.Dropout(dropout) def forward(self, x): return self.dropout(x + self.pe[:x.size(1)])
这节课的标题叫做,位置编码层的直观代码实现,换句话说,就是还有不直观的实现方法。为了方便大家理解,在修改 pe-tensor 数值的时候,是一个一个修改,用到了两层 for 循环,时间复杂度很高。下节课,再给大家讲解一种,利用 tensor 特殊性质,不是那么直观,但是比较高效的生成方法。
本文链接:http://ichenhua.cn/edu/note/652
版权声明:本文为「陈华编程」原创课程讲义,请给与知识创作者起码的尊重,未经许可不得传播或转售!