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.安装 是的,安装超级简单, ...
随机推荐
- 部署 Django
补充说明:关于项目部署,历来是开发和运维人员的痛点.造成部署困难的主要原因之一是大家的Linux环境不同,这包括发行版.解释器.插件.运行库.配置.版本级别等等太多太多的细节.因此,一个成功的部署案例 ...
- System.Diagnostics.Process启动Civil 3D及AutoCAD
QQ群友提出问题, 如何启动Civil 3D或者AutoCAD, Kean的博客里有相关的文章 http://through-the-interface.typepad.com/through_the ...
- VMware ezmomi工具使用
用两个静态IP克隆模板: ezmomi clone --template centos67 --hostname test01 --cpus 2 --mem 4 --ips 172.10.16.203 ...
- 【AGC006C】Rabbit Exercise 置换
题目描述 有\(n\)只兔子站在数轴上.为了方便,将这些兔子标号为\(1\ldots n\).第\(i\)只兔子的初始位置为\(a_i\). 现在这些兔子会按照下面的规则做若干套体操.每一套体操由\( ...
- 「POJ - 2318」TOYS (叉乘)
BUPT 2017 summer training (16) #2 A 题意 有一个玩具盒,被n个隔板分开成左到u右n+1个区域,然后给每个玩具的坐标,求每个区域有几个玩具. 题解 依次用叉积判断玩具 ...
- 【 HDU 1538 】A Puzzle for Pirates (海盗博弈论)
BUPT2017 wintertraining(15) #5D HDU 1538 偷懒直接放个果壳的链接了,感觉比网上直接找这题的题解要更正确.易懂. 海盗博弈论 代码 #include <cs ...
- 牛客网noip集训4
T1 (A)[https://www.nowcoder.com/acm/contest/175/A] 给出 l, r, k,请从小到大输出所有在 [l, r] 范围内,能表示为 k 的非负整数次方的所 ...
- 【刷题】BZOJ 1413 [ZJOI2009]取石子游戏
Description 在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的: 有n堆石子,将这n堆石子摆成一排.游戏由两个人进行,两人轮流操作,每次操作者都可以从 ...
- 聊聊jvm的CompressedClassSpace
序本文主要研究一下jvm的CompressedClassSpace CompressedClassSpacejava8移除了permanent generation,然后class metadata存 ...
- 自学Python之路-django
自学Python之路-django 自学Python之路[第一回]:1.11.2 1.3