栗友们,大家好!我是Zarc
上期的文章《Transformer is all you need!》分享了我在学习transformer过程中的一些记录,并且在后面抛出了一些面试中遇到的问题,其中有两个问题提到了位置编码技术:
简单介绍一下Transformer的位置编码?有什么意义和优缺点? 你还了解哪些关于位置编码的技术,各自的优缺点是什么?
本期来分享下自然语言处理中遇到的一些位置编码技术,希望能够帮助大家更容易理解多种位置编码机制。
01
—
为什么需要位置编码?
在一个句子里面,各个词的前后关系对这个句子的含义是有影响的,比如说 ” 惩 / 恶 / 扬 / 善 “ 和” 惩 / 善 / 扬 / 恶 “,这两组词包含的字都是一样,但是仅仅是调换了顺序,它们的含义就截然相反,因此如果模型不能区分词的先后出现顺序,那么模型的输出很可能会出错。
在传统的RNN模型中,输入序列(比如说一个句子)里面的各个token(比如词)按照它们在序列中的前后位置被一个一个的先后处理,每个时间步处理一个token,虽然说每个token本身的词向量并不携带任何位置信息,但是因为RNN中当前时间步的输出是由上一个时间步的状态向量和当前时间步的输入经过变换后叠加在一起的,由于每个时间步的状态向量不相同,所以即使在不同的时间步出现相同的词,RNN仍然会区分出在不同时间步所出现的相同的词,从而会产生不同的输出。
说完了RNN,再来看一下Attention模块,众所周知,Attention模块相较于 RNN 或者 LSTM 这些时序的神经网络结构,最大的优点就是可以并行处理输入的序列,也就是说,当一个序列进入Attention模块时,序列中所有的token是同时进入的,如果没有位置信息,那么这个序列中的相同的token对于整个模块而言就没有什么语法或者语义上面的区别,因此它们在经过Attention模块后会产生相同的输出,这样就极大限制了模型的表达能力。
所以,对于这种并行处理输入序列的神经网络结构,我们需要人为地向输入序列加入每个token的位置信息来提升模型的语义表达能力,这个位置信息相当于RNN网络结构中的时间步的作用。
02
—
位置编码基本类别
位置编码根据位置向量的注入方式分为以下两种编码方法:
外置位置编码:外置位置编码就是先给输入序列的表达向量(如预训练得到的词向量)中每个token注入位置向量,然后再一起输入到Attention模块中。
内嵌位置编码:内嵌位置编码就是先将输入序列的表达向量(如预训练得到的词向量)输入到Attention模块中,再通过Attention模块为每个token创建一个位置向量,再把这个创建的位置向量通过数学变换加进到之前的输入向量中。
位置编码根据位置向量的产生方式分为以下两种编码类型:
静态位置编码:静态位置编码一般是使用确定性的公式或者表格来产生不同位置的token的位置向量,普通的transformer就是采用这种编码类型,如果使用外置位置编码的编码方法,就可以直接将静态位置编码产生的位置向量注入到输入序列的表达向量之中,再送入Attention模块当中。
动态位置编码:动态位置编码一般是以一种可以被训练的变量的形式而创建出来的张量,也就是说,当Attention模块的参数被训练时,位置编码部分的参数也是会被训练的,在模型训练结束前,位置编码的值并不是固定的,所以这种类型的位置向量产生方式被称为动态位置编码。
位置编码根据位置向量之间的距离表达方式分为以下两种编码类型:
相对位置编码:以输入序列的一个token位置为参考点,其它token离这个参考点位置的距离作为其他token的位置向量表达,我认为这点应该是考虑到输入序列中位置较近的token之间的关系更为紧密。
绝对位置编码:为输入序列的所有token的所有位置进行编码,这里我认为这种绝对位置编码类似于独热码表示,不同token之间的位置向量没有相互关系。
03
—
Transformer位置编码——外置静态绝对位置编码
Transformer的位置编码采用的是外置静态绝对位置编码方式,位置编码的公式如下:
其中:
表示二维矩阵,大小和输入序列的维度一致,行表示词语,列表示词向量
表示词语在输入序列中的位置
表示词向量的总维度
表示在词向量的总维度中的第 维
上述公式表示在每个词语的偶数位置添加正弦向量,奇数位置添加余弦变量,以此来填满整个 矩阵,然后加入到整个输入序列的词嵌入矩阵之中,再送入到Attention模块当中,这样就完成了位置编码的引入。
Transformer位置编码代码如下:
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)
04
—
总结
关于位置编码部分的介绍到这里就要结束啦,不同的位置编码方式都各有千秋,新的位置编码方式也是层出不穷,但是经典永不过时,面试中还是会经常考察到Transformer的位置编码方式,这里分享一道我在面试过程中遇到的题目:
Question : Transformer位置编码是通过正余弦函数公式来实现的,根据和差化积公式:
可以得到词语之间的相对位置关系,但是为何是Transformer是绝对位置编码?
Answer: 虽然输入的位置编码经过数学变换后包含相对位置信息,但是经过self attention的运算,不同token之间是没有相对位置信息,关于这个问题的详细解释可以参考文章——Transformer改进之相对位置编码(RPE)[1]。
实不相瞒,我在面试的时候支支吾吾很久也没有答出来这道题(:溜了溜了
关注六只栗子,面试不迷路!
参考资料
Transformer改进之相对位置编码(RPE): https://zhuanlan.zhihu.com/p/105001610
原文始发于微信公众号(六只栗子):跟所有的位置编码说嗨嗨
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88620.html