一、模型设计
模型的构建需要依次实现其嵌入层(embedding)、编码器(encoder)、解码器(decoder)等部分。下面以python 3.6 + pytorch 1.3.1为例,给出了各部分的核心代码。
代码4-1 基于GRU的编码器
class En服务器托管网coderRNN(nn.Module):def __init__(self, input_size, hidden_size):super(EncoderRNN, self).__init__()self.hidden_size = hidden_sizeself.embedding = nn.Embedding(input_size, hidden_size)self.gru = nn.GRU(hidden_size, hidden_size) #编码器前向逻辑函数def forward(self, input, hidden):output = self.embedding(input).view(1, 1, -1)output, hidden = self.gru(output, hidden)return output, hidden#初始化隐层张量函数def initHidden(self):return torch.zeros(1, 1, self.hidden_size)
代码4-1中,input_size代表编码器的输入尺寸,即源语言英文的词表大小;hidden_size代表GRU的隐藏层神经单元数, 也是词嵌入维度。首先,我们需要将参数hidden_size传入类中,然后实例化Embedding层,输入参数分别是词表单词总数和词嵌入的维度,再实例化GRU,输入参数是hidden_size。
在编码器前向逻辑函数中,参数input代表源语言的输入张量,参数hidden代表初始化的隐藏层张量。经过Embedding处理后,张量是二维的,由于gru要求输入三维张量,所以要对结果拓展维度,同时让任意单词映射后的尺寸是[1,embedding],将经过维度拓展的结果与hidden传入GRU单元中得到返回结果。除此之外,还需要对隐藏层张量大小进行初始化。
| 代码4-2 基于GRU和Attention的解码器 “`
class AttnDecoderRNN(nn.Module):def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):super(AttnDecoderRNN, self).__init__()self.hidden_size = hidden_sizeself.output_size = output_sizeself.dropout_p = dropout_pself.max_length = max_lengthself.embedding = nn.Embedding(self.output_size, self.hidden_size)self.attn = nn.Linear(self.hidden_size * 2, self.max_length)self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)self.dropout = nn.Dropout(self.dropout_p)self.gru = nn.GRU(self.hidden_size, self.hidden_size)self.out = nn.Linear(self.hidden_size, self.output_size) #解码器前向逻辑函数def forward(self, input, hidden, encoder_outputs):embedded = self.embedding(input).view(1, 1, -1)embedded = self.dropout(embedded)attn_weights = F.softmax(self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)attn_applied = torch.bmm(attn_weights.unsqueeze(0), encoder_outputs.unsqueeze(0))output = torch.cat((embedded[0], attn_applied[0]), 1)output = self.attn_combine(output).unsqueeze(0)output = F.relu(output)output, hidden = self.gru(output, hidden)output = F.log_softmax(self.out(output[0]), dim=1)return output, hidden, attn_weights #初始化隐层张量函数def initHidden(self):return torch.zeros(1, 1, self.hidden_si服务器托管网ze)
代码4-2中,hidden_size代表解码器GRU的隐藏层神经单元数, 也是解码器输入尺寸。output_size代表解码器的输出尺寸,即目标语言法文的词表大小;output_size代表解码器的输出尺寸,即目标语言法文的词表大小;dropout_p代表使用dropout层时的置零比率,max_length代表句子的最大长度。
首先,我们需要将参数传入类中,接下来实例化Embedding层,输入参数分别是目标语言单词总数和词嵌入的维度,再实例化两个attention层,第二个attention层的输出要进入GRU中,再实例化dropout层,然后实例化GRU,输入参数是hidden_size。再实例化线性层对象,对GRU的输出做线性变换,得到期待的输出尺寸output_size;最后使用softmax进行处理,以便于分类。
在解码器前向逻辑函数中,参数input代表目标语言的输入张量,参数hidden代表初始化的隐藏层张量,参数encoder_output代表编码器的输出张量。与编码器相同,将经过Embedding处理后的张量维度拓展为三维,使用dropout进行随机丢弃,防止过拟合。再进行attention层处理。attention结构的结果使用relu激活,将激活后的结果作为GRU的输入和hidden一起传入解码器GRU。最后将结果降维并使用softmax处理得到最终的结果。除此之外,还需要对隐藏层张量大小进行初始化。
二、模型训练
| 代码5-1 损失计算与优化训练
| if use_teacher_forcing:for di in range(target_length):loss += criterion(decoder_output, target_tensor[di])decoder_input = target_tensor[di]else:for di in range(target_length):topv, topi = decoder_output.topk(1)loss += criterion(decoder_output, target_tensor[di])if topi.squeeze().item() == EOS_token:Breakdecoder_input = topi.squeeze().detach()encoder_optimizer.step()decoder_optimizer.step()# 使用预定义的SGD作为优化器,将参数和学习率传入其中 encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate) decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)# 选择损失函数 criterion = nn.NLLLoss() |
代码5-1中,target_length是目标文本张量获得对应的长度,target_tensor为目标语言输入张量,encoder_optimizer,decoder_optimizer为编码器,criterion指损失函数计算方法,通过总损失除以间隔得到平均损失。
训练过程中使用了teacher_forcing,它是一种用于序列生成任务的训练技巧,在seq2seq架构中,根据循环神经网络理论,解码器每次应该使用上一步的结果作为输入的一部分,但是训练过程中,一旦上一步的结果是错误的,就会导致这种错误被累积,无法达到训练效果,因此,我们需要一种机制改变上一步出错的情况,因为训练时我们是已知正确的输出应该是什么,因此可以强制将上一步结果设置成正确的输出,这种方式就叫做teacher_forcing。
训练时teacher_forcing比率设置为0.5,也就是有50%的概率使用teacher_forcing。如果使用了teacher_forcing,无论解码器输出的decoder_output是什么,我们都只使用‘正确的答案’,即target_tensor[di]来计算损失,并强制将下一次的解码器输入设置为‘正确的答案’。如果不使用teacher_forcing,损失计算仍然使用decoder_output和target_tensor[di],下一次的解码器输入即当前步最大概率值的那个。
训练参数设置如代码5-2所示:
| 代码5-2 参数设置
| #设置隐层大小为256 ,也是词嵌入维度 hidden_size = 256 # input_lang.n_words输入词汇总数2803,与hidden_size一同传入EncoderRNN类中#得到编码器对象encoder1 encoder1 = EncoderRNN(input_lang.n_words, hidden_size) # output_lang.n_words获取目标词汇总数4345,与hidden_size和dropout_p一同传入AttnDecoderRNN类中#得到解码器对象attn_decoder1 attn_decoder1 = AttnDecoderRNN(hidden_size, output_lang.n_words, dropout_p=0.1)#学习率learning_rate=0.01# 设置迭代步数 n_iters = 75000 # 设置日志打印间隔 print_every = 5000 |
训练输出结果如图5所示:
图5 训练结果
训练的损失下降曲线如图6所示:
图6 损失下降曲线
可以看出模型在经过多次迭代后效果逐渐增强。
三、模型评测
从pairs随机选取10条语言对,模型测试得到的法文翻译输出与正确的翻译对比如图7所示:
图7 生成翻译结果
可以看出模型生成的翻译已经很接近正确翻译,但仍然存在一些偏差。
对句子sentence= “we re both teachers .”评估得到生成的目标语言对应的词汇以及Attention可视化如图8、图9所示:
图8 目标语言对应词汇
图9 attention可视化
Attention图像的纵坐标代表输入的源语言各个词汇对应的索引,0-6分别对应[“we”, “re”, “both”, “teachers”, “.”, “”],纵坐标代表生成的目标语言各个词汇对应的索引,0-7代表[‘nous’, ‘sommes’, ‘toutes’, ‘deux’, ‘enseignantes’, ‘.’, ”],图中浅色小方块(颜色越浅说明影响越大)代表词汇之间的影响关系,通过这样的可视化图像,可以知道Attention的效果好坏,与人为去判定到底还有多大的差距,进而衡量训练模型的可用性。
四、总结
本实验围绕seq2seq模型完成英法翻译,在整个实验过程中我学习到了整个翻译模型进行翻译的过程,包括分词、字符规范化、编码器和解码器设计、加入注意力机制等。在最后得到了模型生成的翻译,感受到了神经网络的强大。除此之外,此次实验生成的翻译在丰富度和正确性上都有所不足,实验迭代的时间较长,对设置合适的训练参数仍有很多进步空间。我在后期的学习会继续努力,期望能做出更好的实验结果。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
语义分割是计算机视觉领域的一项重要任务,旨在将图像中的每个像素标记为对应的语义类别。与传统的图像分类任务不同,语义分割不仅要识别整个图像的类别,还需要对图像中的每个像素进行分类,从而实现对图像的像素级别理解。 语义分割的目标是为图像中的每个像素分配一个语义标签…