当前位置: 首页 > article >正文

【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 =1

class 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 =2

    def 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 +=1

name = '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 s

s="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, pairs

lang1 = '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,pairs

input_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_size
        self.hidden_size = hidden_size
        #实例化Embedding层,输入参数分别是词表单词总数,和词嵌入的维度
        self.embedding = nn.Embedding(input_size,hidden_size)
        #实例化GRU,参数也是hidden_size
        self.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,hidden

    def initHidden(self):
        #将隐藏层张量初始化为1*1*self.hidden_size大小的张量
        return torch.zeros(1,1,self.hidden_size,device=device)

hidden_size =25
input_size =20

input = 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_size
        self.output_size = output_size
        #实例化Embedding对象,输入参数分别是目标语言的单词总数,和词嵌入的维度
        self.embedding = nn.Embedding(output_size,hidden_size)
        #实例化GRU对象
        self.gru = nn.GRU(hidden_size,hidden_size)
        #实例化线性层的对象,对GRU的输出做线性变换,得到希望的输出尺寸output_size
        self.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,hidden

    def initHidden(self):
        #初始化隐藏层张量,形状为1*1*self.hidden_size
        return 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)

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_size
        self.output_size = output_size
        self.dropout_p = dropout_p
        self.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_weights

    def initHidden(self):
        #初始化一个全零的隐藏层张量,形状为1*1*self.hidden_size
        return 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.5

def 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_size
    encoder_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_forcing
    use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False

    #如果使用teacher_forcing
    if 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_forcing
    else:
        #遍历目标张量,进行解码
        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 math

def timeSince(since):
    now = time.time()
    s = now - since
    m=math.floor(s/60)
    s-=m*60
    return '%dm %ds' % (m,s)

since = time.time()-10*60

period = 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 += loss
        plot_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 = 500

if __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_size
        encoder_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_output
            encoder_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>')
                break
            else:
                #要根据索引去将真实的字符添加进结果列表中
                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以及每一个时间步的隐层张量,逐个生成对应的翻译语言。


http://www.kler.cn/a/273566.html

相关文章:

  • Python毕业设计选题:基于django+vue的4S店客户管理系统
  • CAN总线学习笔记(1、CAN总线定义)
  • PKG_CHECK_MODULES(FUSE,fuse)
  • 学习虚幻C++开发日志——定时器
  • el-upload,上传文件,后端提示信息,前端需要再次重新上传(不用重新选择文件)
  • 只允许指定ip远程连接ssh
  • 行为型-观察者模式
  • 递归方法构建哈夫曼树
  • C语言calloc函数的特点,效率低。但是进行初始化操作
  • V-JEPA模型,非LLM另外的选择,AGI的未来:迈向Yann LeCun先进机器智能(AMI)愿景的下一步
  • IPC之管道
  • Android14之HIDL报错:Invalid sparse file format at header magic(一百九十六)
  • RHCE——三:Web服务器(内网穿透实验)
  • transferto转换文件类型报错
  • Auto-DataProcessing:一组让制作数据集变轻松的脚本
  • 显示android设备所以已安装App 可点击启动、搜索
  • Halcon 点云处理流程(点云分割、连通筛选、模型位姿变换、三角化)
  • Linux命令-dhclient命令(动态获取或释放IP地址)
  • 前后端分离项目部署服务器教程--实践成功
  • 【简历篇】如何写简历(二)简历元素、技巧、规范、三省
  • Git——GitHub远端协作详解
  • Apache Doris 如何基于自增列满足高效字典编码等典型场景需求
  • 在Hive中使用Python编写的UDF函数
  • 全量知识系统 微服务及特征复数空间和立体逻辑方阵的设想及百度AI回复
  • MySql安装与卸载—我耀学IT
  • 小程序云开发(十六):JavaScript基础