前言

实际工程中很少有直接用深度学习实现端对端的聊天机器人,但这里我们来看看怎么用深度学习的seq2seq模型来实现一个简易的聊天机器人。这篇文章将尝试使用TensorFlow来训练一个基于seq2seq的聊天机器人,实现根据语料库的训练让机器人回答问题。

seq2seq

关于seq2seq的机制原理可看之前的文章《深度学习的seq2seq模型》。

循环神经网络

在seq2seq模型中会使用到循环神经网络,目前流行的几种循环神经网络包括RNN、LSTM和GRU。这三种循环神经网络的机制原理可看之前的文章《循环神经网络》 《LSTM神经网络》 《GRU神经网络》。

训练样本集

主要是一些QA对,开放数据也很多可以下载,这里只是随便选用一小部分问题和回答,存放的格式是第一行为问题,第二行为回答,第三行又是问题,第四行为回答,以此类推。

数据预处理

要训练就肯定要将数据转成数字,可以用0到n的值来表示整个词汇,每个值表示一个单词,这里用VOCAB_SIZE来定义。还有问题的最大最小长度,回答的最大最小长度。除此之外还要定义UNK、GO、EOS和PAD符号,分别表示未知单词,比如你超过 VOCAB_SIZE范围的则认为未知单词,GO表示decoder开始的符号,EOS表示回答结束的符号,而PAD用于填充,因为所有QA对放到同个seq2seq模型中输入和输出都必须是相同的,于是就需要将较短长度的问题或回答用PAD进行填充。

limit = {'maxq': 10,'minq': 0,'maxa': 8,'mina': 3
}UNK = 'unk'
GO = '<go>'
EOS = '<eos>'
PAD = '<pad>'
VOCAB_SIZE = 1000复制代码

按照QA长度的限制进行筛选。

def filter_data(sequences):filtered_q, filtered_a = [], []raw_data_len = len(sequences) // 2for i in range(0, len(sequences), 2):qlen, alen = len(sequences[i].split(' ')), len(sequences[i + 1].split(' '))if qlen >= limit['minq'] and qlen <= limit['maxq']:if alen >= limit['mina'] and alen <= limit['maxa']:filtered_q.append(sequences[i])filtered_a.append(sequences[i + 1])filt_data_len = len(filtered_q)filtered = int((raw_data_len - filt_data_len) * 100 / raw_data_len)print(str(filtered) + '% filtered from original data')return filtered_q, filtered_a复制代码

我们还要得到整个语料库所有单词的频率统计,还要根据频率大小统计出排名前n个频率的单词作为整个词汇,也就是前面对应的VOCAB_SIZE。另外我们还需要根据索引值得到单词的索引,还有根据单词得到对应索引值的索引。

def index_(tokenized_sentences, vocab_size):freq_dist = nltk.FreqDist(itertools.chain(*tokenized_sentences))vocab = freq_dist.most_common(vocab_size)index2word = [GO] + [EOS] + [UNK] + [PAD] + [x[0] for x in vocab]word2index = dict([(w, i) for i, w in enumerate(index2word)])return index2word, word2index, freq_dist复制代码

前面也说到在我们的seq2seq模型中,对于encoder来说,问题的长短是不同的,那么不够长的要用PAD进行填充,比如问题为"how are you",假如长度定为10,则需要将其填充为"how are you pad pad pad pad pad pad pad"。对于decoder来说,要以GO开始,以EOS结尾,不够长还得填充,比如"fine thank you",则要处理成"go fine thank you eos pad pad pad pad pad "。第三个要处理的则是我们的target,target其实和decoder的输入是相同的,只不过它刚好有一个位置的偏移,比如上面要去掉go,变成"fine thank you eos pad pad pad pad pad pad"。

def zero_pad(qtokenized, atokenized, w2idx):data_len = len(qtokenized)# +2 dues to '<go>' and '<eos>'idx_q = np.zeros([data_len, limit['maxq']], dtype=np.int32)idx_a = np.zeros([data_len, limit['maxa'] + 2], dtype=np.int32)idx_o = np.zeros([data_len, limit['maxa'] + 2], dtype=np.int32)for i in range(data_len):q_indices = pad_seq(qtokenized[i], w2idx, limit['maxq'], 1)a_indices = pad_seq(atokenized[i], w2idx, limit['maxa'], 2)o_indices = pad_seq(atokenized[i], w2idx, limit['maxa'], 3)idx_q[i] = np.array(q_indices)idx_a[i] = np.array(a_indices)idx_o[i] = np.array(o_indices)return idx_q, idx_a, idx_odef pad_seq(seq, lookup, maxlen, flag):if flag == 1:indices = []elif flag == 2:indices = [lookup[GO]]elif flag == 3:indices = []for word in seq:if word in lookup:indices.append(lookup[word])else:indices.append(lookup[UNK])if flag == 1:return indices + [lookup[PAD]] * (maxlen - len(seq))elif flag == 2:return indices + [lookup[EOS]] + [lookup[PAD]] * (maxlen - len(seq))elif flag == 3:return indices + [lookup[EOS]] + [lookup[PAD]] * (maxlen - len(seq) + 1)复制代码

然后将上面处理后的结构都持久化起来,供训练时使用。

构建图

encoder_inputs = tf.placeholder(dtype=tf.int32, shape=[batch_size, sequence_length])
decoder_inputs = tf.placeholder(dtype=tf.int32, shape=[batch_size, sequence_length])
targets = tf.placeholder(dtype=tf.int32, shape=[batch_size, sequence_length])
weights = tf.placeholder(dtype=tf.float32, shape=[batch_size, sequence_length])复制代码

创建四个占位符,分别为encoder的输入占位符、decoder的输入占位符和decoder的target占位符,还有权重占位符。其中batch_size是输入样本一批的数量,sequence_length为我们定义的序列的长度。

cell = tf.nn.rnn_cell.BasicLSTMCell(hidden_size)
cell = tf.nn.rnn_cell.MultiRNNCell([cell] * num_layers)复制代码

创建循环神经网络结构,这里使用LSTM结构,hidden_size是隐含层数量,用MultiRNNCell是因为我们希望创建一个更复杂的网络,num_layers为LSTM的层数。

results, states = tf.contrib.legacy_seq2seq.embedding_rnn_seq2seq(tf.unstack(encoder_inputs, axis=1),tf.unstack(decoder_inputs, axis=1),cell,num_encoder_symbols,num_decoder_symbols,embedding_size,feed_previous=False
)复制代码

使用TensorFlow为我们准备好了的embedding_rnn_seq2seq函数搭建seq2seq结构,当然我们也可以自己从LSTM搭起,分别创建encoder和decoder,但为了方便直接使用embedding_rnn_seq2seq即可。使用tf.unstack函数是为了将encoder_inputs和decoder_inputs展开成一个列表,num_encoder_symbols和num_decoder_symbols对应到我们的词汇数量。embedding_size则是我们的嵌入层的数量,feed_previous这个变量很重要,设为False表示这是训练阶段,训练阶段会使用decoder_inputs作为decoder的其中一个输入,但feed_previous为True时则表示预测阶段,而预测阶段没有decoder_inputs,所以只能依靠decoder上一时刻输出作为当前时刻的输入。

logits = tf.stack(results, axis=1)
loss = tf.contrib.seq2seq.sequence_loss(logits, targets=targets, weights=weights)
pred = tf.argmax(logits, axis=2)
train_op = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)复制代码

接着使用sequence_loss来创建损失,这里根据embedding_rnn_seq2seq的输出来计算损失,同时该输出也可以用来做预测,最大的值对应的索引即为词汇的单词,优化器使用的事AdamOptimizer。

创建会话

with tf.Session() as sess:ckpt = tf.train.get_checkpoint_state(model_dir)if ckpt and ckpt.model_checkpoint_path:saver.restore(sess, ckpt.model_checkpoint_path)else:sess.run(tf.global_variables_initializer())epoch = 0while epoch < 5000000:epoch = epoch + 1print("epoch:", epoch)for step in range(0, 1):print("step:", step)train_x, train_y, train_target = loadQA()train_encoder_inputs = train_x[step * batch_size:step * batch_size + batch_size, :]train_decoder_inputs = train_y[step * batch_size:step * batch_size + batch_size, :]train_targets = train_target[step * batch_size:step * batch_size + batch_size, :]op = sess.run(train_op, feed_dict={encoder_inputs: train_encoder_inputs, targets: train_targets,weights: train_weights, decoder_inputs: train_decoder_inputs})cost = sess.run(loss, feed_dict={encoder_inputs: train_encoder_inputs, targets: train_targets,weights: train_weights, decoder_inputs: train_decoder_inputs})print(cost)step = step + 1if epoch % 100 == 0:saver.save(sess, model_dir + '/model.ckpt', global_step=epoch + 1)复制代码

创建会话开始执行,这里会用到tf.train.Saver对象来保存和读取模型,保险起见可以每隔一定间隔保存一次模型,下次重启会接着训练而不用从头重新来过,这里因为是一个例子,QA对数量不多,所以直接一次性当成一批送进去训练,而并没有分成多批。

预测

with tf.device('/cpu:0'):batch_size = 1sequence_length = 10num_encoder_symbols = 1004num_decoder_symbols = 1004embedding_size = 256hidden_size = 256num_layers = 2encoder_inputs = tf.placeholder(dtype=tf.int32, shape=[batch_size, sequence_length])decoder_inputs = tf.placeholder(dtype=tf.int32, shape=[batch_size, sequence_length])targets = tf.placeholder(dtype=tf.int32, shape=[batch_size, sequence_length])weights = tf.placeholder(dtype=tf.float32, shape=[batch_size, sequence_length])cell = tf.nn.rnn_cell.BasicLSTMCell(hidden_size)cell = tf.nn.rnn_cell.MultiRNNCell([cell] * num_layers)results, states = tf.contrib.legacy_seq2seq.embedding_rnn_seq2seq(tf.unstack(encoder_inputs, axis=1),tf.unstack(decoder_inputs, axis=1),cell,num_encoder_symbols,num_decoder_symbols,embedding_size,feed_previous=True,)logits = tf.stack(results, axis=1)pred = tf.argmax(logits, axis=2)saver = tf.train.Saver()with tf.Session() as sess:module_file = tf.train.latest_checkpoint('./model/')saver.restore(sess, module_file)map = Word_Id_Map()encoder_input = map.sentence2ids(['you', 'want', 'to', 'turn', 'twitter', 'followers', 'into', 'blog', 'readers'])encoder_input = encoder_input + [3 for i in range(0, 10 - len(encoder_input))]encoder_input = np.asarray([np.asarray(encoder_input)])decoder_input = np.zeros([1, 10])print('encoder_input : ', encoder_input)print('decoder_input : ', decoder_input)pred_value = sess.run(pred, feed_dict={encoder_inputs: encoder_input, decoder_inputs: decoder_input})print(pred_value)sentence = map.ids2sentence(pred_value[0])print(sentence)复制代码

预测阶段也同样要创建相同的模型,然后将训练时保存的模型加载进来,然后实现对问题的回答的预测。预测阶段我们用cpu来执行就行了,避免使用GPU。创建图的步骤和训练时基本一致,参数也要保持一致,不同的地方在于我们要将embedding_rnn_seq2seq函数的feed_previous参数设为True,因为我们已经没有decoder输入了。另外我们也不需要损失函数和优化器,仅仅提供预测函数即可。

创建会话后开始执行,先加载model目录下的模型,然后再将待测试的问题转成向量形式,接着进行预测,得到输出如下:
['how', 'do', 'you', 'do', 'this', '', '', '', '', '']。

github

github.com/sea-boat/se…

作者:超人汪小建
链接:https://juejin.im/post/59cc4a9a6fb9a00a69750eeb
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

如何用TensorFlow训练聊天机器人(附github)相关推荐

  1. 基于tensorflow的聊天机器人

    ** 基于tensorflow的聊天机器人 ** 基于Tensorflow的聊天机器人,主要基于机器深度学习,采用seq2seq+Attention模型,先由jieba中文分词框架对汉字文本语句分词再 ...

  2. Python AIML搭建聊天机器人(附遇到的问题及解决)

    Python AIML搭建聊天机器人(附遇到的问题及解决) AIML全名为Artificial Intelligence Markup Language(人工智能标记语言),是一种创建自然语言软件代理 ...

  3. 如何使用深度学习训练聊天机器人

    原文地址 译者观点:目前AI整体处于研究热点,很多领域离产业化还很远,比如本文中的主题如何制作聊天机器人,虽然各大厂都有不同涉足,但是涉及的领域有限,其实在各个细分领域都可以训练专用的聊天机器人.那么 ...

  4. python训练聊天机器人_一个可以进行训练的聊天机器人,一次chat的源码

    chatbot 一个可以使用自己语料进行训练的中文聊天机器人,目前包含seq2seq tf1.x和tf.2x版本,seqGan版本为tf1.x版本,后续计划更新pytorch版本,欢迎大家实践交流. ...

  5. OpenAI使用Reddit训练聊天机器人

    OpenAI是一个非营利人工智能科研团队,其创始人之一Elon Musk是Paypal以及Tesla的CEO.除此之外,他还是私人火箭发射公司SpaceX.太阳能电池商SolarCity的CEO.Op ...

  6. PHP训练聊天机器人,智能聊天机器人,陪你聊天练口才!

    我们口才训练微信群里有智能聊天机器人,专门陪大家聊天练口才! (微信搜索公众号:社交口才训练营,就可以加入我们!) 加入我们口才训练营的朋友,空余时间可以在群里与智能机器人聊天,学习它的聊天技巧和说话 ...

  7. python做人工智能对话_如何用Python制作聊天机器人?

    ,现在几乎30%的任务都是通过聊天机器人完成的.公司使用聊天机器人来提供诸如客户支持.生成信息等服务.以Siri.Alexa等为例,聊天机器人如何在我们的日常生活中发挥作用就变得 ,现在几乎30%的任 ...

  8. python对话机器人软件_如何用Python为聊天机器人创建对话?

    我想写一个快速简单的聊天机器人,可以携带与用户的对话.我想知道如何创建对话,允许无限量的输入和响应.现在我使用的代码不允许用户定义输入.这就是我现在正在使用的代码.在# Import the rand ...

  9. 使用 Python 实现一个简单的智能聊天机器人(附完整代码)

    文章目录 简要说明 总体的思路 需要准备的环境 接收用户的语音输入,并将其存为音频文件 技术提升 调用百度AI接口, 识别音频文件并以文本信息返回 请求智能机器人, 发送文本信息, 返回智能聊天内容 ...

最新文章

  1. python公共键_Python利用公共键如何对字典列表进行排序详解
  2. python创建新文件-Python创建文件和追加文件内容实例
  3. python读写文件实例_python读写文件的简单示例
  4. 使用直接内存时可以更快
  5. linux 等待进程,Linux 进程等待队列
  6. Elasticsearch启动报错:Exception in thread “main“ java.nio.file.AccessDeniedException:
  7. cout不明确什么意思_年轻人不讲武德是什么梗和意思 年轻人不讲武德梗出处
  8. 每天一种设计模式之抽象工厂模式(Java实现)
  9. 留学回国人员申办上海常住户口实施细则
  10. linux 该文件的owner,Linux修改文件/目录的owner/group方法(转载)
  11. Axios 的简易学习笔记
  12. 浅论信息化环境下的印刷业发展
  13. 发布3天获推荐10w+,视频号内容出现新玩法?
  14. Python基础——np.where
  15. 【Python】Decision on buying cars COROLLA or LEVIN(数据分析技术实现过程之·2 data_analysis①)
  16. 安卓手机格式化怎么弄_安卓手机怎样进入格式化?
  17. 纷享销客高燕:回归第一性原理,B2B企业如何向精益化要增长
  18. 聊聊Beaglebone Black的cape和device tree overlay和dtc命令【转】
  19. 英语面试常见问题集锦 .
  20. Win10怎么设置有线网络和WiFi网络优先级?

热门文章

  1. 修改默认runlevel
  2. System 和 Runtime 类
  3. 病症:arm启动后应用程序界面显示…
  4. 重拾Javascript (四) KnockoutJs使用
  5. python 做词云 -jupyter跟随王树义教程学习
  6. Python:高阶函数
  7. 关键字explicit与构造函数
  8. Ubuntu Linux 下文件名乱码(无效的编码)的快速解决办法
  9. PHP是弱类型还是强类型,php弱类型比较(松散比较) | CN-SEC 中文网
  10. ”语义分割”中的“语义”