—一步步构建对话机器人
title: 一步步构建对话机器人
tags: 新建,模板,小书匠
grammar_cjkRuby: true
本文基于斯坦福大学CS 20SI: “TensorFlow for Deep Learning Research”,训练语料为Cornell Movie-Dialogs Corpus
一、数据处理
1.数据结构
语料关键是下面两个文件,一个包含了所有文本,一个包含了文本之间的关系
movie_lines.txt
- 包含每个表达(utterance)的实际文本
- fields:
- lineID
- characterID (who uttered this phrase)
- movieID
- character name
- text of the utterance
前面5个样本:
L1045 +++ +++u0+++ +++ m0 +++ +++BIANCA+++ +++ They do not!
L1044 +++ +++u2+++ +++ m0 +++ +++CAMERON+++ +++ They do to!
L985 +++ +++u0+++ +++ m0 +++ +++BIANCA+++ +++ I hope so.
L984 +++ +++u2+++ +++ m0 +++ +++CAMERON+++ +++ She okay?
L925 +++ +++u0+++ +++ m0 +++ +++BIANCA+++ +++ Let’s Go.
movie_conversations.txt
- 对话的结构-
fields
- characterID of the first character involved in the conversation 对话中的第一个角色的ID
- characterID of the second character involved in the conversation 对话中的第二个角色的ID
movieID of the movie in which the conversation occurred 对话所属电影的ID
list of the utterances that make the conversation, in chronological
order: [‘lineID1’,’lineID2’,?’lineIDN’]
has to be matched with movie_lines.txt to reconstruct the actual content
对话中以时间顺序的各个表达的列表,
order: [‘lineID1’,’lineID2’,?’lineIDN’]必须和movie_lines.txt匹配以便于重构实际内容
前面5个样本:
u0 +++ +++u2+++ +++ m0 +++ +++[‘L194′,‘L195′,‘L196′,‘L197′]u0+++ +++ u2 +++ +++m0+++ +++ [‘L198’, ‘L199’]
u0 +++ +++u2+++ +++ m0 +++ +++[‘L200′,‘L201′,‘L202′,‘L203′]u0+++ +++ u2 +++ +++m0+++ +++ [‘L204’, ‘L205’, ‘L206’]
u0 +++ +++u2+++ +++ m0 +++$+++ [‘L207’, ‘L208’]
因此我们需要对这两个文件分别清洗后进行匹配,获取对话内容
2.Bucketing
sequences长度是RNN网络数据预处理要考虑的重要问题,通常而言,我们要处理的序列长度都不是固定的。我们需要将其-padding成固定
长度的序列进行处理,具体而言就是在序列中添加0等标记进行填充。但这些padding的值会改变输出的loss值,影响真正的梯度,会对我们的结果
造成很大的影响。
解决方案通常有两个:一是进行掩码处理,将真实值设为TRUE,padded值设为FALSE,在loss计算时只计算真实值;二是采用dynamic_rnn等模型设定
真实序列长度,使模型只预测真实值。
另外,为降低padding带来的影响,本次聊天机器人,在数据处理时,还将语料具有相似长度的序列进行group,划分为多个buckets,对每个bucket分别构建子图进行
处理。
二、模型
1.函数
主要使用tensorflow的3个函数:
(1)损失函数采用sampled_softmax_loss
如果每次predict,都要对整个dic进行规范因子计算,无疑计算量太大,为降低计算量,采用sampled_softmax,这样每次只需要更新一小部分词典就可以了。
但若采用sampled_softmax,首先要构建output_projection,这样decode时每一stepe输出的词作为下一step输入时,使用output_projection映射到具体的词,就能顺利
获得对应的词向量
sampled_softmax_loss(
weights, #定义的output_projection的权重 w
biases,#定义的output_projection的权重b
labels,#真实词
inputs,#输入词
num_sampled,,#每次更新采样的数目
num_classes,#字典的数目
num_true=1,#每个训练样本对应类别个数,默认一个训练样本一个真实类别
sampled_values=None,#采样规则
remove_accidental_hits=True,#采样规则
partition_strategy='mod',#采样规则
name='sampled_softmax_loss'
)
(2)整体构架采用embedding_attention_seq2seq
embedding_attention_seq2seq(encoder_inputs,# encoder端输入
decoder_inputs,,# decoder端输入
cell,# 本次采用GRU Cell
num_encoder_symbols,# encoder字典数目
num_decoder_symbols,,# decoder字典数目
embedding_size,,# decoder字典数目
num_heads=1,# attention读取次数
output_projection=None,#前面说的输出映射(w,b)
feed_previous=False,#是否采用上一次输出作为本时刻的输入,这是设置为True
dtype=None,
scope=None,
initial_state_attention=False)
(3)如上所述,我们对语料进行buckets分类,因此最后要将上述两个函数嵌入到model_with_buckets函数
model_with_buckets(encoder_inputs,
decoder_inputs,
targets,
weights,
buckets,
seq2seq,#前面定义的embedding_attention_seq2seq
softmax_loss_function=None,#前面定义的sampled_softmax_loss
per_example_loss=False,
name=None):
三、训练
模型构建完成后,下一步就是进行训练,这没什么好说的,记得做好断点保存就行
四、测试
激动人心的时刻来了,看下效果如何:
可以看到,很多话都没有任何意义的,但也有几句还是有些感觉的。
但我感觉还是不错的,毕竟训练语料不大,而且还有以下提升的地方:
1.采用词嵌入应该会好些,以后有机会再试试
2.训练时间只有6个小时左右,应该还有提升的空间
3.模型encode-decode每次只是一轮对话内容,但如何实现多轮对话训练?也许encode端输入前几轮对话内容?或者增加记忆功能
4.decoder解码本次采用的是greedy算法,也许采用Beam Search会更好一些
5.最后,加深模型,增加新词发现能力,可能会更好些
最终代码在这:别忘了点个赞哦