Tensorflow搞一个聊天机器人
catalogue
. 前言
. 训练语料库
. 数据预处理
. 词汇转向量
. 训练
. 聊天机器人 - 验证效果
0. 前言
不是搞机器学习算法专业的,3个月前开始补了一些神经网络,卷积,神经网络一大堆基础概念,尼玛,还真有点复杂,不过搞懂这些基本数学概念,再看tensorflow的api和python代码觉得跌跌撞撞竟然能看懂了,背后的意思也能明白一点点
0x1: 模型分类
1. 基于检索的模型 vs. 产生式模型
基于检索的模型(Retrieval-Based Models)有一个预先定义的"回答集(repository)",包含了许多回答(responses),还有一些根据输入的问句和上下文(context),以及用于挑选出合适的回答的启发式规则。这些启发式规则可能是简单的基于规则的表达式匹配,或是相对复杂的机器学习分类器的集成。基于检索的模型不会产生新的文字,它只能从预先定义的"回答集"中挑选出一个较为合适的回答。
产生式模型(Generative Models)不依赖于预先定义的回答集,它会产生一个新的回答。经典的产生式模型是基于机器翻译技术的,只不过不是将一种语言翻译成另一种语言,而是将问句"翻译"成回答(response)
2. 长对话模型 vs. 短对话模型
短对话(Short Conversation)指的是一问一答式的单轮(single turn)对话。举例来说,当机器收到用户的一个提问时,会返回一个合适的回答。对应地,长对话(Long Conversation)指的是你来我往的多轮(multi-turn)对话,例如两个朋友对某个话题交流意见的一段聊天。在这个场景中,需要谈话双方(聊天机器人可能是其中一方)记得双方曾经谈论过什么,这是和短对话的场景的区别之一。现下,机器人客服系统通常是长对话模型
3. 开放话题模型 vs. 封闭话题模型
开放话题(Open Domain)场景下,用户可以说任何内容,不需要是有特定的目的或是意图的询问。人们在Twitter、Reddit等社交网络上的对话形式就是典型的开放话题情景。由于该场景下,可谈论的主题的数量不限,而且需要一些常识作为聊天基础,使得搭建一个这样的聊天机器人变得相对困难。
封闭话题(Closed Domain)场景,又称为目标驱动型(goal-driven),系统致力于解决特定领域的问题,因此可能的询问和回答的数量相对有限。技术客服系统或是购物助手等应用就是封闭话题模型的例子。我们不要求这些系统能够谈论政治,只需要它们能够尽可能有效地解决我们的问题。虽然用户还是可以向这些系统问一些不着边际的问题,但是系统同样可以不着边际地给你回复 ;)
Relevant Link:
http://naturali.io/deeplearning/chatbot/introduction/2016/04/28/chatbot-part1.html
http://blog.topspeedsnail.com/archives/10735/comment-page-1#comment-1161
http://blog.csdn.net/malefactor/article/details/51901115
1. 训练语料库
wget https://raw.githubusercontent.com/rustch3n/dgk_lost_conv/master/dgk_shooter_min.conv.zip
解压
unzip dgk_shooter_min.conv.zip
Relevant Link:
https://github.com/rustch3n/dgk_lost_conv
2. 数据预处理
一般来说,我们拿到的基础语料库可能是一些电影台词对话,或者是UBUNTU对话语料库(Ubuntu Dialog Corpus),但基本上我们都要完成以下几大步骤
. 分词(tokenized)
. 英文单词取词根(stemmed)
. 英文单词变形的归类(lemmatized)(例如单复数归类)等
. 此外,例如人名、地名、组织名、URL链接、系统路径等专有名词,我们也可以统一用类型标识符来替代
M 表示话语,E 表示分割,遇到M就吧当前对话片段加入临时对话集,遇到E就说明遇到一个中断或者交谈双方转换了,一口气吧临时对话集加入convs总对话集,一次加入一个对话集,可以理解为拍电影里面的一个"咔"
convs = [] # conversation set
with open(conv_path, encoding="utf8") as f:
one_conv = [] # a complete conversation
for line in f:
line = line.strip('\n').replace('/', '')
if line == '':
continue
if line[] == 'E':
if one_conv:
convs.append(one_conv)
one_conv = []
elif line[] == 'M':
one_conv.append(line.split(' ')[])
因为场景是聊天机器人,影视剧的台词也是一人一句对答的,所以这里需要忽略2种特殊情况,只有一问或者只有一答,以及问和答的数量不一致,即最后一个人问完了没有得到回答
# Grasping calligraphy answer answer
ask = [] # ask
response = [] # answers
for conv in convs:
if len(conv) == :
continue
if len(conv) % != :
conv = conv[:-]
for i in range(len(conv)):
if i % == :
ask.append(conv[i])
else:
response.append(conv[i])
Relevant Link:
3. 词汇转向量
我们知道图像识别、语音识别之所以能率先在深度学习领域取得较大成就,其中一个原因在于这2个领域的原始输入数据本身就带有很强的样本关联性,例如像素权重分布在同一类物体的不同图像中,表现是基本一致的,这本质上也人脑识别同类物体的机制是一样的,即我们常说的"举一反三"能力,我们学过的文字越多,就越可能驾驭甚至能创造组合出新的文字用法,写出华丽的文章
但是NPL或者语义识别领域的输入数据,对话或者叫语料往往是不具备这种强关联性的,为此,就需要引入一个概念模型,叫词向量(word2vec)或短语向量(seq2seq),简单来说就是将语料库中的词汇抽象映射到一个向量空间中,向量的排布是根据预发和词义语境决定的,例如,"中国->人"(中国后面紧跟着一个人字的可能性是极大的)、"你今年几岁了->我 ** 岁了"
0x1: Token化处理、词编码
将训练集中的对话的每个文件拆分成单独的一个个文字,形成一个词表(word table)
def gen_vocabulary_file(input_file, output_file):
vocabulary = {}
with open(input_file) as f:
counter =
for line in f:
counter +=
tokens = [word for word in line.strip()]
for word in tokens:
if word in vocabulary:
vocabulary[word] +=
else:
vocabulary[word] =
vocabulary_list = START_VOCABULART + sorted(vocabulary, key=vocabulary.get, reverse=True)
# For taking custom character kanji
if len(vocabulary_list) > :
vocabulary_list = vocabulary_list[:]
print(input_file + " phrase table size:", len(vocabulary_list))
with open(output_file, "w") as ff:
for word in vocabulary_list:
ff.write(word + "\n")
完成了Token化之后,需要对单词进行数字编码,方便后续的向量空间处理,这里依据的核心思想是这样的
我们的训练语料库的对话之间都是有强关联的,基于这份有关联的对话集获得的词表的词之间也有逻辑关联性,那么我们只要按照此表原生的顺序对词进行编码,这个编码后的[work, id]就是一个有向量空间关联性的词表
def convert_conversation_to_vector(input_file, vocabulary_file, output_file):
tmp_vocab = []
with open(vocabulary_file, "r") as f:
tmp_vocab.extend(f.readlines())
tmp_vocab = [line.strip() for line in tmp_vocab]
vocab = dict([(x, y) for (y, x) in enumerate(tmp_vocab)])
for item in vocab:
print item.encode('utf-8')
所以我们根据训练预料集得到的此表可以作为对话训练集和对话测试机进行向量化的依据,我们的目的是将对话(包括训练集和测试集)的问和答都转化映射到向量空间
土
"土"字在训练集词汇表中的位置是968,我们就给该字设置一个编码968
0x2: 对话转为向量
原作者在词表的选取上作了裁剪,只选取前5000个词汇,但是仔细思考了一下,感觉问题源头还是在训练语料库不够丰富,不能完全覆盖所有的对话语言场景
这一步得到一个ask/answer的语句seq向量空间集,对于训练集,我们将ask和answer建立映射关系
Relevant Link:
4. 训练
0x1: Sequence-to-sequence basics
A basic sequence-to-sequence model, as introduced in Cho et al., 2014, consists of two recurrent neural networks (RNNs): an encoder that processes the input and a decoder that generates the output. This basic architecture is depicted below.
Each box in the picture above represents a cell of the RNN, most commonly a GRU cell or an LSTM cell. Encoder and decoder can share weights or, as is more common, use a different set of parameters. Multi-layer cells have been successfully used in sequence-to-sequence models too
In the basic model depicted above, every input has to be encoded into a fixed-size state vector, as that is the only thing passed to the decoder. To allow the decoder more direct access to the input, an attention mechanism was introduced in Bahdanau et al., 2014.; suffice it to say that it allows the decoder to peek into the input at every decoding step. A multi-layer sequence-to-sequence network with LSTM cells and attention mechanism in the decoder looks like this.
0x2: 训练过程
利用ask/answer的训练集输入神经网络,并使用ask/answer测试向量映射集实现BP反馈与,使用一个三层神经网络,让tensorflow自动调整权重参数,获得一个ask-?的模型
# -*- coding: utf- -*- import tensorflow as tf # 0.12
from tensorflow.models.rnn.translate import seq2seq_model
import os
import numpy as np
import math PAD_ID =
GO_ID =
EOS_ID =
UNK_ID = # ask/answer conversation vector file
train_ask_vec_file = 'train_ask.vec'
train_answer_vec_file = 'train_answer.vec'
test_ask_vec_file = 'test_ask.vec'
test_answer_vec_file = 'test_answer.vec' # word table
vocabulary_ask_size =
vocabulary_answer_size = buckets = [(, ), (, ), (, ), (, )]
layer_size =
num_layers =
batch_size = # read *dencode.vec和*decode.vec data into memory
def read_data(source_path, target_path, max_size=None):
data_set = [[] for _ in buckets]
with tf.gfile.GFile(source_path, mode="r") as source_file:
with tf.gfile.GFile(target_path, mode="r") as target_file:
source, target = source_file.readline(), target_file.readline()
counter =
while source and target and (not max_size or counter < max_size):
counter +=
source_ids = [int(x) for x in source.split()]
target_ids = [int(x) for x in target.split()]
target_ids.append(EOS_ID)
for bucket_id, (source_size, target_size) in enumerate(buckets):
if len(source_ids) < source_size and len(target_ids) < target_size:
data_set[bucket_id].append([source_ids, target_ids])
break
source, target = source_file.readline(), target_file.readline()
return data_set if __name__ == '__main__':
model = seq2seq_model.Seq2SeqModel(source_vocab_size=vocabulary_ask_size,
target_vocab_size=vocabulary_answer_size,
buckets=buckets, size=layer_size, num_layers=num_layers, max_gradient_norm=5.0,
batch_size=batch_size, learning_rate=0.5, learning_rate_decay_factor=0.97,
forward_only=False) config = tf.ConfigProto()
config.gpu_options.allocator_type = 'BFC' # forbidden out of memory with tf.Session(config=config) as sess:
# 恢复前一次训练
ckpt = tf.train.get_checkpoint_state('.')
if ckpt != None:
print(ckpt.model_checkpoint_path)
model.saver.restore(sess, ckpt.model_checkpoint_path)
else:
sess.run(tf.global_variables_initializer()) train_set = read_data(train_ask_vec_file, train_answer_vec_file)
test_set = read_data(test_ask_vec_file, test_answer_vec_file) train_bucket_sizes = [len(train_set[b]) for b in range(len(buckets))]
train_total_size = float(sum(train_bucket_sizes))
train_buckets_scale = [sum(train_bucket_sizes[:i + ]) / train_total_size for i in range(len(train_bucket_sizes))] loss = 0.0
total_step =
previous_losses = []
# continue train,save modle after a decade of time
while True:
random_number_01 = np.random.random_sample()
bucket_id = min([i for i in range(len(train_buckets_scale)) if train_buckets_scale[i] > random_number_01]) encoder_inputs, decoder_inputs, target_weights = model.get_batch(train_set, bucket_id)
_, step_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, False) loss += step_loss /
total_step += print(total_step)
if total_step % == :
print(model.global_step.eval(), model.learning_rate.eval(), loss) # if model has't not improve,decrese the learning rate
if len(previous_losses) > and loss > max(previous_losses[-:]):
sess.run(model.learning_rate_decay_op)
previous_losses.append(loss)
# save model
checkpoint_path = "chatbot_seq2seq.ckpt"
model.saver.save(sess, checkpoint_path, global_step=model.global_step)
loss = 0.0
# evaluation the model by test dataset
for bucket_id in range(len(buckets)):
if len(test_set[bucket_id]) == :
continue
encoder_inputs, decoder_inputs, target_weights = model.get_batch(test_set, bucket_id)
_, eval_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, True)
eval_ppx = math.exp(eval_loss) if eval_loss < else float('inf')
print(bucket_id, eval_ppx)
Relevant Link:
https://www.tensorflow.org/tutorials/seq2seq
http://suriyadeepan.github.io/2016-06-28-easy-seq2seq/
5. 聊天机器人 - 验证效果
# -*- coding: utf- -*- import tensorflow as tf # 0.12
from tensorflow.models.rnn.translate import seq2seq_model
import os
import sys
import locale
import numpy as np PAD_ID =
GO_ID =
EOS_ID =
UNK_ID = train_ask_vocabulary_file = "train_ask_vocabulary.vec"
train_answer_vocabulary_file = "train_answer_vocabulary.vec" def read_vocabulary(input_file):
tmp_vocab = []
with open(input_file, "r") as f:
tmp_vocab.extend(f.readlines())
tmp_vocab = [line.strip() for line in tmp_vocab]
vocab = dict([(x, y) for (y, x) in enumerate(tmp_vocab)])
return vocab, tmp_vocab if __name__ == '__main__':
vocab_en, _, = read_vocabulary(train_ask_vocabulary_file)
_, vocab_de, = read_vocabulary(train_answer_vocabulary_file) # word table
vocabulary_ask_size =
vocabulary_answer_size = buckets = [(, ), (, ), (, ), (, )]
layer_size =
num_layers =
batch_size = model = seq2seq_model.Seq2SeqModel(source_vocab_size=vocabulary_ask_size,
target_vocab_size=vocabulary_answer_size,
buckets=buckets, size=layer_size, num_layers=num_layers, max_gradient_norm=5.0,
batch_size=batch_size, learning_rate=0.5, learning_rate_decay_factor=0.99,
forward_only=True)
model.batch_size = with tf.Session() as sess:
# restore last train
ckpt = tf.train.get_checkpoint_state('.')
if ckpt != None:
print(ckpt.model_checkpoint_path)
model.saver.restore(sess, ckpt.model_checkpoint_path)
else:
print("model not found") while True:
input_string = raw_input('me > ').decode(sys.stdin.encoding or locale.getpreferredencoding(True)).strip()
# 退出
if input_string == 'quit':
exit() # convert the user's input to vector
input_string_vec = []
for words in input_string.strip():
input_string_vec.append(vocab_en.get(words, UNK_ID))
bucket_id = min([b for b in range(len(buckets)) if buckets[b][] > len(input_string_vec)])
encoder_inputs, decoder_inputs, target_weights = model.get_batch({bucket_id: [(input_string_vec, [])]},
bucket_id)
_, _, output_logits = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, True)
outputs = [int(np.argmax(logit, axis=)) for logit in output_logits]
if EOS_ID in outputs:
outputs = outputs[:outputs.index(EOS_ID)] response = "".join([tf.compat.as_str(vocab_de[output]) for output in outputs])
print('AI > ' + response)
神经网络还是很依赖样本的训练的,我在实验的过程中发现,用GPU跑到20000 step之后,模型的效果才逐渐显现出来,才开始逐渐像正常的人机对话了
Relevant Link:
Copyright (c) 2017 LittleHann All rights reserved
Tensorflow搞一个聊天机器人的更多相关文章
- Tensorflow打造聊天机器人
Tensorflow聊天机器人 聊天机器人也叫做对话系统,是一个热门领域.微软.facebook.苹果.google.微信.slack都在上面做了大的投入,这是一波新的试图改变人和服务交流的创业浪潮. ...
- 人工智能不过尔尔,基于Python3深度学习库Keras/TensorFlow打造属于自己的聊天机器人(ChatRobot)
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_178 聊天机器人(ChatRobot)的概念我们并不陌生,也许你曾经在百无聊赖之下和Siri打情骂俏过,亦或是闲暇之余与小爱同学谈 ...
- 聊天机器人(chatbot)终极指南:自然语言处理(NLP)和深度机器学习(Deep Machine Learning)
在过去的几个月中,我一直在收集自然语言处理(NLP)以及如何将NLP和深度学习(Deep Learning)应用到聊天机器人(Chatbots)方面的最好的资料. 时不时地我会发现一个出色的资源,因此 ...
- 通过Django Channels设计聊天机器人WEB框架
这两个月都在忙着设计针对银联客服业务的智能聊天机器人,上一周已经交完设计报告,这一周还和部门同事一起分享了系统设计及运行效果.因为时间的关系,系统原型我使用了Flask+jQuery的组合,感觉用以原 ...
- 把去世的亲友做成聊天机器人,就能让生者慰藉、死者安息吗? - 看了 寻梦历险记,我的回答是 :是的,他/她永远活在我们心里 www.iremember.com.cn
AppU 如今的我们,之所以离不开手机和互联网,是因为它们确实可以带来信息和方便,让我们轻松记录并分享自己的生活体验,留下了充满回忆的各种文字.语音,各种图片.视频…… 但你有没有想过,当我们逝世时, ...
- 智能聊天机器人——基于RASA搭建
前言: 最近了解了一下Rasa,阅读了一下官方文档,初步搭建了一个聊天机器人. 官方文档:https://rasa.com/docs/ 搭建的chatbot项目地址: https://github.c ...
- 开源项目——小Q聊天机器人V1.0
小Q聊天机器人V1.0 http://blog.csdn.net/baiyuliang2013/article/details/51386281 小Q聊天机器人V1.1 http://blog.csd ...
- Bot Framework 搭建聊天机器人
这周我来跟大家分享的是在Microsoft Build 2016上发布的微软聊天机器人的框架. 现如今,各种人工智能充斥在我们的生活里.最典型的人工智能产品就是聊天机器人,它既可以陪我们聊天,也可以替 ...
- 【自然语言处理篇】--Chatterbot聊天机器人
一.前述 ChatterBot是一个基于机器学习的聊天机器人引擎,构建在python上,主要特点是可以自可以从已有的对话中进行学(jiyi)习(pipei). 二.具体 1.安装 是的,安装超级简单, ...
随机推荐
- Luogu4725 【模板】多项式对数函数(NTT+多项式求逆)
https://www.cnblogs.com/HocRiser/p/8207295.html 安利! #include<iostream> #include<cstdio> ...
- POJ 2352 树状数组
学习自:链接以及百度百科 以及:https://www.bilibili.com/video/av18735440?from=search&seid=363548948825132979 理解 ...
- Quartus prime 16.0 中通过JTAG固化程序
前言 下载项目sof文件到开发板中,掉电后会消失:由于开发板有JTAG口,则可以用JTAG固化jic文件到EPCS16芯片中. 流程 1.打开quartus软件并打开convert programmi ...
- apache 与 tomcat、PHP 关系
Apache:web网络服务器,只支持静态网页,如HTML,C语言开发的 Tomcat:web网络服务器,是apache的扩展,且是个java代码解释器,可脱离apache独立使用,Servlet.J ...
- python学习日记(格式化输出,初始编码,运算符)
格式化输出 顾名思义,按照个人意愿定制想输出的格式. name = input('请输入姓名:') age = int(input('请输入年龄:')) job = input('请输入工作:') h ...
- PHP安装-phpMyAdmin+Discuz
PHP安装-phpMyAdmin+Discuz基于Apache和MySQL安装完成之后继续安装PHP.以构建LAMP动态网站平台.http:./configure --prefix=/usr/loca ...
- 【php】php实现数组分块
有时候需要将一个大数组按一定大小分块,那么可以实现这个功能,代码如下: /** * @param array $arr * @param int $size <p> * @param bo ...
- CF1137C Museums Tour(Tarjan,强连通分量)
好题,神题. 题目链接:CF原网 洛谷 题目大意: 一个国家有 $n$ 个城市,$m$ 条有向道路组成.在这个国家一个星期有 $d$ 天,每个城市有一个博物馆. 有个旅行团在城市 $1$ 出发,当天是 ...
- 粉红猪小妹peppa pig中英文版209集+218本绘本+音频
1.avi格式英文版包括,第一季52集有外挂英文字幕,第二季53集外挂英文字幕,第三季52集有外挂同步英文字幕,第四季1到39集有英文字幕,40-52无字幕.另有4季音频(每集都是单独的音频文件,方便 ...
- iview tree 获取选中子节点的整条数据链
这样子获取到数据是,checked等于true的,获取不到他的父级,父级的父级 解决办法代码如下: //需要有一个唯一ID //==================================== ...