文章目录
1、位置编码的2种方案 2、位置编码 3、公式详解 : 绝对位置 、 相对位置 4、代码 4.1 代码1 4.2 代码21、位置编码的2种方案
transformer的作者刚开始说固定的位置编码和可学习的位置编码的效果是差不多的,后来证明可学习的位置编码没有太大的必要,还不如省事直接使用固定的位置编码,
代码中,token_num是句子中的单词数量,embed_dim表示每个单词的特征向量长度,
self.pe =nn.Parameter(torch.zeros(token_num, embed_dim))
2、位置编码
将对应位置的位置编码直接加在输入的单词上,如下图中的最后一行,
为什么 attention 并不能赋予 token 位置信息?
3、公式详解 : 绝对位置 、 相对位置
如下图,设置token的数量为10,token的特征向量长度为128,偶数项和奇数项的位置编码公式如下图所示,
下面详细解释一下位置编码公式,下图中也解释了下面这句话:The wavelengths form a geometric progression from 2 π 2\pi 2π to 10000 ⋅ 2 π 10000 \cdot 2\pi 10000⋅2π,
下面解释一下下面这段话:We chose this function because we hypothesized it would allow the model to easily learn to attend byrelative positions, since for any fixed offset k k k, P E p o s + k PE_{pos+k} PEpos+k can be represented as a linear function of P E p o s PE_{pos} PEpos
4、代码
4.1 代码1
import torch
import math
import matplotlib.pyplot as plt
def positional_encoding(d_model, length):
"""
:param d_model: dimension of the token
:param length: (maximum) token number
:return: length*d_model position matrix
"""
if d_model % 2 != 0:
raise ValueError("Cannot use sin/cos positional encoding with "
"odd dim (got dim={:d})".format(d_model))
pe = torch.zeros(length, d_model)
position = torch.arange(0, length).unsqueeze(1)
div_term = torch.exp((torch.arange(0, d_model, 2, dtype=torch.float) *
-(math.log(10000.0) / d_model)))
pe[:, 0::2] = torch.sin(position.float() * div_term)
pe[:, 1::2] = torch.cos(position.float() * div_term)
return pe
pe = positional_encoding(128, 10)
plt.plot(range(10), pe[:, 0])
plt.show()
输出:
4.2 代码2
import torch
import torch.nn as nn
import numpy as np
class PositionalEncoding(nn.Module):
def __init__(self, d_hid, n_position=200):
super(PositionalEncoding, self).__init__()
self.register_buffer('pos_table', self._get_sinusoid_encoding_table(n_position, d_hid))
def _get_sinusoid_encoding_table(self, n_position, d_hid):
def get_position_angle_vec(position):
return [position / np.power(10000, 2 * (hid_j // 2) / d_hid) for hid_j in range(d_hid)]
sinusoid_table = np.array([get_position_angle_vec(pos_i) for pos_i in range(n_position)])
sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i
sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1
return torch.FloatTensor(sinusoid_table).unsqueeze(0)
def forward(self, x):
return x + self.pos_table[:, :x.size(1)].clone().detach()
总结
### 文章内容总结#### 1. 位置编码的两种方案
Transformer模型中的位置编码有两种主要方案:**固定位置编码**和**可学习位置编码**。最初作者认为两者效果相近,但后续发现可学习位置编码并非必要,推荐直接使用固定位置编码,因其更为简便。在代码中,通过初始化一个参数矩阵`self.pe`来存储位置信息,其大小为句子长度乘以特征向量长度。
#### 2. 位置编码的应用
位置编码通过将每个位置特定的编码直接加到相应的单词输入特征上,以解决自注意力机制(Attention)本身无法赋予单词位置信息的问题。在Transformer模型中,单纯的Attention计算仅依赖全局内容相似性,不包含位置信息。因此,加入位置编码能够有效区分具有相同内容但位置不同的token。
#### 3. 位置编码公式详解:绝对位置与相对位置
文章详细解释了位置编码的公式,特别是对偶数项和奇数项应用正弦和余弦函数的规律性。位置编码的波长呈几何级数增长,从$2\pi$到$10000 \cdot 2\pi$。这种设计使得模型能够更容易地学习到相对位置信息,因为对于任意固定偏移`k`,$PE_{pos+k}$可以表示为$PE_{pos}$的线性函数。这意味着模型在处理位置信息时,能够更灵活地理解token之间的距离关系。
#### 4. 代码示例
- **代码1**:演示了如何生成并可视化一个固定位置编码矩阵。通过定义`positional_encoding`函数,实现了根据给定的模型维度(`d_model`)和长度(`length`),生成位置编码。该示例中,使用了正弦和余弦函数对每个位置进行编码,并通过matplotlib展示了第一个维度的编码结果。
- **代码2**:定义了一个PyTorch模块`PositionalEncoding`,该模块在初始化时生成一个正弦波式的位置编码表,并在前向传播时将此表加到输入数据上。这种方法通过重写`forward`方法,实现了位置编码与输入数据的无缝结合。位置编码表通过`_get_sinusoid_encoding_table`函数生成,该函数利用NumPy数组操作,先行生成一个含有正弦和余弦值的二维数组,再转换为PyTorch张量。