小熊科技|PyTorch文本:01.聊天机器人教程( 九 )


第一个技巧是使用teacher forcing 。这意味着在一些概率是由teacher_forcing_ratio设置 , 我们使用当前目标单词作为解码器 的下一个输入 , 而不是使用解码器的当前推测 。 该技巧充当解码器的training wheels , 有助于更有效的训练 。 然而 , teacher forcing 可能导致推导中的模型不稳定 , 因为解码器可能没有足够的机会在训练期间真正地制作自己的输出序列 。 因此 , 我们必须注意我们如何设置teacher_forcing_ratio ,同时不要被快速的收敛所迷惑 。
第二个技巧是梯度裁剪(gradient clipping) 。 这是一种用于对抗“爆炸梯度(exploding gradient)”问题的常用技术 。 本质上 ,通过将梯度剪切或阈值化到最大值 , 我们可以防止在损失函数中梯度以指数方式增长并发生溢出(NaN)或者越过梯度 。
小熊科技|PyTorch文本:01.聊天机器人教程图片来源: Goodfellow et al. Deep Learning. 2016.
操作顺序
1.通过编码器前向计算整个批次输入 。
2.将解码器输入初始化为SOS_token , 将隐藏状态初始化为编码器的最终隐藏状态 。
3.通过解码器一次一步地前向计算输入一批序列 。
4.如果是 teacher forcing 算法:将下一个解码器输入设置为当前目标;如果是 no teacher forcing 算法:将下一个解码器输入设置为当前解码器输出 。
5.计算并累积损失 。
6.执行反向传播 。
7.裁剪梯度 。
8.更新编码器和解码器模型参数 。
PyTorch的RNN模块(RNN , LSTM , GRU)可以像任何其他非重复层一样使用 , 只需将整个输入序列(或一批序列)传递给它们 。我们在编码器中使用GRU层就是这样的 。 实际情况是 , 在计算中有一个迭代过程循环计算隐藏状态的每一步 。 或者 , 你每次只运行一个 模块 。 在这种情况下 , 我们在训练过程中手动循环遍历序列就像我们必须为解码器模型做的那样 。 只要你正确的维护这些模型的模块 , 就 可以非常简单的实现顺序模型 。
input_variable: tensor([[ 273,64,53,25,25],[ 188,542, 4095,200,200],[53,4,115,67, 3644],[ 660,4, 3600, 1531,2],[1258,4,4,4,0],[4,2,2,2,0],[2,0,0,0,0]])lengths: tensor([7, 6, 6, 6, 4])target_variable: tensor([[ 147,214,219,252,122],[47,4,389,387,27],[7,4,25,25,14],[1026,4,222,4,53],[1034,2,53,2,4],[12,0, 4096,0,4],[1113,0,6,0,4],[4,0,2,0,2],[2,0,0,0,0]])mask: tensor([[1, 1, 1, 1, 1],[1, 1, 1, 1, 1],[1, 1, 1, 1, 1],[1, 1, 1, 1, 1],[1, 1, 1, 1, 1],[1, 0, 1, 0, 1],[1, 0, 1, 0, 1],[1, 0, 1, 0, 1],[1, 0, 0, 0, 0]], dtype=torch.uint8)max_target_len: 95.3 训练迭代现在终于将完整的训练步骤与数据结合在一起了 。 给定传递的模型、优化器、数据等 , trainIters函数负责运行n_iterations的训练 。这个功能显而易见 , 因为我们通过train函数的完成了繁重工作 。
需要注意的一点是 , 当我们保存模型时 , 我们会保存一个包含编码器和解码器state_dicts(参数)、优化器的state_dicts、损失、迭代等的 压缩包 。 以这种方式保存模型将为我们checkpoint,提供最大的灵活性 。 加载checkpoint后 , 我们将能够使用模型参数进行推理 , 或者我们可 以在我们中断的地方继续训练 。
def trainIters(model_name, voc, pairs, encoder, decoder, encoder_optimizer, decoder_optimizer, embedding, encoder_n_layers, decoder_n_layers, save_dir, n_iteration, batch_size, print_every, save_every, clip, corpus_name, loadFilename):# 为每次迭代加载batchestraining_batches = [batch2TrainData(voc, [random.choice(pairs) for _ in range(batch_size)])for _ in range(n_iteration)]# 初始化print('Initializing ...')start_iteration = 1print_loss = 0if loadFilename:start_iteration = checkpoint['iteration'] + 1# 训练循环print("Training...")for iteration in range(start_iteration, n_iteration + 1):training_batch = training_batches[iteration - 1]# 从batch中提取字段input_variable, lengths, target_variable, mask, max_target_len = training_batch# 使用batch运行训练迭代loss = train(input_variable, lengths, target_variable, mask, max_target_len, encoder,decoder, embedding, encoder_optimizer, decoder_optimizer, batch_size, clip)print_loss += loss# 打印进度if iteration % print_every == 0:print_loss_avg = print_loss / print_everyprint("Iteration: {}; Percent complete: {:.1f}%; Average loss: {:.4f}".format(iteration, iteration / n_iteration * 100, print_loss_avg))print_loss = 0# 保存checkpointif (iteration % save_every == 0):directory = os.path.join(save_dir, model_name, corpus_name, '{}-{}_{}'.format(encoder_n_layers, decoder_n_layers, hidden_size))if not os.path.exists(directory):os.makedirs(directory)torch.save({'iteration': iteration,'en': encoder.state_dict(),'de': decoder.state_dict(),'en_opt': encoder_optimizer.state_dict(),'de_opt': decoder_optimizer.state_dict(),'loss': loss,'voc_dict': voc.__dict__,'embedding': embedding.state_dict()}, os.path.join(directory, '{}_{}.tar'.format(iteration, 'checkpoint')))


推荐阅读