Transformer
1、优势
相比之前占领市场的LSTM和GRU模型,Transformer有两个显著优势
1、Transformer能够利用分布式GPU进行并行训练,提升模型训练效率
2、在分析预测更长的文本时,捕捉间隔较长的语义关联效果更好。
2、Transformer模型的作用
基于seq2seq架构的transformer模型可以完成NLP领域研究的典型任务,如机器翻译,文本生成等,同时又可以构建预训练语言模型,用于不同任务的迁移学习
3、声明:
在接下来的架构分析中,我们假设使用Transfomer模型架构处理从一种语言文本到另一种语言文本的翻译工作,因此很多命名方式遵循NLP中的规则。比如Embedding层将称作文本嵌入层。Embedding层产生的张量称为词嵌入张量,它的最后一维将称作词向量等。
4、Transformer总体架构图
4.1、总体架构可分为4个部分:
输入部分、输出部分、解码器部分、编码器部分
4.2、输入部分包含
源文本嵌入层及其位置编码器
目标文本嵌入层及其位置编码器
4.3、输出部分包含
线性层
softmax处理器
4.4、编码器部分
由N个编码器堆叠而成
每个编码器层由两个子层连接结构组成
第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
第二个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
4.5、解码器部分
由N个解码器层堆叠而成
每个解码器层由三个子层连接结构组成
第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
第二个子层连接结构包括一个多头注意力子层和规范化层以及一个残差连接
第三个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
5、输入部分实现
5.1、输入部分包含
源文本嵌入层及其位置编码器
目标文本嵌入层及其位置编码器
5.2、文本嵌入层的作用
无论是源文本嵌入还是目标文本嵌入,都是为了将文本中词汇的数字表示转变为向量表示,希望在这样的高维空间捕捉词汇间的关系。
5.3、代码实现
import torch
import torch.nn as nn
import math
from torch.autograd import Variable
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
import copyfrom torch.autograd import Variableclass Embeddings(nn.Module):def __init__(self,d_model,vocab):#d_model 词嵌入的维度# vocab 词表的大小super(Embeddings,self).__init__()#定义Embedding层self.lut = nn.Embedding(vocab,d_model)#将参数传入类中self.d_model = d_modeldef forward(self,x):# x 代表输入进模型的文本通过词汇映射后的数字张量return self.lut(x) * math.sqrt(self.d_model)# embedding = nn.Embedding(10,3)
# input = torch.LongTensor([[1,2,4,5],[4,3,2,9]])
# embedding(input)
#
# embedding = nn.Embedding(10,3,padding_idx=0)
# input = torch.LongTensor([[0,2,0,5]])
# embedding(input)d_model =512
vovab=1000x= Variable(torch.LongTensor([[100,2,421,508],[491,998,1,221]]))emb = Embeddings(d_model,vovab)
embr = emb(x)
print("embr:",embr)
print(embr.shape)
embr: tensor([[[-20.2554, 17.7277, 8.8031, …, -29.8762, 26.7293, 21.2630],
[ 31.6993, 8.9157, -2.9982, …, 6.6459, -32.7140, -54.1738],
[-28.2323, 14.4516, 44.2505, …, 4.9644, 0.4566, 19.9003],
[ 5.0414, 6.6755, -33.0183, …, -8.5692, -12.1072, 1.1112]],
[[ -3.4436, -0.6920, 23.7635, …, 23.3742, -17.4037, 36.8717],
[-20.4073, 4.0683, -15.1138, …, -8.5952, -39.0693, 6.9092],
[ -6.2102, 13.6323, 13.0433, …, -9.4752, 3.8937, 4.6310],
[ 1.4172, -16.3261, -3.3502, …, 4.2463, -6.5221, 1.6463]]],
grad_fn=)
torch.Size([2, 4, 512])
6、位置编码器
6.1、作用
因为在Transformer的编码器结构中,并没有针对词汇位置信息的处理,因此需要在Embedding层后加入位置编码器,将词汇位置不同可能会产生不同语义的信息加入到词嵌入张量中,以弥补位置信息的缺失。
6.2、代码分析
class PositionalEncoding(nn.Module):def __init__(self,d_model,dropout,max_len=5000):# d_model 词嵌入的维度#dropout 置零比率#max_len 代表每隔句子的最大长度super(PositionalEncoding,self).__init__()#实例化Dropout层self.dropout = nn.Dropout(p=dropout)#初始化一个位置编码矩阵,大小是max_len*d_modelpe = torch.zeros(max_len,d_model)#初始化一个绝对位置矩阵,max_len*1position = torch.arange(0,max_len).unsqueeze(1)#定义一个变化矩阵div_term,跳跃式的初始化div_term = torch.exp(torch.arange(0,d_model,2)* -(math.log(10000.0) / d_model))#将前面定义的变化矩阵进行奇数,偶数分别赋值pe[:,0::2] = torch.sin(position * div_term)pe[:,1::2] = torch.cos(position * div_term)#将二位张量扩充成三维张量pe = pe.unsqueeze(0)#将位置编码矩阵注册成模型的buffer,这个buffer不是模型中的参数,不跟随优化器同步更新#注册成buffer后我们就可以在模型保存后重新加载的时候,将这个位置编码器和模型参数一同加载进来self.register_buffer('pe',pe)def forward(self,x):#X 代表文本序列的词嵌入表示x = x+Variable(self.pe[:,:x.size(1)],requires_grad = False)return self.dropout(x)d_model=512
dropout = 0.1
max_len =60x=embr
pe = PositionalEncoding(d_model,dropout,max_len)
pe_result = pe(x)
print(pe_result)
print(pe_result.shape)
tensor([[[ -6.3630, 1.9184, 10.4811, …, -14.3086, -12.9894, 2.4670],
[ 10.2568, -0.0000, -12.9044, …, 31.4996, -9.7708, 7.2341],
[ 48.8647, -11.6848, -6.0655, …, 9.5355, -28.6491, 23.9751],
[ -0.1019, -8.0144, 11.9831, …, -47.2554, -2.2179, -15.1906]],
[[ 31.3757, 15.6444, -5.7853, …, -23.6737, 35.4592, -34.0238],
[ 6.6492, 4.1495, -32.2207, …, 0.4814, 13.9256, -2.7659],
[-16.4884, 0.7166, 2.5357, …, -16.8972, 23.9224, -23.4673],
[ 3.0689, 19.4020, -17.6528, …, 9.2178, -0.0000, -44.4839]]],
grad_fn=)
torch.Size([2, 4, 512])
7、绘制词汇向量中特征的分布曲线
#绘制词汇向量中特征的分布曲线
import matplotlib.pyplot as plt
plt.figure(figsize=(15,5))#实例化PositionalEncoding类对象,词嵌入维度给20,置零比率设置为0
pe = PositionalEncoding(20,0)
#向pe中传入一个全零初始化X,相当于展示pe
y=pe(Variable(torch.zeros(1,100,20)))
plt.plot(np.arange(100),y[0,:,4:8].data.numpy())
plt.legend(["dim %d"%p for p in [4,5,6,7]])
效果分析
每条颜色的曲线代表某一个词汇中的特征在不同位置的含义
保证同一词汇随着所在位置不同它对应位置嵌入向量会发生变化
正弦波和余弦波的值域范围都是1到-1 这又很好的控制了嵌入数值的大小,有助于梯度的快速计算。
8、编码器
8.1、编码器部分
由N个编码器层堆叠而成
每个编码器层由两个子层连接结构组成
每一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
第二个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
8.2、掩码张量
什么是掩码张量
掩张量遮掩,码就算我们张量的数值,它的尺寸不定,里面一般只有1和0的元素,代表位置被遮掩或者不被遮掩,至于是0位置被遮掩可以自定义,因此他的作用就是让另一个张量中的一些数值被遮掩,可以说被替换,它的表现形式是一个张量
掩码张量的作用
在transformer中,掩码张量的主要作用在应用attention时,有一些生成的attention张量中的值计算有可能已知了未来信息而得到的,未来信息被看到是因为训练时会把整个输出结果都一次性进行Embedding,但是理论上解码器的输出却不是一次就能产生最终结果的,而是一次次通过上一次结果综合得出的,因此,未来的信息可能被提前利用。所以,我们会进行遮掩。
生成掩码张量的代码分析
def subsqquent_mask(size):#size 代表掩码张量后两个维度,形成一个方阵attn_shape =(1,size,size)#先用np.ones()先构建一个全1的张量,然后利用np.triu()形成上三角矩阵subsqquent_mask=np.triu(np.ones(attn_shape),k=1).astype('uint8')#使得上三角矩阵反转return torch.from_numpy(1-subsqquent_mask)# np.triu([[1,2,3],[4,5,6],[7,8,9],[10,11,12]],k=-1)
# np.triu([[1,2,3],[4,5,6],[7,8,9],[10,11,12]],k=0)
# np.triu([[1,2,3],[4,5,6],[7,8,9],[10,11,12]],k=1)size=5
sm = subsqquent_mask(size)
print("sm:",sm)plt.figure(figsize=(5,5))
plt.imshow(subsqquent_mask(20)[0])
sm: tensor([[[1, 0, 0, 0, 0],
[1, 1, 0, 0, 0],
[1, 1, 1, 0, 0],
[1, 1, 1, 1, 0],
[1, 1, 1, 1, 1]]], dtype=torch.uint8)
8.3、注意力机制
什么是注意力机制
我们观察事物时,之所以能够快速判断一种事物(当然允许判断是错误的),是因为我们大脑能够很快把注意力放在食物最具有辨识度的部分,从而做出判断,而非是从头到尾的观察一遍事物后,才能有的判断结果。正是基于这样的理论,就产生了注意力机制
什么是注意力计算规则
他需要三个指定的输入Q(query),K(key),V(value),然后通过公式得到注意力的计算结果,这个结果代表query 和value 作用下的表示。
注意力计算规则
A t t e n t i o n ( Q , K , V ) = S O F T M A X ( Q K T / d k ) V Attention(Q,K,V)=SOFTMAX(QK^T/\sqrt{d_k})V Attention(Q,K,V)=SOFTMAX(QKT/dk)V
什么是自注意力机制
注意力机制是注意力计算规则能够应用的深度学习网络的载体,除了注意力计算规则外,还包括一些必要的全连接层以及相关张量处理,使其与应用网络融为一体。使用自注意力计算规则的注意力机制称为自注意力机制
注意力机制在网络中实现的图形表示
代码分析
def attention(query,key,value,mask=None,dropout=None):#query,key,value 代表注意力的三个输入张量#mask 掩码张量#dropout 传入dropout实例化对象d_k = query.size(-1)#按照注意力计算公式,将query和key的转置进行矩阵乘法,然后除以缩放稀疏scores = torch.matmul(query,key.transpose(-2,-1))/math.sqrt(d_k)#判断是否使用掩码张量if mask is not None:#利用masked_fill方法,将掩码张量和0进行位置的意义比较,如果等于0,替换成一个非常小的scores=scores.masked_fill(mask==0,-1e9)#对scores的最后一个维度上进行softmax操作p_attn = F.softmax(scores,dim=-1)#判断是否使用dropoutif dropout is not None:p_attn=dropout(p_attn)return torch.matmul(p_attn,value),p_attnquery = key =value = pe_result
mask = Variable(torch.zeros(2,4,4))
attn,p_attn = attention(query,key,value,mask=mask)
print('attn:',attn)
print(attn.shape)
print('p_attn:',p_attn)
print(p_attn.shape)
attn: tensor([[[ 13.1641, -4.4452, 0.8736, …, -5.1322, -13.4068, 4.6214],
[ 13.1641, -4.4452, 0.8736, …, -5.1322, -13.4068, 4.6214],
[ 13.1641, -4.4452, 0.8736, …, -5.1322, -13.4068, 4.6214],
[ 13.1641, -4.4452, 0.8736, …, -5.1322, -13.4068, 4.6214]],
[[ 6.1514, 9.9781, -13.2808, …, -7.7179, 18.3268, -26.1852],
[ 6.1514, 9.9781, -13.2808, …, -7.7179, 18.3268, -26.1852],
[ 6.1514, 9.9781, -13.2808, …, -7.7179, 18.3268, -26.1852],
[ 6.1514, 9.9781, -13.2808, …, -7.7179, 18.3268, -26.1852]]],
grad_fn=)
torch.Size([2, 4, 512])
p_attn: tensor([[[0.2500, 0.2500, 0.2500, 0.2500],
[0.2500, 0.2500, 0.2500, 0.2500],
[0.2500, 0.2500, 0.2500, 0.2500],
[0.2500, 0.2500, 0.2500, 0.2500]],
[[0.2500, 0.2500, 0.2500, 0.2500],
[0.2500, 0.2500, 0.2500, 0.2500],
[0.2500, 0.2500, 0.2500, 0.2500],
[0.2500, 0.2500, 0.2500, 0.2500]]], grad_fn=)
torch.Size([2, 4, 4])
9、多头注意力机制
9.1、定义
每个头开始从词义层面分割输出的张量,也就是每个头都想获得一组Q,K,V进行注意力机制的计算,但是句子中每个词的表示只获得一部分,也就是只分割了最后一维的词嵌入向量。这就是所谓的多头。将每个头的获得的输入送到注意力机制中,就形成多头注意力机制。
9.2、多头注意力机制结构图
9.3、多头注意力机制的作用
这种结构设计能让每个注意力机制去优化每个词汇的不同特征部分,从而均衡同一种注意力机制可能产生的偏差,让词义拥有来自更多元的表达,实验表明可以从而提升模型效果。
9.4、代码实现
#需要使用clone函数将他们一同初始化到一个网络层列表对象中
import copy
def clones(module,N):# modele:代表要克隆目标网络层# N 将module克隆几个return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])#实现多头注意力机制的类
class MultiHeadedAttention(nn.Module):def __init__(self,head,embedding_dim,dropout=0.1):#head, 代表几个头的参数# embedding_dim, 代表词嵌入的维度# dropout 进行Dropout操作时,置零的比率super(MultiHeadedAttention,self).__init__()#要确认一个事实:多头的数量head需要整除词嵌入的维度embedding_dimassert embedding_dim % head ==0#要得到每个头获得的词向量的维度self.d_k = embedding_dim//headself.head = headself.embedding_dim = embedding_dim#获得线性层,要获得4个,分别时Q,K,V以及最终的输出线性层self.linears = clones(nn.Linear(embedding_dim,embedding_dim),4)#初始化注意力张量self.attn = None#初始化dropoutself.dropout = nn.Dropout(p=dropout)def forward(self,query,key,value,mask=None):#query,key,value 是注意力机制的三个输入张量,mask代表掩码张量#首先判断是否使用掩码张量if mask is not None:#使用squeeze将掩码张量进行维度扩充,代表多头中第n个头mask = mask.unsqueeze(1)#得到batch_sizebatch_size = query.size(0)#首先使用zip将网络层和输入数据连接在一起,模型的输出利用view和transpose进行维度和形状的该表query,key,value =\[model(x).view(batch_size,-1,self.head,self.d_k).transpose(1,2)for model,x in zip(self.linears,(query,key,value))]#将每个头的输出传入到注意力层x,self.attn = attention(query,key,value,mask = mask,dropout=self.dropout)#得到每个头的计算结果是4维张量,需要进行形状的转换#前面已经将1,2两个维度进行过转置,这里要重新转置回来#注意:经历了transpose()方法后,必须要使用contiguous方法,不然无法使用view()x = x.transpose(1,2).contiguous().view(batch_size,-1,self.head * self.d_k)#最后将x输入线性层列表中的最后一个线性层中进行处理,得到最终的多头注意力结构输出return self.linears[-1](x)#实例化若干参数
head =8
embedding_dim =512
dropout =0.2#若干输入参数的初始化
query = key = value = pe_result
mask = Variable(torch.zeros(2,4,4))
mha = MultiHeadedAttention(head,embedding_dim,dropout)
mha_result = mha(query,key,value,mask)
print(mha_result)
print(mha_result.shape)
tensor([[[-0.8171, -0.9882, 1.8880, …, 2.5175, 3.3250, -0.4115],
[ 0.6181, -1.1813, 0.2220, …, -0.2681, 2.4096, 1.6456],
[-0.7741, -2.9558, -0.5935, …, 1.7400, 1.3091, 0.9415],
[-0.5231, 0.0089, 1.1868, …, 1.1572, 1.6008, -0.2667]],
[[-2.0087, -0.1469, 1.9072, …, 1.4838, 0.7195, 0.4826],
[-1.5568, -0.4944, 1.6701, …, 1.1582, 1.1512, -0.1627],
[ 0.8133, -2.3988, 1.0239, …, 1.4076, 1.1036, 1.6094],
[ 0.7464, -1.4357, 0.8769, …, 2.2530, 0.7432, 1.5688]]],
grad_fn=)
torch.Size([2, 4, 512])
10、前馈全连接层
10.1、定义
在Transformer中前馈全连接层就是具有两层线性层的全连接网络。
10.2、作用
考虑注意力机制可能对复杂过程的拟合程度不够,通过增加两层网络来增强模型的能力。
10.3、代码
#前馈全连接层的代码分析
class PositionwiseFeedForward(nn.Module):def __init__(self,d_model,d_ff,dropout=0.1):#d_model 代表词嵌入的维度,同时也是两个线性层的输入维度和输出维度# d_ff 代表第一个线性层的输出维度,和第二个线性层的输入维度# dropout 经过dropout层处理时,随机置零的比率super(PositionwiseFeedForward,self).__init__()#定义两层全连接线性层self.w1 = nn.Linear(d_model,d_ff)self.w2 = nn.Linear(d_ff,d_model)self.dropout = nn.Dropout(dropout)def forward(self,x):#x:代表来自上一层的输出#首先将x送入第一个线性层网络,然后经历relu函数的激活,再经历dropout层的处理# 最后送入第二个线性层return self.w2(self.dropout(F.relu(self.w1(x))))d_model=512
d_ff =64
dropout=0.2x=mha_result
ff=PositionwiseFeedForward(d_model,d_ff,dropout)
ff_result = ff(x)
print(ff_result)
print(ff.result.shape)
tensor([[[ 2.2672e+00, 4.0089e-01, 3.8683e-01, …, 3.2871e-01,
-3.5182e-01, 1.9039e+00],
[ 1.7482e+00, 3.1679e-01, -5.1712e-01, …, 1.5162e+00,
9.0447e-01, 2.3641e+00],
[ 2.4683e+00, -7.0630e-01, 2.0410e+00, …, 1.1127e+00,
-2.6693e-03, 2.4294e+00],
[ 9.6046e-01, -7.2104e-01, 6.4226e-01, …, -3.3724e-01,
-6.3437e-01, 6.8779e-01]],
[[ 3.5217e-01, -5.0803e-01, -5.4572e-01, …, 1.0836e+00,
2.1864e+00, 1.9360e+00],
[ 6.1565e-01, 9.1158e-01, -5.3556e-01, …, 2.0134e+00,
1.1383e+00, 3.0298e+00],
[ 1.6306e+00, -1.9340e+00, -8.7146e-01, …, 9.7488e-01,
-3.3027e-02, 6.0742e-01],
[ 1.0307e+00, -1.6499e+00, -4.5989e-02, …, 2.8859e-01,
1.2486e+00, 1.1595e+00]]], grad_fn=)
torch.Size([2, 4, 512])
11、规范化层
它是所有深层网络模型都需要的标准网络层,因为随着网络层数的增加,通过多层的计算后参数可能开始出现过大或过小的情况,这样可能会导致学习过程出现异常,模型可能收敛非常的慢。因此都会在一定层数后接规范化层进行数值的规范化,使其特征数值在合理范围内。
代码实现
class LayerNorm(nn.Module):def __init__(self,features,eps=1e-6):#features,代表词嵌入的维度# eps 一个足够小的正数,用来规范化计算公式的分母中,防止除零操作super(LayerNorm, self).__init__()#初始化两个参数张量a2,b2,用于对结果做规范化操作计算#将其用nn.Parameter进行封装,代表他们也是模型中的参数self.a2 = nn.Parameter(torch.ones(features))self.b2 = nn.Parameter(torch.zeros(features))self.eps=epsdef forward(self,x):#x:代表上一层网络的输出mean = x.mean(-1,keepdim=True)#接着对x进行字后一个维度上的求标准差的操作,同事保持输出维度和输入维度std = x.std(-1,keepdim=True)#按照规范化公式进行计算并返回return self.a2 * (x-mean) / (std + self.eps) + self.b2features = d_model =512
eps=1e-6
x = ff_result
ln = LayerNorm(features,eps)
ln_result = ln(x)
print(ln_result)
print(ln_result.shape)
tensor([[[ 1.7829, 0.3386, 0.3277, …, 0.2827, -0.2439, 1.5017],
[ 1.3629, 0.2351, -0.4220, …, 1.1802, 0.6982, 1.8483],
[ 1.6529, -0.4555, 1.3690, …, 0.7526, 0.0118, 1.6271],
[ 0.7694, -0.5806, 0.5139, …, -0.2725, -0.5111, 0.5505]],
[[ 0.2824, -0.4432, -0.4750, …, 0.8993, 1.8294, 1.6182],
[ 0.4761, 0.7333, -0.5244, …, 1.6908, 0.9303, 2.5742],
[ 1.6352, -2.0278, -0.9360, …, 0.9614, -0.0744, 0.5838],
[ 0.9482, -1.5066, -0.0378, …, 0.2686, 1.1478, 1.0662]]],
grad_fn=)
torch.Size([2, 4, 512])
12、子层连接结构
12.1、定义
输入到每个子层以及规范化层的过程中,还使用了残差链接(跳跃连接),因此我们把这一部分结构整体叫做子层连接(代表子层及其链接结构),在每个编码层中,都有两个子层,这两个子层加上周围的链接结构就形成了两个子层连接结构。
12.2、子层连接结构图
12.3、代码
#子层连接结构的类
class SublayerConnection(nn.Module):def __init__(self,size,dropout=0.1):#size 代表词嵌入的维度#dropout 进行dropout操作的置零比率super(SublayerConnection,self).__init__()#实例化一个规范化层的对象self.norm = LayerNorm(size)#实例化一个dropout对象self.dropout = nn.Dropout(p=dropout)self.size = sizedef forward(self,x,sublayer):# x 代表上一次传入的张量#sublayer 该子层连接中子层函数#首先将x进行规范化,然后送入子层函数中处理,处理结果进入dropout层,最后进行残差return x+self.dropout(sublayer(self.norm(x)))size =d_model= 512
dropout =0.2
head=8x=pe_result
mask = Variable(torch.zeros(2,4,4))
self_attn = MultiHeadedAttention(head,d_model)
sublayer = lambda x:self_attn(x,x,x,mask)sc = SublayerConnection(size,dropout)
sc_result = sc(x,sublayer)
print(sc_result)
print(sc_result.shape)
tensor([[[ 0.0000, 6.1652, 35.3878, …, 48.7933, -0.1440, 2.8220],
[ -3.0348, -0.2250, 7.0457, …, -38.1462, 7.8839, -6.9128],
[ 36.2534, 8.1799, 29.8616, …, -0.2696, 30.2995, -20.3161],
[ 12.0003, 36.1357, -23.8233, …, 3.2105, 19.3110, 26.2077]],
[[-26.5623, -23.5423, 47.0930, …, -29.3046, -8.6820, -27.5048],
[ 5.8856, 12.6959, -10.2683, …, -20.8578, 20.0263, -5.2042],
[ 27.9057, -6.0476, -49.2554, …, 0.4476, 4.0543, -51.9210],
[-10.1166, 4.9567, -18.5832, …, 34.9062, -7.5173, -1.0328]]],
grad_fn=)
torch.Size([2, 4, 512])
13、编码器层
13.1 作用
作为编码器的组成单元,每个编码器完成一次对输入的特征提取过程,即编码过程
13.2 构成图
13.3代码
class EncoderLayer(nn.Module):def __init__(self,size,self_attn,feed_forward,dropout):#size,代表词嵌入的维度# self_attn,传入的多头自注意力子层的实例化对象# feed_forward,前馈全连接层实例化对象# dropout进行dropout操作时的置零比率super(EncoderLayer,self).__init__()#将两个实例化对象和参数传入类中self.self_attn = self_attnself.feed_forward = feed_forwardself.size = size#编码器层有2个子层连接结构,使用clones函数进行操作self.sublayer = clones(SublayerConnection(size,dropout),2)def forward(self,x,mask):#x,上一层的传入张量# mask 代表掩码张量#首先让x经过第一个子层连接结构,内部包含多头自注意力机制子层#再让张量经过第二个子层连接结构,其中包含前馈全连接网络x = self.sublayer[0](x, lambda x:self.self_attn(x,x,x,mask))return self.sublayer[1](x,self.feed_forward)size =512
head=8
d_model=512
d_ff =64
x= pe_result
dropout=0.2
self_attn=MultiHeadedAttention(head,d_model)
ff = PositionwiseFeedForward(d_model,d_ff,dropout)
mask = Variable(torch.zeros(2,4,4))el = EncoderLayer(size,self_attn,ff,dropout)
el_result = el(x,mask)
print(el_result)
print(el_result.shape)
tensor([[[-4.2720e-01, 5.7465e+00, 3.5722e+01, …, 4.8735e+01,
2.6938e-01, 2.9583e+00],
[-3.5640e+00, -6.7944e-02, 6.5657e+00, …, -3.7921e+01,
8.0924e+00, -7.5972e+00],
[ 3.6331e+01, 7.8085e+00, 2.9987e+01, …, 3.6380e-02,
3.0400e+01, -2.0897e+01],
[ 1.2096e+01, 3.5518e+01, -2.3804e+01, …, 3.3239e+00,
1.9529e+01, 2.5491e+01]],
[[-2.6602e+01, -2.3194e+01, 4.7234e+01, …, -3.0099e+01,
-8.8597e+00, -2.8042e+01],
[ 5.9610e+00, 1.2979e+01, -9.6283e+00, …, -2.1194e+01,
1.9972e+01, -5.7832e+00],
[ 2.8189e+01, -5.8794e+00, -4.8681e+01, …, -8.4260e-01,
3.9109e+00, -5.2528e+01],
[-9.9694e+00, 5.2560e+00, -1.7931e+01, …, 3.3784e+01,
-7.7284e+00, -1.3714e+00]]], grad_fn=)
torch.Size([2, 4, 512])
14、编码器
14.1、作用
编码器用于对输入进行指定的特征提取过程,也称为编码,由N个编码器层堆叠而成
14.2、结构图
14.3、代码
class Encoder(nn.Module):def __init__(self,layer,N):#layer,编码器层# N 编码器中有几个layersuper(Encoder, self).__init__()#首先使用clones函数克隆N个编码层放置在self.layers中self.layers = clones(layer,N)#初始化一个规范化层,作用在编码器的最后面self.norm = LayerNorm(layer.size)def forward(self,x,mask):#x,上一层输出的张量# mask 掩码张量#让x依次经历N个编码器层的处理,最后再经过规范化层就可以输出了for layer in self.layers:x = layer(x,mask)return self.norm(x)size = d_model =512
d_ff =64
head=8
c = copy.deepcopy
attn = MultiHeadedAttention(head,d_model)
ff= PositionwiseFeedForward(d_model,d_ff,dropout)
dropout=0.2
layer = EncoderLayer(size,c(attn),c(ff),dropout)
N =8
mask = Variable(torch.zeros(2,4,4))en=Encoder(layer,N)
en_result = en(x,mask)
print(en_result)
print(en_result.shape)
tensor([[[-0.1518, 0.3273, 1.4329, …, 1.7899, 0.0459, 0.0939],
[-0.4083, -0.0095, 0.1389, …, -1.7411, 0.4418, -0.3828],
[ 1.2302, 0.3678, 1.2092, …, -0.1921, 1.1543, -0.8638],
[ 0.2952, 1.4045, -1.0659, …, 0.0298, 0.6647, 0.8942]],
[[-1.3855, -0.9462, 1.8530, …, -1.4786, -0.4059, -1.2553],
[ 0.0166, 0.7179, -0.3595, …, -0.9669, 0.8786, -0.2681],
[ 0.9744, -0.1501, -1.9463, …, -0.2236, 0.2517, -2.1043],
[-0.7056, 0.4221, -0.7811, …, 1.3430, -0.1311, -0.2085]]],
grad_fn=)
torch.Size([2, 4, 512])
15、解码器
15.1、解码器部分
由N个解码器层堆叠而成
每个解码器层由三个子层连接结构组成
第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
第二个子层连接结构包括一个多头注意力子层和规范化层以及一个残差连接
第三个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
15.2、作用
作为解码器的组成单元,每个解码器层根据给定的输入向目标方向进行特征提取操作,即解码过程
15.3、代码实现
class DecoderLayer(nn.Module):def __init__(self,size,self_attn,src_attn,feed_forward,dropout):#size,词嵌入的维度# self_attn,多头自注意力机制的对象# src_attn,常规的注意力机制的对象# feed_forward,前馈全连接层的对象# dropout dropout的置零比率super(DecoderLayer,self).__init__()#将参数传入类中self.size = sizeself.self_attn = self_attnself.src_attn = src_attnself.feed_forward = feed_forward#按照解码器层的机构图,使用clones函数克隆3个子层连接对象self.sublayer = clones(SublayerConnection(size,dropout),3)def forward(self,x,memory,source_mask,target_mask):#x,上一次输入的张量# memory,编码器的语义存储张量# source_mask,源数据的掩码张量# target_mask 目标数据的掩码张量m=memory#第一步让x经历第一个子层,多头自注意力机制的子层#采用target_mask,为了将解码时未来的信息进行遮掩,比如模型解码第二个字符,只能x=self.sublayer[0](x,lambda x:self.self_attn(x,x,x,target_mask))#第二步x经历第二个子层,常规的注意力机制的子层,Q!=K=Vx= self.sublayer[1](x,lambda x:self.src_attn(x,m,m,source_mask))#第三步让x经历第三个子层,前馈全连接层return self.sublayer[2](x,self.feed_forward)size = d_model =512
d_ff =64
head=8
dropout=0.2
c = copy.deepcopy
self_attn = src_attn = MultiHeadedAttention(head,d_model,dropout)
ff= PositionwiseFeedForward(d_model,d_ff,dropout)x= pe_result
memory = en_result
mask = Variable(torch.zeros(2,4,4))
source_mask = target_mask = maskdl = DecoderLayer(size,self_attn,src_attn,ff,dropout)
dl_result = dl(x,memory,source_mask,target_mask)
print(dl_result)
print(dl_result.shape)
tensor([[[ 0.3419, 6.5338, 35.6971, …, 49.1141, 0.2969, 2.5040],
[ -2.8462, 0.5236, 7.3461, …, -38.0432, 8.3481, -6.8091],
[ 36.6298, 8.2685, 30.4646, …, 0.1778, 29.6717, -21.2542],
[ 12.3353, 36.6340, -23.5155, …, 3.6022, 19.3118, 26.0038]],
[[-26.3296, -23.1329, 48.3801, …, -29.6159, -8.4855, -27.4689],
[ 5.7288, 13.0199, -9.1441, …, -20.5544, 19.5439, -5.7355],
[ 28.1815, -5.6043, -47.8068, …, 0.5109, 3.7007, -52.9843],
[-10.1336, 5.2637, -17.1411, …, 34.1590, -7.7160, -1.8273]]],
grad_fn=)
torch.Size([2, 4, 512])
16、解码器
16.1、作用
根据编码器的结果以及上一次预测的结果,对下一次可能出现的’值’进行特征表示。
16.2、代码
#构建解码器的类
class Decoder(nn.Module):def __init__(self,layer,N):#layer,解码器层的对象# N,将layer进行几层的拷贝super(Decoder, self).__init__()#利用clones函数克隆N个layerself.layers = clones(layer,N)#实例化一个规范化层self.norm = LayerNorm(layer.size)def forward(self,x,memory,source_mask,target_mask):#x,目标数据的嵌入表示# memory,编码器的输出张量# source_mask,源数据的掩码张量# target_mask,目标数据的掩码张量#要将x依次经历所有的编码器层处理,最后通过规范化层for layer in self.layers:x = layer(x,memory,source_mask,target_mask)return self.norm(x)size = d_model =512
d_ff =64
head=8
dropout=0.2
c=copy.deepcopy
attn = MultiHeadedAttention(head,d_model)
ff=PositionwiseFeedForward(d_model,d_ff,dropout)
layer = DecoderLayer(d_model,c(attn),c(attn),c(ff),dropout)
N=8x=pe_result
memory = en_result
mask=Variable(torch.zeros(2,4,4))
source_mask = target_mask =maskde= Decoder(layer,N)
de_result = de(x,memory,source_mask,target_mask)
print(de_result)
print(de_result.shape)
tensor([[[-6.9816e-01, -3.6691e-01, 9.4570e-01, …, 2.0494e-01,
4.0592e-01, -1.9676e+00],
[ 1.1468e+00, 3.7656e-01, -5.4274e-01, …, -5.8568e-03,
-1.5184e+00, 2.0903e-01],
[ 1.0665e+00, 8.5443e-01, 1.1914e-01, …, 1.0040e+00,
-2.1382e+00, 8.5563e-01],
[ 4.6478e-01, -1.5773e+00, 1.8236e-02, …, 1.3445e+00,
-1.0138e-03, 5.7734e-01]],
[[ 1.4148e+00, 3.9655e-01, -2.4402e-01, …, -2.8617e-01,
1.7083e-02, -2.2609e-01],
[-1.4069e+00, -1.2511e+00, 9.4521e-01, …, 1.6437e-01,
-1.0347e+00, -9.7143e-01],
[ 4.1406e-02, 2.6812e-01, 7.7384e-01, …, -1.8742e+00,
1.5459e+00, -1.5853e-01],
[ 1.7966e-01, -1.9605e-01, -2.1808e-01, …, 4.5682e-01,
8.9362e-01, 1.6721e-01]]], grad_fn=)
torch.Size([2, 4, 512])
17、输出部分实现
17.1、输出部分包含
线性层、softmax层
17.2、线性层作用
通过对上一步的线性变化得到指定维度的输出,也就是转换维度的作用
17.3、softmax层的作用
使最后一维的向量中的数字缩放到0-1的概率值域内,并满足他们的和为1
#输出部分实现
#线性层和softmax层的代码分析
import torch.nn.functional as F
class Generator(nn.Module):def __init__(self,d_model,vocab_size):#d_model,词嵌入的维度# vocab_size,词表的总大小super(Generator, self).__init__()#定义一个线性层,作用是完成网络输出维度的变换self.project = nn.Linear(d_model,vocab_size)def forward(self,x):# x 上一层的输出张量#首先将x送入线性层中,让其经历softmax的处理return F.log_softmax(self.project(x),dim=-1)d_model=512
vocab_size =1000
x=de_resultgen = Generator(d_model,vocab_size)
gen_result = gen(x)
print(gen_result)
print(gen_result.shape)
tensor([[[-7.8733, -7.1830, -7.4383, …, -6.5414, -7.9115, -7.0621],
[-7.4397, -7.2935, -6.5066, …, -6.7869, -6.7470, -7.4885],
[-7.0322, -6.2038, -7.4650, …, -6.6588, -7.6186, -6.5686],
[-6.8242, -6.5199, -8.4175, …, -6.1877, -7.2230, -6.7671]],
[[-6.7454, -7.5659, -6.6611, …, -7.1787, -8.3839, -6.3692],
[-6.7054, -6.3426, -5.6615, …, -7.7665, -7.5928, -7.1189],
[-6.9217, -7.7351, -6.8831, …, -6.1793, -6.9670, -7.6834],
[-8.1110, -7.1037, -6.9986, …, -6.8283, -7.3800, -6.7944]]],
grad_fn=)
torch.Size([2, 4, 1000])
18、模型构建
#模型构建编码器-解码器结构类
class EncoderDecoder(nn.Module):def __init__(self,encoder,decoder,source_embed,target_embed,generator):#encoder,编码器对象# decoder,解码器对象# source_embed,源数据的嵌入函数# target_embed,目标函数的嵌入函数# generator 输出部分类别生成器对象super(EncoderDecoder, self).__init__()self.encoder = encoderself.decoder = decoderself.src_embed=source_embedself.tgt_embed = target_embedself.generator = generatordef forward(self,source,target,source_mask,target_mask):#source,源数据#target,目标数据# source_mask,源数据的掩码张量# target_mask,目标数据的掩码张量return self.decoder(self.encoder(source,source_mask),source_mask,target,target_mask)def encoder(self,source,source_mask):return self.encoder(self.src_embed(source),source_mask)def decoder(self,memory,source_mask,target,target_mask):#memory 经历编码器编码后的输出张量return self.decoder(self.tgt_embed(target),memory,source_mask,target_mask)vocab_size=1000
d_model=512
encoder = en
decoder = de
source_embed = nn.Embedding(vocab_size,d_model)
target_embed = nn.Embedding(vocab_size,d_model)
generator = gensource = target = Variable(torch.LongTensor([[100,2,421,508],[491,998,1,221]]))
source_mask = target_mask = Variable(torch.zeros(8,4,4))ed = EncoderDecoder(encoder,decoder,source_embed,target_embed,generator)
ed_result = ed(source,target,source_mask,target_mask)
print(ed_result)
print(ed_result.shape)def make_model(source_vocab,taget_vocab,N=6,d_model =512,d_ff =2048,head =8,dropout=0.1):#source_vocab,源数据的词汇总数# taget_vocab,目标数据的词汇总数# N=6,编码器和解码器堆叠的层数# d_model =512,词嵌入的维度# d_ff =2048,前馈全连接层中变换矩阵的维度# head =8,多头注意力机制中的头数# dropout=0.1置零的比率c = copy.deepcopy#实例化一个多头注意力的类attn = MultiHeadedAttention(head,d_model)#实例化一个前馈全连接层的网络对象ff = PositionwiseFeedForward(d_model,d_ff,dropout)#实例化一个位置编码器position = PositionalEncoding(d_model,dropout)# 实例化模型model 利用的是EncoderDecoder类#编码器的结构里面有2个子层,attention层和前馈全连接层#解码器的结构有3个子层,两个attention层和前馈全连接层model = EncoderDecoder(Encoder(EncoderLayer(d_model,c(attn),c(ff),dropout),N),Decoder(DecoderLayer(d_model,c(attn),c(attn),c(ff),dropout),N),nn.Sequential(Embeddings(d_model,source_vocab),c(position)),nn.Sequential(Embeddings(d_model, taget_vocab), c(position)),Generator(d_model,target_vocab))#初始化整个模型中的参数,判断参数维度大于1,将矩阵初始化成一个服从均匀分布的矩阵for p in model.parameters():if p.dim() > 1:nn.init.xavier_uniform(p)return modelsource_vocab=11
target_vocab =11
N = 6if __name__ == '__main__':res = make_model(source_vocab,target_vocab,N)print(res)
EncoderDecoder(
(encoder): Encoder(
(layers): ModuleList(
(0): EncoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(1): EncoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(2): EncoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(3): EncoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(4): EncoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(5): EncoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
)
(norm): LayerNorm()
)
(decoder): Decoder(
(layers): ModuleList(
(0): DecoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(src_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(2): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(1): DecoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(src_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(2): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(2): DecoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(src_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(2): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(3): DecoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(src_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(2): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(4): DecoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(src_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(2): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(5): DecoderLayer(
(self_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(src_attn): MultiHeadedAttention(
(linears): ModuleList(
(0): Linear(in_features=512, out_features=512, bias=True)
(1): Linear(in_features=512, out_features=512, bias=True)
(2): Linear(in_features=512, out_features=512, bias=True)
(3): Linear(in_features=512, out_features=512, bias=True)
)
(dropout): Dropout(p=0.1, inplace=False)
)
(feed_forward): PositionwiseFeedForward(
(w1): Linear(in_features=512, out_features=2048, bias=True)
(w2): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(sublayer): ModuleList(
(0): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(2): SublayerConnection(
(norm): LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
)
(norm): LayerNorm()
)
(src_embed): Sequential(
(0): Embeddings(
(lut): Embedding(11, 512)
)
(1): PositionalEncoding(
(dropout): Dropout(p=0.1, inplace=False)
)
)
(tgt_embed): Sequential(
(0): Embeddings(
(lut): Embedding(11, 512)
)
(1): PositionalEncoding(
(dropout): Dropout(p=0.1, inplace=False)
)
)
(generator): Generator(
(project): Linear(in_features=512, out_features=11, bias=True)
)
)
19、模型的基本测试
19.1、copy任务介绍
任务描述:针对数字序列进行学习,学习的最终目标是使输出与输入的序列相同。如输入[1,5,8,9,3],输出也是[1,5,8,9,3].
任务意义:copy任务在模型基础测试中具有重要意义,因为copy操作对于模型来讲是一条明显的规律,因此模型能否在短时间内,小数据集中学会它,可以帮助我们断定模型所有过程是否正常,是否已具备基本学习能力。
19.2、使用copy任务进行模型四步曲
1、构建数据集生成器
2、获得Transformer模型及其优化器和损失函数
3、运行模型进行训练和评估
4、使用模型进行贪婪解码
19.3、代码
#模型基本测试运行
# 1、构建数据集生成器from pyitcast.transformer_utils import Batchdef data_generator(V,batch,num_batch):#V,随机生成数据的最大值+1# batch,每次输送给模型的样本数量,经历这些样本训练后进行一次参数的更新# num_batch:一共输送模型多少数据for i in range(num_batch):#使用numpy中的random.randint()来随机生成[1,v)#分布形状(batch,10)data = torch.from_numpy(np.random.randint(1,V,size=(batch,10)))#将数据的第一列全部设置为1,作为起始标志data[:,0]=1#因为是copy任务,所以源数据和目标数据完全一致#设置参数requires_grad = False,样本的参数不需要参与梯度的计算source = Variable(data,requires_grad = False)target = Variable(data,requires_grad = False)yield Batch(source,target)V=11
batch =20
num_batch =30if __name__ == '__main__':res = data_generator(V,batch,num_batch)print(res)# 2、获得Transformer模型及其优化器和损失函数
from pyitcast.transformer_utils import get_std_opt #标准化
from pyitcast.transformer_utils import LabelSmoothing #平滑包
from pyitcast.transformer_utils import SimpleLossCompute #交叉熵损失函数#使用make_model()函数获得模型的实例化对象
model = make_model(V,V,N=2)
#使用工具包get_std_opt获得模型优化器
model_optimizer = get_std_opt(model)
#使用工具包LabelSmooting获得标签平滑对象
criterion = LabelSmoothing(size = V,padding_idx=0,smoothing=0.0)#使用工具包SimpleLossCompute 获得利用标签平滑的结果得到的损失计算方法
loss = SimpleLossCompute(model.generator,criterion,model_optimizer)# 3、运行模型进行训练和评估
from pyitcast.transformer_utils import run_epochdef run(model,loss,epochs=10):#model,将要训练的模型# loss,使用的损失计算方法# epochs 模型训练的轮次数for epoch in range(epochs):#首先进入训练模式,所有的参数将会被更新model.train()#训练时,传入的batch_size是20run_epoch(data_generator(V,8,20),model,loss)#训练结束后,进入评估模型,所有参数固定不变model.eval()#评估时,传入的batch_size是5run_epoch(data_generator(V,8,5),model,loss)if __name__ == '__main__':run(model,loss)# 4、使用模型进行贪婪解码
#贪婪解码的方式是每次预测都选择概率最大的结果作为输出,它不一定能全局最优性,但却拥有最高的执行效率
from pyitcast.transformer_utils import greedy_decodedef run(model,loss,epochs =10):for epoch in range(epochs):#首先进入训练模式,所有的参数将会被更新model.train()run_epoch(data_generator(V,8,20),model,loss)#训练结束后,进入评估模式,所有参数固定不变model.eval()run_epoch(data_generator(V,8,5),model,loss)#跳出for循环后,代表模型训练结束,进入评估模式model.eval()#初始化一个输入张量source = Variable(torch.LongTensor([[1,3,2,5,4,6,7,8,9,10]]))#初始化一个输入张量的掩码张量,全1代表没有任何的遮掩source_mask = Variable(torch.ones(1,1,10))#设定解码的最大长度max_len等于10,起始数字的标志默认等于1result = greedy_decode(model,source,source_mask,max_len=10,start_symbol=1)print(result)if __name__ == '__main__':run(model,loss)