输入部分主要分为“文本嵌入层”(Embedings)和“位置编码器”(PositionalEncoding)两个部分。
一、文本嵌入层
这一层的目的就是将文本词汇的数字表示转变为向量表示。
class Embeddings(nn.Module):
def __init__(self,d_model,vocab):
super(Embeddings,self).__init__()
self.lut = nn.Embedding(vocab,d_model)
self.d_model = d_model
def forward(self,x):
return self.lut(x)*math.sqrt(self.d_model)
d_model表示词的嵌入维度,也就是一个词需要多少维向量来表示。
vocab表示词汇表中词汇的总数。
直接用自带的Embedding模块对数据进行处理,最后使用d_model进行缩放处理。
测试代码:
d_model = 512
vocab = 1000
x=Variable(torch.LongTensor([[100,2,29,165],[1,6,8,7]]))
emb=Embeddings(d_model,vocab)
embr=emb(x)
二、位置编码器
向量化的文本是没有位置信息的,使用位置编码器,可以让向量能够体现出位置信息。
class PositionalEncoding(nn.Module):
def __init__(self,d_model,dropout,max_len=5000):
super(PositionalEncoding,self).__init__()
self.dropout = nn.Dropout(p=dropout)
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.00)/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)
位置信息首先需要一个位置编码矩阵和一个绝对位置矩阵。
位置编码矩阵先置为0,它的大小是(句子长度,词维度)。绝对位置矩阵的大小为(0,句子长度)它的值为连续自然数,在使用unsqueeze方法拓宽矩阵的维度,使得它变成一个(句子长度,1)的矩阵。
我们想要将绝对位置矩阵变成(句子长度,词维度)的大小,这需要有一个变换矩阵div_term。div_term初始化的时候要使用跳跃,这样是为了按照奇数和偶数进行初始化,这样可以使用sin和cos来区分位置。
这样处理完后我们得到一个二维的矩阵,;要和embedding进行输出,我们就要对矩阵进行升维,最后将编码注册成模型buffer,因为这个矩阵没有参数,不需要后续更新,从一开始就可以固定了。
还需要对数据进行一些处理,将这个三位张量的第二维也就是句子的最大长度的那一维将切片到与输入的X的第二维相同。还要适配句子长度等。