Transformer解读
1.整体架构解读
image.png由图可知:
1.1输入部分:
inputs和带标签的输入分别进encoder和decoder
Positional Encoding
1.2输出部分
线性层
softmax层
1.3编码器部分
由N个编码器堆叠而成
每个编码器有两个子层相连接
第一个子层->多头自注意力机制和规范化层以及一个残差连接
第二个子层->全连接层和规范化层以及一个残差连接
1.4解码器部分
由N个解码器堆叠而成
每个编码器有三个子层相连接
第一个子层->一个多头自注意力机制层和规范化层以及一个残差连接
第二个子层->多头注意力机制和规范化层以及一个残差连接
第三个子层->全连接层和规范化层以及一个残差连接
2.输入部分详解
2.1Embedding词嵌入
目的是为了将目标文本的数字表示->向量表示,为了在高维空间捕捉词汇间的关系
-
torch.autograd.Variable的解释
#1.embedding类的实现
import torch
from torch.autograd import Variable
import math
import torch.nn as nn
class Embeddings(nn.Module):#需要继承
def __init__(self,d_model,vocab):#词嵌入的维度,词表大小
super(Embeddings,self).__init__()#继承
self.lcut = nn.Embedding(vocab,d_model)
self.d_model = d_model
def forward(self,x):
"""
可以理解为该层的前向传播逻辑,所有层中都会有此函数。传给类的实例化对象时,自动调用
:param x:输入进模型的文本通过词汇映射后的数字张量
:return:
"""
return self.lcut(x)*math.sqrt(self.d_model)
效果如下
d_model = 512
vocab = 100
x = Variable(torch.LongTensor([[1,2,3,4],[5,6,7,8]]))#[2,4]
emb = Embeddings(d_model,vocab)
embr = emb(x)
print("emb:",embr)
print(embr.shape)#[2,4,512]
>>>>>
emb: tensor([[[-13.7408, -4.3108, 35.0170, ..., -44.5515, -16.5556, -2.9577],
[ 23.3314, -9.3100, 26.4966, ..., 7.4887, 28.7678, 16.3725],
[-13.5485, 28.9699, 35.8271, ..., -22.9973, -0.4782, 9.3057],
[-26.4563, 6.6810, -58.8932, ..., 55.4734, 16.3932, -38.4998]],
[[ 0.6895, 43.8544, -10.3074, ..., 39.5331, 20.9398, 13.0055],
[ 10.1109, 16.5785, 7.7799, ..., 19.8802, -18.9864, -0.1557],
[-31.9423, -5.7930, 2.0319, ..., 0.4931, -27.7125, -3.4999],
[-47.6672, 4.2582, -32.6036, ..., 12.7451, 9.8043, -1.6442]]],
grad_fn=<MulBackward0>)
torch.Size([2, 4, 512])
2.2Positional Encoding
2.2.1为什么需要位置编码器(没有上下文关系这种)
在Transformer编码器中没有针对词汇位置信息的处理,故需要在embedding层后加入位置编码器,将词汇位置不同可能会产生不同语义的信息加入到嵌入张量中(embedding),用来弥补位置信息的缺失。
2.2.2位置信息编码的实现
class PositionalEncoding(nn.Module):
def __init__(self,d_model,dropout,max_len=5000):
"""
:param d_model:embedding的维度
:param dropout: Dropout的置零比例
:param max_len: 每个句子的最大长度
"""
super(PositionalEncoding, self).__init__()
#实例化Dropout层
self.dropout = nn.Dropout(p=dropout)
#初始一个位置编码矩阵,大小是max_len*d_model
pe = torch.zeros(max_len,d_model)
#初始化一个绝对位置矩阵,词汇的位置就是用它的索引表示max_len*1
position = torch.arange(0,max_len).unsqueeze(1)#由[0,1,2...max_len][max_len] -> [[0],[1]...[max_len]][max_len,1]
#目的是要把position的信息放到pe里面去
#定义一个变换矩阵使得position的[max_len,1]*变换矩阵得到pe[max_len,d_model]->变换矩阵格式[1,d_model]
#除以这个是为了加快收敛速度
#div_term格式是[0,1,2...d_model/2],分成了两个部分,步长为2
div_term = torch.exp(torch.arange(0,d_model,2)*-(math.log(10000.0)/d_model))
# print(div_term.shape)
# print(position * div_term)
# a = position*div_term
# print(a.shape)
#将前面定义好的矩阵进行奇数偶数赋值
pe[:,0::2] = torch.sin(position*div_term)
pe[:,1::2] = torch.cos(position*div_term)
#此时pe[max_len,d_model]
#embedding三维(可以是[batch_size,vocab,d_model])#vocab就是max_len
#将pe升起一个维度扩充成三维张量
pe = pe.unsqueeze(0)
#位置编码矩阵注册成模型的buffer,它不是模型中的参数,不会跟随优化器进行优化
#注册成buffer后我们就可以在模型的保存和加载时,将这个位置编码器和模型参数加载进来
self.register_buffer('pe',pe)
def forward(self,x):
"""
:param x:x代表文本序列的词嵌入
pe编码过长将第二个维度也就是max_len的维度缩小成句子的长度
"""
x = x + Variable(self.pe[:,:x.size(1)],requires_grad=False)
return self.dropout(x)
x = torch.LongTensor([[1,2,3,4],[5,6,7,8]])#[2,4]
emb = Embeddings(d_model,vocab)#[2,4,512]
embr = emb(x)
pe = PositionalEncoding(d_model,dropout=0.2,max_len=50)
pe_result = pe(embr)
#
print(pe_result)
print(pe_result.shape)#[2,4,512]
网友评论