前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深度学习对话系统实战篇 -- 简单 chatbot 代码实现

深度学习对话系统实战篇 -- 简单 chatbot 代码实现

作者头像
AI研习社
发布2018-03-16 14:14:09
2.1K0
发布2018-03-16 14:14:09
举报
文章被收录于专栏:AI研习社AI研习社

本文的代码都可以到我的 github 中下载:https://github.com/lc222/seq2seq_chatbot 前面几篇文章我们已经介绍了 seq2seq 模型的理论知识,并且从 tensorflow 源码层面解析了其实现原理,本篇文章我们会聚焦于如何调用 tf 提供的 seq2seq 的 API,实现一个简单的 chatbot 对话系统。这里先给出几个参考的博客和代码:

  1. tensorflow 官网 API 指导(http://t.cn/R8MiZcR )
  2. Chatbots with Seq2Seq Learn to build a chatbot using TensorFlow(http://t.cn/R8MiykP )
  3. DeepQA(http://t.cn/R8MiVld )
  4. Neural_Conversation_Models(http://t.cn/RtthjXn )

经过一番调查发现网上现在大部分 chatbot 的代码都是基于 1.0 版本之前的 tf 实现的,而且都是从 tf 官方指导文档 nmt 上进行迁移和改进,所以基本上大同小异,但是在实际使用过程中会发现一个问题,由于 tf 版本之间的兼容问题导致这些代码在新版本的 tf 中无法正常运行,常见的几个问题主要是:

  • seq2seq API 从 tf.nn 迁移到了 tf.contrib.legacy_seq2seq;
  • rnn 目前也大都使用 tf.contrib.rnn 下面的 RNNCell;
  • embedding_attention_seq2seq 函数中调用 deepcopy(cell) 这个函数经常会爆出(TypeError: can't pickle _thread.lock objects)的错误

关于上面第三个错误这里多说几句,因为确实困扰了我很久,基本上我在网上找到的每一份代码都会有这个错(DeepQA 除外)。首先来讲一种最简单的方法是将 tf 版本换成 1.0.0,这样问题就解决了。

然后说下不想改 tf 版本的办法,我在网上找了很久,自己也尝试着去找 bug 所在,错误定位在 embedding_attention_seq2seq 函数中调用 deepcopy 函数,于是就有人尝试着把 deepcopy 改成 copy,或者干脆不进行 copy 直接让 encoder 和 decoder 使用相同参数的 RNNcell,但这明显是不正确的做法。我先想出了一种解决方案就是将 embedding_attention_seq2seq 的传入参数中的 cell 改成两个,分别是 encoder_cell 和 decoder_cell,然后这两个 cell 分别使用下面代码进行初始化:

代码语言:javascript
复制
encoCell = tf.contrib.rnn.MultiRNNCell([create_rnn_cell() for _ in range(num_layers)],)
   decoCell = tf.contrib.rnn.MultiRNNCell([create_rnn_cell() for _ in range(num_layers)],)

这样做不需要调用 deepcopy 函数对 cell 进行复制了,问题也就解决了,但是在模型构建的时候速度会比较慢,我猜测是因为需要构造两份 RNN 模型,但是最后训练的时候发现速度也很慢,就先放弃了这种做法。

然后我又分析了一下代码,发现问题并不是单纯的出现在 embedding_attention_seq2seq 这个函数,而是在调用 module_with_buckets 的时候会构建很多个不同 bucket 的 seq2seq 模型,这就导致了 embedding_attention_seq2seq 会被重复调用很多次,后来经过测试发现确实是这里出现的问题,因为即便不使用 model_with_buckets 函数,我们自己为每个 bucket 构建模型时同样也会报错,但是如果只有一个 bucket 也就是只调用一次 embedding_attention_seq2seq 函数时就不会报错,其具体的内部原理我现在还没有搞清楚,就看两个最简单的例子:

代码语言:javascript
复制
  import tensorflow as tf
   import copy

   cell = tf.contrib.rnn.BasicLSTMCell(10)
   cell1 = copy.deepcopy(cell)#这句代码不会报错,可以正常执行

   a = tf.constant([1,2,3,4,5])
   b = copy.deepcopy(a)#这句代码会报错,就是can't pickle _thread.lock objects。

可以理解为a已经有值了,而且是tf内部类型,导致运行时出错???
还是不太理解tf内部运行机制,为什么cell没有线程锁,但是a有呢==

所以先忽视原因,只看解决方案的话就是,不适用 buckets 构建模型,而是简单的将所有序列都 padding 到统一长度,然后直接调用一次 embedding_attention_seq2seq 函数构建模型即可,这样是不会抱错的。(希望看到这的同学如果对这里比较理解可以指点一二,或者互相探讨一下)

最后我也是采用的这种方案,综合了别人的代码实现了一个 embedding+attention+beam_search 等多种功能的 seq2seq 模型,训练一个基础版本的 chatbot 对话机器人,tf 的版本是 1.4。写这份代码的目的一方面是为了让自己对 tf 的 API 接口的使用方法更熟悉,另一方面是因为网上的一些代码都很繁杂,想 DeepQA 这种,里面会有很多个文件还实现了前端,然后各种封装,显得很复杂,不适合新手入门,所以就想写一个跟 textcnn 相似风格的代码,只包含四个文件,代码读起来也比较友好。接下来就让我们看一下具体的代码实现吧。最终的代码我会放在 github 上

数据处理

这里我们借用 [DeepQA](https://github.com/Conchylicultor/DeepQA#chatbot) 里面数据处理部分的代码,省去从原始本文文件构造对话的过程直接使用其生成的 dataset-cornell-length10-filter1-vocabSize40000.pkl 文件。有了该文件之后数据处理的代码就精简了很多,主要包括:

1. 读取数据的函数 loadDataset()

2. 根据数据创建 batches 的函数 getBatches() 和 createBatch()

3. 预测时将用户输入的句子转化成 batch 的函数 sentence2enco()

具体的代码含义在注释中都有详细的介绍,这里就不赘述了,见下面的代码:

代码语言:javascript
复制
padToken, goToken, eosToken, unknownToken = 0, 1, 2, 3

   class Batch:
       #batch类,里面包含了encoder输入,decoder输入,decoder标签,decoder样本长度mask
       def __init__(self):
           self.encoderSeqs = []
           self.decoderSeqs = []
           self.targetSeqs = []
           self.weights = []

   def loadDataset(filename):
       '''       读取样本数据       :param filename: 文件路径,是一个字典,包含word2id、id2word分别是单词与索引对应的字典和反序字典,                       trainingSamples样本数据,每一条都是QA对       :return: word2id, id2word, trainingSamples       '''
       dataset_path = os.path.join(filename)
       print('Loading dataset from {}'.format(dataset_path))
       with open(dataset_path, 'rb') as handle:
           data = pickle.load(handle)  # Warning: If adding something here, also modifying saveDataset
           word2id = data['word2id']
           id2word = data['id2word']
           trainingSamples = data['trainingSamples']
       return word2id, id2word, trainingSamples

   def createBatch(samples, en_de_seq_len):
       '''       根据给出的samples(就是一个batch的数据),进行padding并构造成placeholder所需要的数据形式       :param samples: 一个batch的样本数据,列表,每个元素都是[question, answer]的形式,id       :param en_de_seq_len: 列表,第一个元素表示source端序列的最大长度,第二个元素表示target端序列的最大长度       :return: 处理完之后可以直接传入feed_dict的数据格式       '''
       batch = Batch()
       #根据样本长度获得batch size大小
       batchSize = len(samples)
       #将每条数据的问题和答案分开传入到相应的变量中
       for i in range(batchSize):
           sample = samples[i]
           batch.encoderSeqs.append(list(reversed(sample[0])))  # 将输入反序,可提高模型效果
           batch.decoderSeqs.append([goToken] + sample[1] + [eosToken])  # Add the <go> and <eos> tokens
           batch.targetSeqs.append(batch.decoderSeqs[-1][1:])  # Same as decoder, but shifted to the left (ignore the <go>)
           # 将每个元素PAD到指定长度,并构造weights序列长度mask标志
           batch.encoderSeqs[i] = [padToken] * (en_de_seq_len[0] - len(batch.encoderSeqs[i])) + batch.encoderSeqs[i]
           batch.weights.append([1.0] * len(batch.targetSeqs[i]) + [0.0] * (en_de_seq_len[1] - len(batch.targetSeqs[i])))
           batch.decoderSeqs[i] = batch.decoderSeqs[i] + [padToken] * (en_de_seq_len[1] - len(batch.decoderSeqs[i]))
           batch.targetSeqs[i] = batch.targetSeqs[i] + [padToken] * (en_de_seq_len[1] - len(batch.targetSeqs[i]))

       #--------------------接下来就是将数据进行reshape操作,变成序列长度*batch_size格式的数据------------------------
       encoderSeqsT = []  # Corrected orientation
       for i in range(en_de_seq_len[0]):
           encoderSeqT = []
           for j in range(batchSize):
               encoderSeqT.append(batch.encoderSeqs[j][i])
           encoderSeqsT.append(encoderSeqT)
       batch.encoderSeqs = encoderSeqsT

       decoderSeqsT = []
       targetSeqsT = []
       weightsT = []
       for i in range(en_de_seq_len[1]):
           decoderSeqT = []
           targetSeqT = []
           weightT = []
           for j in range(batchSize):
               decoderSeqT.append(batch.decoderSeqs[j][i])
               targetSeqT.append(batch.targetSeqs[j][i])
               weightT.append(batch.weights[j][i])
           decoderSeqsT.append(decoderSeqT)
           targetSeqsT.append(targetSeqT)
           weightsT.append(weightT)
       batch.decoderSeqs = decoderSeqsT
       batch.targetSeqs = targetSeqsT
       batch.weights = weightsT

       return batch

   def getBatches(data, batch_size, en_de_seq_len):
       '''       根据读取出来的所有数据和batch_size将原始数据分成不同的小batch。对每个batch索引的样本调用createBatch函数进行处理       :param data: loadDataset函数读取之后的trainingSamples,就是QA对的列表       :param batch_size: batch大小       :param en_de_seq_len: 列表,第一个元素表示source端序列的最大长度,第二个元素表示target端序列的最大长度       :return: 列表,每个元素都是一个batch的样本数据,可直接传入feed_dict进行训练       '''
       #每个epoch之前都要进行样本的shuffle
       random.shuffle(data)
       batches = []
       data_len = len(data)
       def genNextSamples():
           for i in range(0, data_len, batch_size):
               yield data[i:min(i + batch_size, data_len)]

       for samples in genNextSamples():
           batch = createBatch(samples, en_de_seq_len)
           batches.append(batch)
       return batches

   def sentence2enco(sentence, word2id, en_de_seq_len):
       '''       测试的时候将用户输入的句子转化为可以直接feed进模型的数据,现将句子转化成id,然后调用createBatch处理       :param sentence: 用户输入的句子       :param word2id: 单词与id之间的对应关系字典       :param en_de_seq_len: 列表,第一个元素表示source端序列的最大长度,第二个元素表示target端序列的最大长度       :return: 处理之后的数据,可直接feed进模型进行预测       '''
       if sentence == '':
           return None
       #分词
       tokens = nltk.word_tokenize(sentence)
       if len(tokens) > en_de_seq_len[0]:
           return None
       #将每个单词转化为id
       wordIds = []
       for token in tokens:
           wordIds.append(word2id.get(token, unknownToken))
       #调用createBatch构造batch
       batch = createBatch([[wordIds, []]], en_de_seq_len)
       return batch

模型构建

有了数据之后看一下模型构建的代码,其实主体代码还是跟前面说到的 tf 官方指导文档差不多,主要分为以下几个功能模块:

1. 一些变量的传入和定义

2. OutputProjection 层和 sampled_softmax_loss 函数的定义

3. RNNCell 的定义和创建

4. 根据训练或者测试调用相应的 embedding_attention_seq2seq 函数构建模型

5. step 函数定义,主要用于给定一个 batch 的数据,构造相应的 feed_dict 和 run_opt

代码如下所示:

代码语言:javascript
复制
import tensorflow as tf
   from my_seq2seq_chatbot.seq2seq import embedding_attention_seq2seq
   class Seq2SeqModel():

       def __init__(self, source_vocab_size, target_vocab_size, en_de_seq_len, hidden_size, num_layers,
                    batch_size, learning_rate, num_samples=1024,
                    forward_only=False, beam_search=True, beam_size=10):
           '''           初始化并创建模型           :param source_vocab_size:encoder输入的vocab size           :param target_vocab_size: decoder输入的vocab size,这里跟上面一样           :param en_de_seq_len: 源和目的序列最大长度           :param hidden_size: RNN模型的隐藏层单元个数           :param num_layers: RNN堆叠的层数           :param batch_size: batch大小           :param learning_rate: 学习率           :param num_samples: 计算loss时做sampled softmax时的采样数           :param forward_only: 预测时指定为真           :param beam_search: 预测时是采用greedy search还是beam search           :param beam_size: beam search的大小           '''
           self.source_vocab_size = source_vocab_size
           self.target_vocab_size = target_vocab_size
           self.en_de_seq_len = en_de_seq_len
           self.hidden_size = hidden_size
           self.num_layers = num_layers
           self.batch_size = batch_size
           self.learning_rate = tf.Variable(float(learning_rate), trainable=False)
           self.num_samples = num_samples
           self.forward_only = forward_only
           self.beam_search = beam_search
           self.beam_size = beam_size
           self.global_step = tf.Variable(0, trainable=False)

           output_projection = None
           softmax_loss_function = None
           # 定义采样loss函数,传入后面的sequence_loss_by_example函数
           if num_samples > 0 and num_samples < self.target_vocab_size:
               w = tf.get_variable('proj_w', [hidden_size, self.target_vocab_size])
               w_t = tf.transpose(w)
               b = tf.get_variable('proj_b', [self.target_vocab_size])
               output_projection = (w, b)
               #调用sampled_softmax_loss函数计算sample loss,这样可以节省计算时间
               def sample_loss(logits, labels):
                   labels = tf.reshape(labels, [-1, 1])
                   return tf.nn.sampled_softmax_loss(w_t, b, labels=labels, inputs=logits, num_sampled=num_samples, num_classes=self.target_vocab_size)
               softmax_loss_function = sample_loss

           self.keep_drop = tf.placeholder(tf.float32)
           # 定义encoder和decoder阶段的多层dropout RNNCell
           def create_rnn_cell():
               encoDecoCell = tf.contrib.rnn.BasicLSTMCell(hidden_size)
               encoDecoCell = tf.contrib.rnn.DropoutWrapper(encoDecoCell, input_keep_prob=1.0, output_keep_prob=self.keep_drop)
               return encoDecoCell
           encoCell = tf.contrib.rnn.MultiRNNCell([create_rnn_cell() for _ in range(num_layers)])

           # 定义输入的placeholder,采用了列表的形式
           self.encoder_inputs = []
           self.decoder_inputs = []
           self.decoder_targets = []
           self.target_weights = []
           for i in range(en_de_seq_len[0]):
               self.encoder_inputs.append(tf.placeholder(tf.int32, shape=[None, ], name="encoder{0}".format(i)))
           for i in range(en_de_seq_len[1]):
               self.decoder_inputs.append(tf.placeholder(tf.int32, shape=[None, ], name="decoder{0}".format(i)))
               self.decoder_targets.append(tf.placeholder(tf.int32, shape=[None, ], name="target{0}".format(i)))
               self.target_weights.append(tf.placeholder(tf.float32, shape=[None, ], name="weight{0}".format(i)))

           # test模式,将上一时刻输出当做下一时刻输入传入
           if forward_only:
               if beam_search:#如果是beam_search的话,则调用自己写的embedding_attention_seq2seq函数,而不是legacy_seq2seq下面的
                   self.beam_outputs, _, self.beam_path, self.beam_symbol = embedding_attention_seq2seq(
                       self.encoder_inputs, self.decoder_inputs, encoCell, num_encoder_symbols=source_vocab_size,
                       num_decoder_symbols=target_vocab_size, embedding_size=hidden_size,
                       output_projection=output_projection, feed_previous=True)
               else:
                   decoder_outputs, _ = tf.contrib.legacy_seq2seq.embedding_attention_seq2seq(
                       self.encoder_inputs, self.decoder_inputs, encoCell, num_encoder_symbols=source_vocab_size,
                       num_decoder_symbols=target_vocab_size, embedding_size=hidden_size,
                       output_projection=output_projection, feed_previous=True)
                   # 因为seq2seq模型中未指定output_projection,所以需要在输出之后自己进行output_projection
                   if output_projection is not None:
                       self.outputs = tf.matmul(decoder_outputs, output_projection[0]) + output_projection[1]
           else:
               # 因为不需要将output作为下一时刻的输入,所以不用output_projection
               decoder_outputs, _ = tf.contrib.legacy_seq2seq.embedding_attention_seq2seq(
                   self.encoder_inputs, self.decoder_inputs, encoCell, num_encoder_symbols=source_vocab_size,
                   num_decoder_symbols=target_vocab_size, embedding_size=hidden_size, output_projection=output_projection,
                   feed_previous=False)
               self.loss = tf.contrib.legacy_seq2seq.sequence_loss(
                   decoder_outputs, self.decoder_targets, self.target_weights, softmax_loss_function=softmax_loss_function)

               # Initialize the optimizer
               opt = tf.train.AdamOptimizer(learning_rate=self.learning_rate, beta1=0.9, beta2=0.999, epsilon=1e-08)
               self.optOp = opt.minimize(self.loss)
               self.saver = tf.train.Saver(tf.all_variables())

       def step(self, session, encoder_inputs, decoder_inputs, decoder_targets, target_weights, go_token_id):
           #传入一个batch的数据,并训练性对应的模型
           # 构建sess.run时的feed_inpits
           feed_dict = {}
           if not self.forward_only:
               feed_dict[self.keep_drop] = 0.5
               for i in range(self.en_de_seq_len[0]):
                   feed_dict[self.encoder_inputs[i].name] = encoder_inputs[i]
               for i in range(self.en_de_seq_len[1]):
                   feed_dict[self.decoder_inputs[i].name] = decoder_inputs[i]
                   feed_dict[self.decoder_targets[i].name] = decoder_targets[i]
                   feed_dict[self.target_weights[i].name] = target_weights[i]
               run_ops = [self.optOp, self.loss]
           else:
               feed_dict[self.keep_drop] = 1.0
               for i in range(self.en_de_seq_len[0]):
                   feed_dict[self.encoder_inputs[i].name] = encoder_inputs[i]
               feed_dict[self.decoder_inputs[0].name] = [go_token_id]
               if self.beam_search:
                   run_ops = [self.beam_path, self.beam_symbol]
               else:
                   run_ops = [self.outputs]

           outputs = session.run(run_ops, feed_dict)
           if not self.forward_only:
               return None, outputs[1]
           else:
               if self.beam_search:
                   return outputs[0], outputs[1]

接下来我们主要说一下我做的主要工作,就是 beam_search 这部分,其原理想必大家看过前面的文章应该已经很清楚了,那么如何编程实现呢,首先我们要考虑的是在哪里进行 beam search,因为 beam search 是在预测时需要用到,代替 greedy 的一种搜索策略,所以第一种方案是在 tf 之外,用 python 实现,这样做的缺点是 decode 速度会很慢。第二种方案是在 tf 内模型构建时进行,这样做的好处是速度快但是比较麻烦。

在网上找了很久在 tensorflow 的一个 issue(http://t.cn/R8M6mDo ) 里面发现了一个方案,他的思路是修改 loop_function 函数,也就是之前根据上一时刻输出得到下一时刻输入的函数,在 loop function 里面实现 top_k 取出概率最大的几个序列,并把相应的路径和单词对应关系保存下来。但是存在一个问题就是一开始 decode 的时候传入的是一句话,也就是 batch_size 为 1,但是经过 loop_function 之后返回的是 beam_size 句话,但是再将其传入 RNNCell 的时候就会报错,如何解决这个问题呢,想了很久决定直接从 decode 开始的时候就把输入扩展为 beam_size 个,把 encoder 阶段的输出和 attention 向量都变成 beam_size 维的 tensor,就说把 decoder 阶段的 RNN 输入的 batch_size 当做为 beam_size。

但是这样做仍然会出现一个问题,就是你会发现最后的输出全部都相同,原因就在于 decoder 开始的时候样本是 beam_szie 个完全相同的输入,所以经过 loop_function 得到的 beam_size 个最大序列也是完全相同的,为了解决这个问题我们需要在第一次编码的时候不取整体最大的前 beam_size 个序列,而是取第一个元素编码结果的前 beam_size 个值作为结果。这部分代码比较多就只贴出来 loop_function 的函数,有兴趣的同学可以去看我 github 上面的代码,就在 seq2seq 文件中。

代码语言:javascript
复制
def loop_function(prev, i, log_beam_probs, beam_path, beam_symbols):
       if output_projection is not None:
           prev = nn_ops.xw_plus_b(prev, output_projection[0], output_projection[1])
       # 对输出概率进行归一化和取log,这样序列概率相乘就可以变成概率相加
       probs = tf.log(tf.nn.softmax(prev))
       if i == 1:
           probs = tf.reshape(probs[0, :], [-1, num_symbols])
       if i > 1:
           # 将当前序列的概率与之前序列概率相加得到结果之前有beam_szie个序列,本次产生num_symbols个结果,
           # 所以reshape成这样的tensor
           probs = tf.reshape(probs + log_beam_probs[-1], [-1, beam_size * num_symbols])
       # 选出概率最大的前beam_size个序列,从beam_size * num_symbols个元素中选出beam_size个
       best_probs, indices = tf.nn.top_k(probs, beam_size)
       indices = tf.stop_gradient(tf.squeeze(tf.reshape(indices, [-1, 1])))
       best_probs = tf.stop_gradient(tf.reshape(best_probs, [-1, 1]))

       # beam_size * num_symbols,看对应的是哪个序列和单词
       symbols = indices % num_symbols  # Which word in vocabulary.
       beam_parent = indices // num_symbols  # Which hypothesis it came from.
       beam_symbols.append(symbols)
       beam_path.append(beam_parent)
       log_beam_probs.append(best_probs)

       # 对beam-search选出的beam size个单词进行embedding,得到相应的词向量
       emb_prev = embedding_ops.embedding_lookup(embedding, symbols)
       emb_prev = tf.reshape(emb_prev, [-1, embedding_size])
       return emb_prev

模型训练

其实模型训练部分的代码很简单,就是每个 epoch 都对样本进行 shuffle 然后分 batches,接下来将每个 batch 的数据分别传入 model.step() 进行模型的训练,这里比较好的一点是,DeepQA 用的是 embedding_rnn_seq2seq 函数,训练过程中 loss 经过 30 个人 epoch 大概可以降到 3 点多,但是我这里改成了 embedding_attention_seq2seq 函数,最后 loss 可以降到 2.0 以下,可以说效果还是很显著的,而且模型的训练速度并没有降低,仍然是 20 个小时左右就可以完成训练。

代码语言:javascript
复制
for e in range(FLAGS.numEpochs):
       print("----- Epoch {}/{} -----".format(e + 1, FLAGS.numEpochs))
       batches = getBatches(trainingSamples, FLAGS.batch_size, model.en_de_seq_len)
       for nextBatch in tqdm(batches, desc="Training"):
           _, step_loss = model.step(sess, nextBatch.encoderSeqs, nextBatch.decoderSeqs, nextBatch.targetSeqs,
                                     nextBatch.weights, goToken)
           current_step += 1
           if current_step % FLAGS.steps_per_checkpoint == 0:
               perplexity = math.exp(float(step_loss)) if step_loss < 300 else float('inf')
               tqdm.write("----- Step %d -- Loss %.2f -- Perplexity %.2f" % (current_step, step_loss, perplexity))
               checkpoint_path = os.path.join(FLAGS.train_dir, "chat_bot.ckpt")
               model.saver.save(sess, checkpoint_path, global_step=model.global_step)

贴上两张图看一下训练的效果,这里用的是 deepQA 的截图,因为我的代码训练的时候忘了加 tensorboard 的东西:

模型预测

预测好模型之后,接下来需要做的就是对模型效果进行测试,这里也比较简单,主要是如何根据 beam_search 都所处的结果找到对应的句子进行输出。代码如下所示:

代码语言:javascript
复制
      if beam_search:
           sys.stdout.write("> ")
           sys.stdout.flush()
           sentence = sys.stdin.readline()
           while sentence:
               #将用户输入的句子转化为id并处理成feed_dict的格式
               batch = sentence2enco(sentence, word2id, model.en_de_seq_len)
               beam_path, beam_symbol = model.step(sess, batch.encoderSeqs, batch.decoderSeqs, batch.targetSeqs,
                                                   batch.weights, goToken)
               paths = [[] for _ in range(beam_size)]
               curr = [i for i in range(beam_size)]
               num_steps = len(beam_path)
               #根据beam_path和beam_symbol得到真正的输出语句,存在paths中
               for i in range(num_steps-1, -1, -1):
                   for kk in range(beam_size):
                       paths[kk].append(beam_symbol[i][curr[kk]])
                       curr[kk] = beam_path[i][curr[kk]]
               recos = set()
               print("Replies --------------------------------------->")
               #转换成句子并输出
               for kk in range(beam_size):
                   foutputs = [int(logit) for logit in paths[kk][::-1]]
                   if eosToken in foutputs:
                       foutputs = foutputs[:foutputs.index(eosToken)]
                   rec = " ".join([tf.compat.as_str(id2word[output]) for output in foutputs if output in id2word])
                   if rec not in recos:
                       recos.add(rec)
                       print(rec)
               print("> ", "")
               sys.stdout.flush()
               sentence = sys.stdin.readline()

接下来我们看一下几个例子,这里 beam_size=5,并去掉了一些重复的回答:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AI研习社 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据处理
  • 模型构建
  • 模型训练
  • 模型预测
相关产品与服务
腾讯智能对话平台
腾讯智能对话平台(Tencent Bot Platform,TBP)专注于“对话即服务”的愿景,全面开放腾讯对话系统核心技术,为大型企业客户、开发者和生态合作伙伴提供开发平台和机器人中间件能力,实现便捷、低成本构建人机对话体验和高效、多样化赋能行业。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档