小熊科技|PyTorch文本:01.聊天机器人教程( 八 )
现在我们已经定义了注意力子模块 , 我们可以实现真实的解码器模型 。 对于解码器 , 我们将每次手动进行一批次的输入 。这意味着我们的词嵌 入张量和GRU输出都将具有相同大小*(1 , batch_size , hidden_size)* 。
计算图
1.获取当前输入的词嵌入
2.通过单向GRU进行前向传播
3.通过2输出的当前GRU计算注意力权重
4.将注意力权重乘以编码器输出以获得新的“weighted sum”上下文向量
5.使用Luong eq.5连接加权上下文向量和GRU输出
6.使用Luong eq.6预测下一个单词(没有softmax)
7.返回输出和最终隐藏状态
输入
input_step:每一步输入序列batch(一个单词);shape =(1 , batch_size)last_hidden:GRU的最终隐藏层;shape =(n_layers x num_directions , batch_size , hidden_size)encoder_outputs:编码器模型的输出;shape =(max_length , batch_size , hidden_size)输出
output: 一个softmax标准化后的张量 ,代表了每个单词在解码序列中是下一个输出单词的概率;shape =(batch_size , voc.num_words)hidden: GRU的最终隐藏状态;shape =(n_layers x num_directions , batch_size , hidden_sizeclass LuongAttnDecoderRNN(nn.Module):def __init__(self, attn_model, embedding, hidden_size, output_size, n_layers=1, dropout=0.1):super(LuongAttnDecoderRNN, self).__init__()self.attn_model = attn_modelself.hidden_size = hidden_sizeself.output_size = output_sizeself.n_layers = n_layersself.dropout = dropout# 定义层self.embedding = embeddingself.embedding_dropout = nn.Dropout(dropout)self.gru = nn.GRU(hidden_size, hidden_size, n_layers, dropout=(0 if n_layers == 1 else dropout))self.concat = nn.Linear(hidden_size * 2, hidden_size)self.out = nn.Linear(hidden_size, output_size)self.attn = Attn(attn_model, hidden_size)def forward(self, input_step, last_hidden, encoder_outputs):# 注意:我们一次运行这一步(单词)# 获取当前输入字的嵌入embedded = self.embedding(input_step)embedded = self.embedding_dropout(embedded)# 通过单向GRU转发rnn_output, hidden = self.gru(embedded, last_hidden)# 从当前GRU输出计算注意力attn_weights = self.attn(rnn_output, encoder_outputs)# 将注意力权重乘以编码器输出以获得新的“加权和”上下文向量context = attn_weights.bmm(encoder_outputs.transpose(0, 1))# 使用Luong的公式五连接加权上下文向量和GRU输出rnn_output = rnn_output.squeeze(0)context = context.squeeze(1)concat_input = torch.cat((rnn_output, context), 1)concat_output = torch.tanh(self.concat(concat_input))# 使用Luong的公式6预测下一个单词output = self.out(concat_output)output = F.softmax(output, dim=1)# 返回输出和在最终隐藏状态return output, hidden5.定义训练步骤5.1 Masked 损失由于我们处理的是批量填充序列 , 因此在计算损失时我们不能简单地考虑张量的所有元素 。 我们定义maskNLLLoss可以根据解码器的输出张量、 描述目标张量填充的binary mask张量来计算损失 。 该损失函数计算与mask tensor中的1对应的元素的平均负对数似然 。
def maskNLLLoss(inp, target, mask):nTotal = mask.sum()crossEntropy = -torch.log(torch.gather(inp, 1, target.view(-1, 1)).squeeze(1))loss = crossEntropy.masked_select(mask).mean()loss = loss.to(device)return loss, nTotal.item()5.2 单次训练迭代train函数包含单次训练迭代的算法(单批输入) 。
我们将使用一些巧妙的技巧来帮助融合:
推荐阅读
- 所持股份|万兴科技:公司控股股东、实际控制人吴太兵质押150万股
- 发布公告|数量过半!博创科技:天通股份累计减持约150万股
- 英雄科技聊数码|蔡崇信有实力买下篮网,那身价3200亿的马云,能买下几支NBA球队
- 科技前沿阵地|涨疯了!海思安防芯片遭哄抬“围剿”
- 月影浓|吴亦凡机械造型走秀 垫肩披风搭银框眼镜科技感足
- 中国历史发展过程|中国历史发展过程.中国的科技史界过去半个多世纪
- 天津|桂发祥:不再持有昆汀科技股份
- 消费|减持!天通股份:减持博创科技约32万股
- 处罚|老周侃股:吉鑫科技大股东应补偿踩雷投资者
- 华中科技大学|杯具!超本科线95分,本科有路不走,却梦幻般碰瓷,撞开专科的门
