本文主要是介绍【NLP6-使用seq2seq模型架构实现英译法任务】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
使用seq2seq模型架构实现英译法任务
目标
1、更深一步了解seq2seq模型架构和翻译数据集
2、掌握适用基于GRU的seq2seq模型实现翻译过程
3、掌握Attention机制在解码器端的实现过程
seq2seq模型架构
encoder编码器 Decoder解码器
数据预览
基于GRU的seq2seq模型架构实现翻译的过程
1、导入必备工具包
from io import open #从io工具包导入open方法
import unicodedata #用于字符规范化
import re #用于正则表达式
import random
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch import optim #torch中预定义的优化方法工具包
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
2、对持久化文件中数据进行处理,以满足模型训练要求
2.1、将指定语言中的词汇映射成数值
# 2、对持久化文件中数据进行处理,以满足模型训练要求
#定义起始标志
SOS_token =0
#定义介绍标志
EOS_token =1class Lang():def __init__(self,name):"""初始化name:参数代表传入某种语言的名字"""self.name = name#初始化单词到索引的映射字典self.word2index = {}#初始化索引到单词的映射字典,其中0,1对应的SOS,EOS已经在字典中了self.index2word ={0:"SOS",1:"EOS"}#初始化词汇对应的数字索引,从2开始,因为0,1已经被开始字符和结束字符占用了self.n_words =2def addSentence(self,sentence):"""添加句子的函数,将整个句子中所有的单词依次添加到字典中因为英文,法文都是空格进行分割的语言,直接进行分词就可以"""for word in sentence.split(' '):self.addWord(word)def addWord(self,word):"""添加单词到类内字典中,将单词转换为数字首先判断word是否已经在self.word2index字典的key中"""if word not in self.word2index:#添加的时候,索引值取当前类中单词的总数量self.word2index[word] = self.n_words#再添加翻转的字典self.index2word[self.n_words]=word#第三步更新类内的单词总数量self.n_words +=1name = 'eng'
sentence ="hello I am Jay"
eng1 = Lang(name)
eng1.addSentence(sentence)print("word2index:",eng1.word2index)
print("index2word:",eng1.index2word)
print("n_words:",eng1.n_words)
word2index: {‘hello’: 2, ‘I’: 3, ‘am’: 4, ‘Jay’: 5}
index2word: {0: ‘SOS’, 1: ‘EOS’, 2: ‘hello’, 3: ‘I’, 4: ‘am’, 5: ‘Jay’}
n_words: 6
2.2、字符规范化
#字符规范化
#将unicode 转为Ascii,我们可以认为是去掉一些语言中的重音标记
def unicodeToAscii(s):return ''.join(c for c in unicodedata.normalize('NFD',s)if unicodedata.category(c) !='Mn')#定义字符串规范化函数
def normalizeString(s):#第一步使字符转变为小写并去除两侧的空白符,再调用上面的函数转换为ASCII字符串s = unicodeToAscii(s.lower().strip())#在.!?前面加一个空格s = re.sub(r"([.!?])",r" \1",s)s = re.sub(r"[^a-zA-Z.!?]+",r" ",s)return ss="Are you kidding me?"nsr = normalizeString(s)
print(nsr)
are you kidding me ?
input_lang: <main.Lang object at 0x000002437ECABDD8>
2.3、将持久化文件中的数据加载到内存,并实例化类Lang
#将持久化文件中的数据加载到内存,并实例化Lang
data_path = 'D:\data\code_and_data\eng-fra.txt'
def readLangs(lang1,lang2):#lang1:源语言的名字 lang2:目标语言的名字# 整个函数返回对应的两个类对象,以及语言对的列表lines = open(data_path,encoding='utf-8').read().strip().split('\n')#对lines列表中的句子进行标准化处理,并以\t进行再次划分,形成子列表pairs =[[normalizeString(s) for s in l.split('\t')] for l in lines]#直接初始化两个类对象input_lang = Lang(lang1)output_lang = Lang(lang2)return input_lang, output_lang, pairslang1 = 'eng'
lang2 ='fra'input_lang, output_lang, pairs = readLangs(lang1, lang2)
print("input_lang:", input_lang)
print("output_lang:", output_lang)
print("pairs[:5]:",pairs[:5])
pairs[:5]: [[‘go .’, ‘va !’], [‘run !’, ‘cours !’], [‘run !’, ‘courez !’], [‘wow !’, ‘ca alors !’], [‘fire !’, ‘au feu !’]]
2.4、过滤出符合我们要求的语言对
#设置组成句子中单词或标点的最多个数
MAX_LENGTH =10#选择带有指定前缀的英文源语言的语句作为训练数据
eng_prefixes =("i am ","i m","he is","he s","she is ","she s","you are ","you re","they are ","they re"
)#过滤语言对的具体逻辑函数
def filterPair(pair):return len(pair[0].split(' '))<MAX_LENGTH and \pair[0].startswith(eng_prefixes) and \len(pair[1].split(' ')) < MAX_LENGTH#过滤语言对的函数
def filterPairs(pairs):return [pair for pair in pairs if filterPair(pair)]fpairs = filterPairs(pairs)
print("fpairs[:5]:",fpairs[:5])
fpairs[:5]: [[‘i m .’, ‘j ai ans .’], [‘i m ok .’, ‘je vais bien .’], [‘i m ok .’, ‘ca va .’], [‘i m fat .’, ‘je suis gras .’], [‘i m fat .’, ‘je suis gros .’]]
2.5、对以上数据准备函数进行整合,并使用类Lang对语言对进行数值映射
#整合数据预处理的函数
def prepareData(lang1,lang2):#lang1:代表源语言的名字,英文#lang2:代表目标语言的名字,法文#通过调用readlangs()函数得到两个类对象,并得到字符串类型的语言对的列表input_lang,output_lang,pairs = readLangs(lang1,lang2)#对字符串类型的列表进行过滤操作pairs = filterPairs(pairs)#对过滤后的语言对列表进行遍历操作,添加进类对象中for pair in pairs:input_lang.addSentence(pair[0])output_lang.addSentence(pair[1])#返回数值映射后的类对象,以及过滤后的语言对列表return input_lang,output_lang,pairsinput_lang,output_lang,pairs=prepareData('eng','fra')
print("input_n_words:",input_lang.n_words)
print("output_n_words:",output_lang.n_words)
print(random.choice(pairs))
input_n_words: 3082
output_n_words: 4552
[‘you re going to ruin your eyes .’, ‘vous allez vous bousiller les yeux .’]
2.6、将语言对转化维模型输入需要的张量
#将语言对转化为模型输入需要的张量
def tensorFromSentence(lang,sentence):indexes = [lang.word2index[word] for word in sentence.split(' ')]indexes.append(EOS_token)return torch.tensor(indexes,dtype=torch.long,device=device).view(-1,1)def tensorsFromPair(pair):input_tensor = tensorFromSentence(input_lang,pair[0])output_tensor = tensorFromSentence(output_lang,pair[1])return (input_tensor,output_tensor)pair = pairs[0]
pair_tensor = tensorsFromPair(pairs[0])
print(pair_tensor)
(tensor([[2],
[3],
[4],
[1]]), tensor([[2],
[3],
[4],
[5],
[1]]))
3、构建基于GRU的编码器和解码器
# 3、构建基于GRU的编码器和解码器
#编码器
class EncoderRNN(nn.Module):def __init__(self,input_size,hidden_size):#input_size:代表编码器输入尺寸,就是英文的词表大小#hidden_size:代表GRU的隐藏层神经单元数,同时也是词嵌入的维度super(EncoderRNN, self).__init__()#将参数传入类中self.input_size = input_sizeself.hidden_size = hidden_size#实例化Embedding层,输入参数分别是词表单词总数,和词嵌入的维度self.embedding = nn.Embedding(input_size,hidden_size)#实例化GRU,参数也是hidden_sizeself.gru = nn.GRU(hidden_size,hidden_size)def forward(self,input,hidden):#input:代表源语言中的输入张量#hidden:代表初始化的隐藏层张量#注意:经过Embedding处理后,张量是一个二维张量,但是GRU要求输入是三维张量#所以要对结果进行扩展维度 view(),同时让任意单词映射后的尺寸是(1,Embedding)output =self.embedding(input).view(1,1,-1)output,hidden = self.gru(output,hidden)return output,hiddendef initHidden(self):#将隐藏层张量初始化为1*1*self.hidden_size大小的张量return torch.zeros(1,1,self.hidden_size,device=device)hidden_size =25
input_size =20input = pair_tensor[0][0]
hidden = torch.zeros(1,1,hidden_size)encoder = EncoderRNN(input_size,hidden_size)
encoder_output,hidden = encoder(input,hidden)
print(encoder_output)
tensor([[[-2.8290e-01, -9.6259e-02, 2.5239e-01, -5.4805e-01, 1.4647e-01,
-4.3992e-02, 1.9509e-01, 2.4348e-01, 3.6683e-01, -5.5452e-02,
4.6634e-02, 1.5199e-01, 2.5359e-02, 3.4754e-01, 7.4054e-02,
8.3258e-02, 1.8888e-01, 3.2992e-01, 1.1495e-01, -2.5253e-01,
-4.0492e-02, 8.2203e-02, 4.5562e-04, 8.7427e-02, 1.9541e-01]]],
grad_fn=)
#解码器
class DecoderRNN(nn.Module):def __init__(self,hidden_size,output_size):#hidden_size:代表隐藏层的神经元个数,同时也是解码器的输入尺寸#output_size:代表整个解码器的输出尺寸,指定的尺寸也就是目标语言的单词总数super(DecoderRNN,self).__init__()#将参数传入类中self.hidden_size = hidden_sizeself.output_size = output_size#实例化Embedding对象,输入参数分别是目标语言的单词总数,和词嵌入的维度self.embedding = nn.Embedding(output_size,hidden_size)#实例化GRU对象self.gru = nn.GRU(hidden_size,hidden_size)#实例化线性层的对象,对GRU的输出做线性变换,得到希望的输出尺寸output_sizeself.out = nn.Linear(hidden_size,output_size)#最后进入softmax的处理self.softmax=nn.LogSoftmax(dim=1)def forward(self,input,hidden):#input:代表目标语言的输入张量#hidden:代表初始化的GRU隐藏层张量#经历了Embedding层处理后,要将张量形状改变为三维张量output = self.embedding(input).view(1,1,-1)#适用relu函数对输出进行处理,使得Embedding矩阵更稀疏,防止过拟合output=F.relu(output)#将张量传入GRU解码器output,hidden = self.gru(output,hidden)#经历GRU处理后的张量是三维张量,但是全连接层需要二位张量,利用output[0]来降维output=self.softmax(self.out(output[0]))return output,hiddendef initHidden(self):#初始化隐藏层张量,形状为1*1*self.hidden_sizereturn torch.zeros(1,1,self.hidden_size,device=device)hidden_size=25
output_size =10input1 = pair_tensor[1][0]
hidden=torch.zeros(1,1,hidden_size)decoder = DecoderRNN(hidden_size,output_size)
output,hidden = decoder(input1,hidden)
print(output)
print(output.shape)
tensor([[-2.3544, -2.3464, -2.1223, -2.2558, -2.2278, -2.4300, -2.5363, -2.3480,
-2.3790, -2.1068]], grad_fn=)
torch.Size([1, 10])
构建基于GRU和Attention的解码器
#构建基于GRU和Attention的解码器class AttnDecoderRNN(nn.Module):def __init__(self,hidden_size,output_size,dropout_p=0.1,max_length=MAX_LENGTH):#hidden_size:代表解码器的GRU输出尺寸,就是隐藏层神经元个数#output_size:指定的网络输出尺寸,代表目标语言的词汇总数#dropout_p:使用dropout层的置零比例#max_length:代表句子的最大长度super(AttnDecoderRNN, self).__init__()#将参数传入类中self.hidden_size = hidden_sizeself.output_size = output_sizeself.dropout_p = dropout_pself.max_length=max_length#实例化一个Embedding对象,参数是目标语言的词汇总数和词嵌入的维度self.embedding = nn.Embedding(output_size,hidden_size)#实例化第一个注意力层,注意输入是两个张量的合并self.attn=nn.Linear(self.hidden_size * 2,self.max_length)#实例化第二个注意力层,注意输入也是两个张量的合并,同时输出要进入GRU中self.attn_combine = nn.Linear(self.hidden_size * 2 ,self.hidden_size)#实例化一个nn.Dropout(self.dropout_p)self.dropout = nn.Dropout(self.dropout_p)#实例化GRU单元self.gru = nn.GRU(self.hidden_size,self.hidden_size)#实例化GRU之后的线性层,作为整个解码器的输出self.out =nn.Linear(self.hidden_size,self.output_size)def forward(self,input1,hidden,encoder_output):#input1:源数据的输入张量#hidden:初始化的隐藏层张量#encoder_output:代表编码器的输出张量#对输入input1 进行词嵌入处理,并扩展维度embedded = self.embedding(input1).view(1,1,-1)#紧接着将其输入dropout层,防止过拟合embedded=self.dropout(embedded)#在进行第一个注意力层处理前,要将Q,K进行纵轴拼接attn_weights = F.softmax(self.attn(torch.cat((embedded[0],hidden[0]),1)),dim=1)#进行bmm操作,注意将二位张量扩展成三维张量attn_applied = torch.bmm(attn_weights.unsqueeze(0),encoder_output.unsqueeze(0))#再次进行拼接,顺便要进行一次降维output = torch.cat((embedded[0],attn_applied[0]),1)#将output输入第二个注意力层output=self.attn_combine(output).unsqueeze(0)#使用relu进行激活层处理output=F.relu(output)#将激活后的张量,连同隐藏层张量,一起传入GRU中output,hidden = self.gru(output,hidden)#最后将结果先降维,然后线性层梳理成指定的输出维度,最后经过softmax处理output=F.log_softmax(self.out(output[0]),dim=1)#返回解码器的最终输出结果,最后的隐藏层张量,注意力权重张量return output,hidden,attn_weightsdef initHidden(self):#初始化一个全零的隐藏层张量,形状为1*1*self.hidden_sizereturn torch.zeros(1,1,self.hidden_size,device=device)#实例化参数
hidden_size=25
output_size=10#输入参数
input1= pair_tensor[1][0]
hidden = torch.zeros(1,1,hidden_size)
encoder_output = torch.randn(10,25)#调用
decoder= AttnDecoderRNN(hidden_size,output_size)
output,hidden,attn_weights = decoder(input1,hidden,encoder_output)
print(output)
print(output.shape)
print(hidden.shape)
print(attn_weights)
print(attn_weights.shape)
tensor([[-2.3155, -2.0316, -2.4451, -2.2536, -2.5503, -2.3917, -2.0801, -2.4474,
-2.2222, -2.4182]], grad_fn=)
torch.Size([1, 10])
torch.Size([1, 1, 25])
tensor([[0.0789, 0.0532, 0.1400, 0.0623, 0.3510, 0.1217, 0.0299, 0.0812, 0.0376,
0.0441]], grad_fn=)
torch.Size([1, 10])
4、构建模型训练函数,并进行训练
什么是teacher_forcing:它是一种用于序列生成任务的训练技巧,在seq2seq架构中,根据循环神经网络,解码器每次应该使用上一步的结果作为输入的一部分,但是训练过程中,一旦上一步的结果是错误的,就会导致这种错误被累积,无法达到训练效果。因此,我们需要一种机制改变上一步出错的情况。因为训练时我们是已知争取的输出应该是什么,因此可以强制将上一步结果设置成正确的输出,这种方式叫做teacher_forcing
teacher_forcing作用:能够在训练的时候矫正模型的预测,避免在序列生成的过程中误差进一步放大。teacher_forcing能够极大的加快模型的收敛速度,令训练过程更快更平稳
构建训练函数train
构建时间计算函数timesince
调用训练函数并打印日志和制图
损失曲线分析:一直下降的损失曲线,说明模型正在收敛,能够从数据中找到一些规律应用于数据
# 4、构建模型训练函数,并进行训练
#设定teacher_forcing的比率,在多大的概率下使用这个策略进行训练
teacher_forcing_ratio =0.5def train(input_tensor,target_tensor,encoder,decoder,encoder_optimizer,decoder_optimizer,criterion,max_length=MAX_LENGTH):#input_tensor:源语言的输入张量#target_tensor:目标语言的输入张量#encoder:编码器的实例化对象 decoder:解码器的实例化对象#encoder_optimizer 编码器优化器 decoder_optimizer :解码器优化器#criterion:损失函数 max_length:代表句子的最大长度#初始化编码器的隐藏层张量encoder_hidden=encoder.initHidden()#训练前将编码器和解码器的优化器梯度归零encoder_optimizer.zero_grad()decoder_optimizer.zero_grad()#根据源文本和目标文本张量获得对应的长度input_length = input_tensor.size(0)target_length = target_tensor.size(0)#初始化编码器的输出矩阵张量,形状是max_length*encoder.hidden_sizeencoder_outputs = torch.zeros(max_length,encoder.hidden_size,device=device)#设置初始损失值loss =0#遍历输入张量for ei in range(input_length):encoder_output,encoder_hidden = encoder(input_tensor[ei],encoder_hidden)#每一个轮次的输出encoder_output是三维张量,使用[0,0]进行降维到一维列表,赋值给输出张量encoder_outputs[ei]=encoder_output[0,0]#初始化解码器的第一个输入字符decoder_input = torch.tensor([[SOS_token]],device=device)#初始化解码器的隐藏层张量,赋值给最后一次编码器的隐藏层张量decoder_hidden = encoder_hidden#判断是否使用teacher_forcinguse_teacher_forcing = True if random.random() < teacher_forcing_ratio else False#如果使用teacher_forcingif use_teacher_forcing:#遍历目标张量,进行解码for di in range(target_length):decoder_output,decoder_hidden,decoder_attention = decoder(decoder_input,decoder_hidden,encoder_outputs)#使用损失函数计算损失值,并进行累加loss +=criterion(decoder_output,target_tensor[di])#因为使用了teacher_forcing,所以将下一步的解码器输入强制设定为“正确的答案”decoder_input=target_tensor[di]#如果不适用teacher_forcingelse:#遍历目标张量,进行解码for di in range(target_length):decoder_output,decoder_hidden,decoder_attention = decoder(decoder_input,decoder_hidden,encoder_outputs)#预测值变成输出张量中概率最大的那一个topv,topi = decoder_output.topk(1)#使用损失函数计算损失值,并进行累加loss+=criterion(decoder_output,target_tensor[di])#如果某一步的解码结果是句子终止符号,则解码直接结束,跳出循环if topi.squeeze().item()==EOS_token:break#下一步解码器的输入要设定为当前步最大概率值的那一个decoder_input=topi.squeeze().detach()#应用反向传播进行梯度计算loss.backward()#利用编码器和解码器的优化器进行参数的更新encoder_optimizer.step()decoder_optimizer.step()#返回平均损失return loss.item()/target_length#构建时间函数
import time
import mathdef timeSince(since):now = time.time()s = now - sincem=math.floor(s/60)s-=m*60return '%dm %ds' % (m,s)since = time.time()-10*60period = timeSince(since)
print(period)#调用训练函数并打印日志和制图
import matplotlib.pyplot as plt
def trainIters(encoder,decoder,n_iters,print_every=1000,plot_every=100,learning_rate =0.01):#encoder:编码器实例化对象# decoder:解码器实例化对象# n_iters:训练总迭代步数# print_every:每隔多少轮次进行一次训练日志的打印# plot_every :每隔多少轮次进行一次损失值的添加,为了后续绘制损失曲线# learning_rate :学习率#获取训练开始时间start = time.time()#初始化存放平均损失值的列表plot_losses =[]#每隔打印间隔的总损失值print_loss_total =0#每个绘制曲线损失值的列表plot_loss_total =0#定义编码器和解码器的优化器encoder_optimizer = optim.SGD(encoder.parameters(),lr=learning_rate)decoder_optimizer = optim.SGD(decoder.parameters(),lr=learning_rate)#定义损失函数criterion = nn.NLLLoss()#按照设定的总迭代次数进行迭代训练for iter in range(1,n_iters+1):#每次从语言对的列表中随机抽取一条样本作为本轮迭代的训练数据training_pair = tensorsFromPair(random.choice(pairs))#依次将选取出来的语句对作为输入张量,和输出张量input_tensor = training_pair[0]target_tensor = training_pair[1]#调用train()函数获得本轮迭代的损失值loss = train(input_tensor,target_tensor,encoder,decoder,encoder_optimizer,decoder_optimizer,criterion)#将本轮迭代的损失值进行累加print_loss_total += lossplot_loss_total += loss#如果到达了打印的轮次if iter % print_every == 0:#首先获取本次打印的平均损失值print_loss_avg = print_loss_total/print_every#为了下一个打印间隔的累加,这里将累加器清零print_loss_total = 0#打印若干信息print('%s (%d %d%%) %.4f' % (timeSince(start),iter,iter / n_iters * 100, print_loss_avg))#如果到达了绘制损失曲线的轮次if iter % plot_every == 0:#首先获取本次损失添加的平均损失值plot_loss_avg = plot_loss_total / plot_every#将平均损失值添加进最后的列表plot_losses.append(plot_loss_avg)#为了下一个添加损失值的累计,这里将累加器清零plot_loss_total = 0#绘制损失曲线plt.figure()plt.plot(plot_losses)#plt.savefig("D:\data\s2s.png")#输入参数
hidden_size=256
encoder1 = EncoderRNN(input_lang.n_words,hidden_size).to(device)
attn_decoder1 = AttnDecoderRNN(hidden_size,output_lang.n_words,dropout_p=0.1).to(device)
#attn_decoder1 = AttnDecoderRNN(hidden_size,output_lang.n_words).to(device)n_iters = 5000
print_every = 500if __name__ == '__main__':trainIters(encoder1,attn_decoder1,n_iters,print_every=print_every)#trainIters(encoder1,attn_decoder1,n_iters)
0m 35s (500 10%) 4.4319
1m 12s (1000 20%) 4.0590
1m 49s (1500 30%) 3.7745
2m 28s (2000 40%) 3.6969
3m 5s (2500 50%) 3.5634
3m 44s (3000 60%) 3.4102
4m 21s (3500 70%) 3.4046
5m 0s (4000 80%) 3.3699
5m 38s (4500 90%) 3.2801
6m 17s (5000 100%) 3.1754
5、构建模型评估函数,并进行测试以及Attention效果分析
5.1、构建模型评估函数evaluate
def evaluate(encoder,decoder,sentence,max_length=MAX_LENGTH):#encoder:编码器#decoder:解码器#sentence:待评估的源语句#max_length:句子最大长度with torch.no_grad():#对输入语句进行张量表示input_tensor = tensorFromSentence(input_lang,sentence)#获得输入句子长度input_length = input_tensor.size(0)#初始化编码器的隐藏层张量encoder_hidden = encoder.initHidden()#初始化编码器的输出张量,矩阵的形状max_length * hidden_sizeencoder_outputs= torch.zeros(max_length,encoder.hidden_size,device=device)#遍历输入张量for ei in range(input_length):#循环进入编码器的处理encoder_output,encoder_hidden = encoder(input_tensor[ei],encoder_hidden)#将三维张量的输出先进行降维到一维,然后赋值给encoder_outputencoder_outputs[ei] = encoder_output[0,0]#初始化解码器的第一个输入,就是起始字符decoder_input = torch.tensor([[SOS_token]],device=device)#初始化解码器的隐藏层输入decoder_hidden = encoder_hidden#初始化预测词汇的列表decoded_words =[]#初始化一个attention张量decoder_attentions = torch.zeros(max_length,max_length)#遍历解码for di in range(max_length):#将张量送入解码器处理decoder_output,decoder_hidden,decoder_attention = decoder(decoder_input,decoder_hidden,encoder_outputs)#首先将注意力张量保存decoder_attentions[di] = decoder_attention.data#topv,topi = decoder_output.data.topk(1)#如果解析出的是结束字符if topi.item()== EOS_token:#将结束字符添加到结果列表中,并退出解码循环decoded_words.append('<EOS>')breakelse:#要根据索引去将真实的字符添加进结果列表中decoded_words.append(output_lang.index2word[topi.item()])#最后一步将预测的标签赋值给下一步解码器的输入decoder_input = topi.squeeze().detach()return decoded_words,decoder_attentions[:di + 1]
5.2、随机选择指定数量的数据进行评估
def evaluateRandomly(encoder,decoder,n=6):for i in range(n):pair = random.choice(pairs)print('>', pair[0])print('=', pair[1])output_words ,attentions = evaluate(encoder,decoder,pair[0])output_sentence = ' '.join(output_words)print('<',output_sentence)print(' ')if __name__ == '__main__':evaluateRandomly(encoder1,attn_decoder1)
5.3、进行了Attention可视化分析
sentence="we re both teachers ."
output_words,attention = evaluate(encoder1,attn_decoder1,sentence)
print(output_words)
plt.figure()
plt.matshow(attention.numpy())
he saved us all .
= il nous a toutes sauvees .
< il a a de . .
they are russian .
= ils sont russes .
< ils sont en .
they re babies .
= ce sont des bebes .
< ils sont sont .
i m very strict .
= je suis tres strict .
< je suis tres en . .
she s eating for two .
= elle mange comme deux .
< elle est en train .
i m concentrating .
= je suis en train de me concentrer .
< je suis en .
[‘tu’, ‘es’, ‘vraiment’, ‘de’, ‘.’, ‘’]
分析:
Attention图像的纵坐标代表输入的源语言各个词汇对应的索引,0-6分别对应[“we”, “re”," both", “teachers”, “.”,“”],纵坐标代表生成的目标语言各个词汇对应的索引,0-7代表[‘nous’,‘sommes’,‘toutes’,‘deux’,‘enseignantes’,‘.’,"],图中浅色小方块(颜色越浅说明影响越大)代表词汇之间的影响关系,比如源语言的第1个词汇对生成目标语言的第1个词汇影响最大,源语言的第4、5个词对生成目标语言的第5个词会影响最大,通过这样的可视化图形,我们可以知道Attention的效果好坏,与我们人为去判定到底还有多大的差距,进而衡量我们训练模型的可用性。
seq2seq模型架构分析
从图中可知,seq2seq模型架构,包括两部分分别是encoder(编码器)和decoder(解码器)。编码器和解码器的内部实现都使用了GRU模型,这里它要完成的是一个中文到英文的翻译:欢迎 来 北京 —>welcome to beijing 。编码器首先处理中文输入"欢迎来北京",通过GRU模型获得每个时间步的输出张量,最后将它们拼接成一个中间语义张量C,接着解码器将使用这个中间语义张量c以及每一个时间步的隐层张量,逐个生成对应的翻译语言。
这篇关于【NLP6-使用seq2seq模型架构实现英译法任务】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!