一、为什么需要位置编码
在Transformer出现以前,NLP任务大多是以RNN、LSTM为代表的循环处理方式,即一个token一个token的输入到模型当中。模型本身是一种顺序结构,天生就包含了token在序列中的位置信息。但是这种模型有很多天生的缺陷,比如:
1.会出现“遗忘”的现象,无法支持长时间序列,虽然LSTM在一定程度上缓解了这种现象,但是这种缺陷仍然存在;
2.句子越靠后的token对结果的影响越大;
3.只能利用上文信息,不能获取下文信息;
4.计算的时间复杂度比较高,循环网络是一个token一个token的输入的,也就是句子有多长就要循环多少遍;
为了解决上述问题,Transformer出现了,Transformer将token序列一次性输入到模型,不使用循环的形式。因为是一次性接收所有token作为输入进行并行处理,“遗忘”的问题没有了、所有的token都一视同仁了、上下文的信息能同时获取到、时间复杂度也降下来了。但是这又出现了新的问题,因为所有token一视同仁了,模型就没有办法知道每个token在句子中的相对和绝对的位置信息,而位置关系对于NLP任务来说是有着决定性影响的。比如下面的两句话token完全一样,但含义截然相反:
1.我把小姐姐按在地上摩擦
2.小姐姐把我按在地上摩擦
为了解决这个问题,Transformer把token的顺序信号加到词向量上帮助模型学习这些信息,这就位置编码(Positional Encoding)。下面这张图不用解释,大家再熟悉不过,接下来的内容不会涉及Self-Attention等牛逼的创新设计,只讨论位置编码,也就是红框的内容。
二、位置编码分类
位置编码用来标记token的前后顺序,总的来说,位置编码分为两个类型:绝对位置编码和相对位置编码。
1.绝对位置编码
绝对位置编码为序列中的每个位置分配一个唯一的编码,这种编码直接反映了元素在序列中的绝对位置。
最简单的想法就是从1开始向后排列,我们使用单字分词,如:
我 把 小 姐 姐 按 在 地 上 摩 擦
1 2 3 4 5 6 7 8 9 10 11
但是这种方式存在很大的问题,句子越长,后面的值越大,数字越大说明这个位置占的权重也越大,这样的方式无法凸显每个位置的真实的权重,所以这种方式基本没有人用。
基于正弦和余弦函数的编码:由Transformer模型提出,使用正弦和余弦函数的不同频率来为序列中的每个位置生成唯一的编码。这种方法的优点是能够支持到任意长度的序列,并且模型可以从编码中推断出位置信息。本文将重点介绍这个。
可学习的位置编码:一些模型选择通过训练过程中学习位置编码,而不是使用预定义的函数。这种方法允许模型自适应地优化位置编码,以最适合特定任务的方式。但是这种方式一般要求固定输入长度,而且可解释性差,所以使用的也不多。不过可以举个例子,将绝对位置转换成位置编码向量,这种操作类似于embedding:
token | 绝对位置编码 | 学习后的位置编码 |
我 |
1 | [0.71370285 0.30396524 0.58153026.....] |
把 | 2 | [0.0313028 0.42265211 0.08111295.....] |
小 | 3 | [0.02844932 0.98765191 0.60967702.....] |
姐 | 4 | [0.35318159 0.16468592 0.40572621.....] |
姐 | 5 | [0.98159706 0.23294547 0.96545601.....] |
按 | 6 | [0.44537133 0.91877043 0.6651366..... ] |
在 | 7 | [0.54972841 0.14469192 0.02620772.....] |
地 | 8 | [0.55298035 0.10918267 0.61341373.....] |
上 | 9 | [0.18383826 0.63904704 0.53275445.....] |
摩 | 10 | [0.0640315 0.20394013 0.29269359.....] |
擦 | 11 | [0.65344585 0.04618416 0.47607832.....] |
2.相对位置编码
函数型位置编码通过输入token的位置信息,得到相应的位置编码。这种编码方式使模型能够更关注元素之间的相对位置关系。
虽然原始的Transformer模型使用的是绝对位置编码,但后续的研究和变体模型中已经引入了相对位置编码的概念,以期望提高模型对序列中元素之间相对位置关系的理解能力。例如:Transformer-XL和T5模型就采用了相对位置编码。篇幅原因(懒),相对位置编码我们以后再说。
三、Transformer的位置编码
1.位置编码应有的特点
好的位置编码应该有如下特点:
1.每个时间步都有唯一的编码。
2.在不同长度的句子中,两个时间步之间的距离应该一致。
3.模型不受句子长短的影响,并且编码范围是有界的。(不会随着句子加长数字就无限增大)
4.同一时间序列中,每个连续的编码应该是等步长的,即线性相关的。
5.位置编码应该能表示token的绝对或相对位置关系。
下面我们就来看一下Transformer的位置编码,是否符合上面几点要求。
2.Transformer的位置编码公式
使用正余弦函数表示绝对位置,通过两者乘积得到相对位置:
(1)
(1)
其中PE表示位置编码; 表示一句话中token的位置;每个token的位置编码是一个向量(或者叫embedding),i表示这个向量中每个元素的index; 表示位置编码向量的维度。
式1,其实是一个式子,看起来非常复杂,写的直观一点是下面这个样子:
(2)
其中 ,可以看到,每个位置编码向量都是sin和cos交替。
3.编码因子
那么 表达式有什么意义呢?下面简单介绍一下。
a.指数级频率变化
通过 这个因子,不同维度的波长呈指数级变化。
指数函数允许位置编码在非常宽的频率范围内分布,从非常低的频率到非常高的频率。这种广泛的覆盖确保了模型可以同时捕捉到长距离和短距离的依赖关系,适应不同的序列长度和结构
指数级的变化使得每个维度的波长快速变化,这意味着即使在较低的维度,也能有效地编码位置信息。
b.平滑的频率变化
因为模型要处理高维特征, 越大, 的增长越慢。这是因为指数 变小,导致整个表达式的增长率下降。频率与 的变化速度有关。当 增长慢时,相对于位置 pos 的变化,这个比值变化得更慢,导致频率变化缓慢。
当频率变化较慢时,相邻位置的编码差异较小,这使得模型可以更容易地捕捉到位置的连续性和顺序性。这种平滑的变化有助于模型在学习过程中不会对位置的微小变化过度敏感,从而更好地泛化。
在处理长序列时,平滑的频率变化意味着即使在序列的远端,位置编码仍然能够有效地区分不同的位置。这对于模型来说是非常重要的,因为它需要在整个序列中维持对位置的敏感性,以便正确地解释和预测序列中的信息。
c.远程衰减性
随着两个token相对距离的增加,它们的相关性越来越弱,呈现出远程衰减性,这个有相关推导,这里就不赘述了,知道结论就可以了。
d.10000 是一个经验选择的常数,用于调节频率的下降速度,这个数越大下降速度就越慢。它被选为一个足够大的数,以确保即使在模型维度很大时,频率的变化也能覆盖一个广泛的范围。
4.三角函数
从式(2)中可以发现Transformer的位置编码使用了三角函数,三角函数是周期函数,那为什么要把每个维度设计成周期形式呢,可以看下面这个图,二进制格式表示一个数字:
可以看到以列为单位,不同位置上的数字都会出现0、1的交替变化。规律是第i位置上第个数据交替一次。比如第0列(右面数第一列)是每1次都切换,第1列(右面数第二列)是每两次切换。但是在浮点数的世界中使用二进制值是对空间的浪费,所以我们可以用正弦函数代替。事实上,正弦函数也能表示出二进制那样的交替。随着波长的变长,带来的是数据变化的变慢,就如同上面的提到的。
Transformer的位置编码选择三角函数的官方解释是这样的:
位置编码的每个维度都对应于一个正弦曲线。波长形成一个从2π到10000·2π的几何轨迹。我们之所以选择这个函数,是因为我们假设它可以让模型很容易地通过相对位置进行学习,因为对于任何固定的偏移量k,PEpos+k都可以表示为PEpos的线性函数。
也就是说,每个维度都是波长不同的正弦波,波长范围是2π到10000·2π,选用10000这个比较大的数是因为三角函数式有周期的,在这个范围基本上,就不会出现波长一样的情况了。然后谷歌的科学家们为了让PE值的周期更长,还交替使用sin/cos来计算PE的值,就得到了最终的公式(1)。
下图可以比较清晰的表现这过程。相同颜色的是同一个token的词向量,为了方便展示,波形图只使用sin,而且波形图只是展示使用,并不是实际波形图:
图1
可以看到,每个维度(即每一行)都是三角函数的波形,随着pos的增加,波长越来越长,这就很好的实现了低位变化快、高位变化慢的情况。
在实践中Transformer通过在偶数和奇数维度上使用正弦和余弦函数,位置编码向量的每个维度都能够保持一定的正交性。正交性确保了位置编码在不同维度上的独立表示,这有助于模型更清晰地区分和学习来自不同位置的特征。例如,如果两个位置的编码在多个维度上都有显著差异,模型可以更容易地识别这两个位置是不同的。
那每个pos内(即每一列)的元素是否为线性相关呢?
5.线性相关
我们知道某一个token的位置是pos,如果某一个token表示为 pos+k ,那就表明这个位置距上一个token为k。
如果这时我们需要看看一个位置pos和pos+k 这两个token的关系。按照位置编码的的公式,我们可以计算pos+k 的位置编码,其结果如下:
(3)
(3)
因为
所以
(5)
(5)
将式(1)中的 用 代替,得到:
(6)
(6)
将式(6)带入式子(5),得到:
(7)
(7)
因为k是一个常数,所有上面公式中sin() 和cos() 的计算值也是常数,可以表示为:
(8)
(8)
这样,就可以将 写成矩阵乘法的形式:
(9)
至此可以发现,位置 pos的编码与位置pos+k的位置编码是线性关系。
那么问题来了,上面的推导过程可以看到线性关系,那怎么表示每个token的相对距离关系呢?
6.相对位置关系
虽然这种方法是基于绝对位置的,但是也能一定程度的捕捉相对位置信息。这是因为自注意力机制能够利用这些位置编码之间的差异来推断元素之间的相对距离。
我们将 和 相乘 (两个向量的内积),可以得到如下结果:
我们发现相乘后的结果为一个余弦的加和。这里影响值的因素就是k 。如果两个token的距离越大,也就是K越大,根据余弦函数的性质可以知道,两个位置的PE相乘结果越小。这样的关系可以得到,如果两个token距离越远则乘积的结果越小,位置编码可以表示相对位置关系。
尽管Transformer的原始位置编码能够间接地表示相对位置信息,但这种表示并不是直接或显式的。这意味着模型可能需要更多的训练数据和学习能力来充分利用相对位置信息,特别是在处理长序列和复杂依赖关系时。
四、总结
问我们上面提到了位置编码应该有的特点,我们再总结一下,Transformer的位置编码简称PE:
1.属于绝对位置编码,但也能在一定程度上间接表示token的相对位置;
2.每个时间步都有唯一的编码:根据公式(1), 是确定的,根据pos和i能计算出确定的值,这一点无疑是符合的;
3.在不同长度的句子中,两个时间步之间的距离应该一致:根据公式(1), (词向量的长度)是一个定值,原版Transform的512,所以就算输入句子长度不一样,到计算编码这一步,两个时间步之间的距离也是一致的;
4.编码范围是有界的。(不会随着句子加长数字就无限增大):PE使用三角函数,振幅是1,所以范围是[-1,1];
5.同一时间序列中,每个连续的编码应该是等步长的,即线性相关的:根据上一节的推导过程可以证明;
Transformer的位置编码就简单介绍到这里,关注不迷路(*^▽^*)