Position Embedding 的解释及具体应用

这是我的第一篇博客,想把博客作为分享巩固自己学习感悟的地方。
最近做了一个要用到Position Embedding 的项目,于是就研究了一下。

词向量

词向量,顾名思义就是把每一个单词变成一个向量,以便于后续对文本进行处理,词向量在pytorch中是用nn.Embedding(n_letters + 1, dim)来实现的,这个函数相当定义了一个词向量矩阵,n_letters 是你文本中所有单词的个数,dim是你设置的词向量维数。为什么是max_seq_len+1维的呢?这是因为在对文本处理的时候对文本进行了补0操作,所以第一行相当于是没有意义的,代表0。
举个例子:
我定义了一个10×5的词向量矩阵,然后对输入的两行句子(句子的长度为4)进行词向量编码,于是得到了一个size=torch.Size([2, 4, 5])的张量。

import torch.nn as nn
import torch
from torch.autograd import Variable
embed=nn.Embedding(10,5)
voc_embed=embed(Variable(torch.LongTensor([[2,3,4,0],[1,2,3,5]])))
print(voc_embed)
tensor([[[ 0.7789,  0.0579, -0.7092, -0.0603,  1.8060],
         [-0.0105,  0.9317,  0.0043,  0.8903, -1.5857],
         [ 0.4796,  0.4742, -1.1648, -0.9848, -0.4475],
         [-0.1791,  0.8067,  0.7030,  0.9265,  0.7657]],

        [[ 1.5996, -0.6025,  1.0755, -0.2043, -1.8362],
         [ 0.7789,  0.0579, -0.7092, -0.0603,  1.8060],
         [-0.0105,  0.9317,  0.0043,  0.8903, -1.5857],
         [ 0.9202, -1.3789, -1.3456, -0.1070,  1.2475]]],
       grad_fn=<EmbeddingBackward>)

Position Embedding

词向量的问题在于它只记录了每一个单词的词信息,而没有记录这些单词在句子中出现的位置信息,我们知道相同的单词在句子中出现的先后位置不同表示的意思可能是完全不同的。所以我们也应该记录单词在句子中的位置信息,这就是位置编码的意义。Position Embedding最先在《Attention Is All You Need》这篇论文中提出,Position Embedding加在词向量层之后,补充位置信息,注意这里加入位置编码的方式不是拼接,而是直接向量相加(Transformer 源码如此,详细解释见here)。举个例子,代码如下:

import torch
from torch.autograd import Variable
import numpy as np
import torch.nn as nn
embed=nn.Embedding(10,5)
voc_embed_0=embed(Variable(torch.LongTensor([[2,3,4,0],[1,2,3,5]])))
class PositionalEncoding(nn.Module):

    def __init__(self, max_seq_len, d_model):
        """初始化。

        Args:
            d_model: 一个标量。模型的维度,论文默认是512
            max_seq_len: 一个标量。文本序列的最大长度
        """
        super(PositionalEncoding, self).__init__()

        # 根据论文给的公式,构造出PE矩阵
        position_encoding = np.array([
            [pos / np.power(10000, 2.0 * (j // 2) / d_model) for j in range(d_model)]
            for pos in range(max_seq_len+1)])
        # 偶数列使用sin,奇数列使用cos
        position_encoding[:, 0::2] = np.sin(position_encoding[:, 0::2])
        position_encoding[:, 1::2] = np.cos(position_encoding[:, 1::2])
        position_encoding = torch.from_numpy(position_encoding)
        self.position_encoding = nn.Embedding(max_seq_len + 1, d_model)
        self.position_encoding.weight = nn.Parameter(position_encoding,
                                                  requires_grad=False)
P=PositionalEncoding(4,5)
voc_embed_1=P.position_encoding(Variable(torch.LongTensor([[1,2,3,0],[1,2,3,4]])))
print(voc_embed_0[0].size())
print(voc_embed_1[0].size())
#将两个向量拼接起来
result=torch.zeros(2,4,5)
t=torch.zeros(4,5,dtype=torch.float64)
for i in range(2):
    ans=t.new_tensor(voc_embed_0[i])
    voc_embed_2= ans + voc_embed_1[i]
    print(voc_embed_2.size())
    result[i]=voc_embed_2
print(result.size())

其实由Position Embedding的定义代码我们可以看出:其实Position Embedding和nn.Embedding很相似,只是Position Embedding将nn.Embedding的权重矩阵换成了论文中给的PE矩阵。
经过如此处理过后的词向量便具有了位置信息,可以在网络中做进一步运算,我做的是有关CNN的项目,在加入了位置信息再进行卷积后,得到的结果正确率有很大的提升。
希望我的这篇博客对你有帮助。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐