[ 源码讲解 ] pytorch通过Seq2Seq开发聊天机器人

Song • 2795 次浏览 • 0 个回复 • 2018年03月06日

大家好,在这篇文章中,笔者要向大家介绍,如何使用pytorch这个框架来写出一个seq2seqmodel,在阅读本文之前,如果对pytorch的基本构架和seq2seq的概念不是很熟悉的话,可以查看相关文章。

本篇的示例code放在pytorch-chatbot,以下会针对各段示例code做说明。

流程

在使用深度学习的框架去训练一个model时,通常都会有以下几个主要的步骤,

1、处理数据

  • Preprocessing:要先对数据做预处理,去除噪声过多,或是不适合拿来训练的数据。
  • Feature extraction:由于数据有可能是文字、图片等,如何转换成张量,使得这些张量含有最多信息,并利用降维来去除多余的feature而不失信息量是这一个步骤的重要课题。

2、训练Training

  • Module(模块):比较大型的model通常可以切成好几个部份,就seq2seq -model而言,可以切成encoderdecoder两个部份。将这些部份包装成module不只更为直观,更可重复利用。这个是pytorch特有的。
  • Graph:将input Variable通过modelfunctionpytorch会动态的建成graph以计算gradient
  • Gradient decent:利用公式weight = weight - learning_rate * gradient来做gradient decent,在pytorchtorch.optim里,有提供不同算法的optimizer来做gradient decent,包括常见的SGDAdamRMSProp

3、测试Testing

  • 将测试数据(testing data)通过已经训练好的model,测试其表现(performance)。

模块Module

(本段要说明的是model.py)

我们可以把seq2seqmodel拆成两个部份,encoderdecoder,在代码中,EncoderRNN这个moduleencoder,而LuongAttnDecoderRNN是使用attention mechanismdecoder

在实现这些model最重要也是最难的地方在于张量维度的转换,只要熟悉model的构架,要实现是非常直观、简单的,因此笔者在这里将不详述代码,而陈列了几个实现上要注意的事项。

  • Embedding layer

Embedding layer是一个查找表(lookup table),当输入字的索引,会回传它所对应的word vector,对于使用word embedding是一个非常方便使用的构架。这些word vectors是存在weight这个Variable里面,所以requires_grad = True的时候,在进行training的时候是会跟着被训练的,如果要将pretrainword vector放入embedding layer

emb = nn.Embedding(num_embeddings,embedding_dim)
emb.weight.data.copy_(pretrained_word2vec)
emb.weight.requires_grad = False
  • Packed Sequence

Recurrent neural network里,由于每笔数据的inputoutput在长度会有所不同,无法用batch的方式来train,在pytorch有一个特别的classPackedSequence,用来帮忙解决这个问题。有以下几点需要注意

不能直接宣告一个PackedSequence物件,要用torch.nn.utils.rnn.pack_padded_sequenceVariable转换成PackedSequence,如果要在转换回Variable,要用torch.nn.utils.rnn.pad_packed_sequence这个函式。

长度需要由长排到短,这也是为什么在load的时候,training data需要依照长度排序。

  • requires_grad

module里的parameters,预设requires_grad = True,有别于一般Variable的预设为False

训练Training

(本段要说明的是train.py)在本段代码中,最主要的三个函式为

  • batch2TrainData

load.py所整理好的training pairs,转换成inputoutput Variable

# 将batch里的training pairs按照input sequence的长度排,由长到短
pair_batch.sort(key=lambda x: len(x[0].split(" ")), reverse=True)

# 将pairs拆开成input和output
input_batch, output_batch = [], []
for i in range(len(pair_batch)):
    input_batch.append(pair_batch[i][0])
    output_batch.append(pair_batch[i][1])

# 将input和output batch做padding后,转换成Variable
# 记录lengths做packed sequence用
# mask是用来计算loss
input, lengths = inputVar(input_batch, voc)
output, mask, max_target_len = outputVar(output_batch, voc)
return input, lengths, output, mask, max_target_len
  • train

input data通过model,再利用backpropagation算出gradient,使用optimizer去更新model的参数。

# 将grad重设
encoder_optimizer.zero_grad()
decoder_optimizer.zero_grad()

# encoder stage
encoder_outputs, encoder_hidden = encoder(input_variable, lengths, None)

# decoder stage
decoder_input = Variable(torch.LongTensor([[SOS_token for _ in range(batch_size)]]))
decoder_input = decoder_input.cuda() if USE_CUDA else decoder_input

decoder_hidden = encoder_hidden[:decoder.n_layers]

for t in range(max_target_len):
    decoder_output, decoder_hidden, decoder_attn = decoder(
        decoder_input, decoder_hidden, encoder_outputs
    )
    decoder_input = target_variable[t].view(1, -1) # Next input is current target
    mask_loss, nTotal = maskNLLLoss(decoder_output, target_variable[t], mask[t])
    loss += mask_loss
    print_losses.append(mask_loss.data[0] * nTotal)
    n_totals += nTotal
  • trainEpochs

准备好training data,建好training所需要的moduleoptimizer,并重复的把training batch喂进model里面做参数的更新。

# Voc, training batch list
voc, pairs = loadPrepareData()
training_batches = [batch2TrainData(voc, [random.choice(pairs) for _ in range(batch_size)], reverse)
                          for _ in range(n_epochs)]

# encoder, decoder, optimizer
encoder = EncoderRNN(voc.n_words, hidden_size, embedding, n_layers)
decoder = LuongAttnDecoderRNN(attn_model, embedding, hidden_size, voc.n_words, n_layers)
encoder_optimizer = optim.Adam(encoder.parameters(), lr=learning_rate)
decoder_optimizer = optim.Adam(decoder.parameters(), lr=learning_rate * decoder_learning_ratio)

# iterative training
for epoch in tqdm(range(start_epoch, n_epochs + 1)):
    training_batch = training_batches[epoch - 1]
    input_variable, lengths, target_variable, mask, max_target_len = training_batch
    loss = train(input_variable, lengths, target_variable, mask, max_target_len, encoder,
                     decoder, embedding, encoder_optimizer, decoder_optim

原文链接:Pytorch Seq2Seq 篇


原创文章,转载请注明 :[ 源码讲解 ] pytorch通过Seq2Seq开发聊天机器人 - pytorch中文网
原文出处: https://ptorch.com/news/136.html
问题交流群 :168117787
提交评论
要回复文章请先登录注册
用户评论
  • 没有评论
Pytorch是什么?关于Pytorch! Pytorch seq2seq聊天机器人(pytorch-chatbot)