Tensorflow搞一个聊天机器人

catalogue

0. 前言
1. 训练语料库
2. 数据预处理
3. 词汇转向量
4. 训练
5. 聊天机器人 - 验证效果

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),但基本上我们都要完成以下几大步骤

1. 分词(tokenized)
2. 英文单词取词根(stemmed)
3. 英文单词变形的归类(lemmatized)(例如单复数归类)等
4. 此外,例如人名、地名、组织名、URL链接、系统路径等专有名词,我们也可以统一用类型标识符来替代 

M 表示话语,E 表示分割,遇到M就吧当前对话片段加入临时对话集,遇到E就说明遇到一个中断或者交谈双方转换了,一口气吧临时对话集加入convs总对话集,一次加入一个对话集,可以理解为拍电影里面的一个"咔"

convs = []  # conversation set
with open(conv_path, encoding="utf8") as f:one_conv = []  # a complete conversationfor line in f:line = line.strip('\n').replace('/', '')if line == '':continueif line[0] == 'E':if one_conv:convs.append(one_conv)one_conv = []elif line[0] == 'M':one_conv.append(line.split(' ')[1])

因为场景是聊天机器人,影视剧的台词也是一人一句对答的,所以这里需要忽略2种特殊情况,只有一问或者只有一答,以及问和答的数量不一致,即最后一个人问完了没有得到回答

# Grasping calligraphy answer answer
ask = []  # ask
response = []  # answers
for conv in convs:if len(conv) == 1:continueif len(conv) % 2 != 0:conv = conv[:-1]for i in range(len(conv)):if i % 2 == 0: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 = 0for line in f:counter += 1tokens = [word for word in line.strip()]for word in tokens:if word in vocabulary:vocabulary[word] += 1else:vocabulary[word] = 1vocabulary_list = START_VOCABULART + sorted(vocabulary, key=vocabulary.get, reverse=True)# For taking 10000 custom character kanjiif len(vocabulary_list) > 10000:vocabulary_list = vocabulary_list[:10000]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,我们就给该字设置一个编码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-8 -*-import tensorflow as tf  # 0.12
from tensorflow.models.rnn.translate import seq2seq_model
import os
import numpy as np
import mathPAD_ID = 0
GO_ID = 1
EOS_ID = 2
UNK_ID = 3# 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 6000
vocabulary_ask_size = 6000
vocabulary_answer_size = 6000buckets = [(5, 10), (10, 15), (20, 25), (40, 50)]
layer_size = 256
num_layers = 3
batch_size = 64# 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 = 0while source and target and (not max_size or counter < max_size):counter += 1source_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])breaksource, target = source_file.readline(), target_file.readline()return data_setif __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 memorywith 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 + 1]) / train_total_size for i in range(len(train_bucket_sizes))]loss = 0.0total_step = 0previous_losses = []# continue train,save modle after a decade of timewhile 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 / 500total_step += 1print(total_step)if total_step % 500 == 0:print(model.global_step.eval(), model.learning_rate.eval(), loss)# if model has't not improve,decrese the learning rateif len(previous_losses) > 2 and loss > max(previous_losses[-3:]):sess.run(model.learning_rate_decay_op)previous_losses.append(loss)# save modelcheckpoint_path = "chatbot_seq2seq.ckpt"model.saver.save(sess, checkpoint_path, global_step=model.global_step)loss = 0.0# evaluation the model by test datasetfor bucket_id in range(len(buckets)):if len(test_set[bucket_id]) == 0:continueencoder_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 < 300 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-8 -*-import tensorflow as tf  # 0.12
from tensorflow.models.rnn.translate import seq2seq_model
import os
import sys
import locale
import numpy as npPAD_ID = 0
GO_ID = 1
EOS_ID = 2
UNK_ID = 3train_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_vocabif __name__ == '__main__':vocab_en, _, = read_vocabulary(train_ask_vocabulary_file)_, vocab_de, = read_vocabulary(train_answer_vocabulary_file)# word table 6000vocabulary_ask_size = 6000vocabulary_answer_size = 6000buckets = [(5, 10), (10, 15), (20, 25), (40, 50)]layer_size = 256num_layers = 3batch_size = 1model = 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 = 1with tf.Session() as sess:# restore last trainckpt = 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 vectorinput_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][0] > 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=1)) 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:

Tensorflow搞一个聊天机器人相关推荐

  1. 【NLP实战】如何基于Tensorflow搭建一个聊天机器人

    实战是学习一门技术最好的方式,也是深入了解一门技术唯一的方式.因此,NLP专栏计划推出一个实战专栏,让有兴趣的同学在看文章之余也可以自动动手试一试. 本篇介绍如何基于tensorflow快速搭建一个基 ...

  2. 当你对一个聊天机器人敞开了心扉

    ▼ 点击上方蓝字 关注网易智能 聚焦AI,读懂下一个大时代! [网易智能讯 2月2日消息]几个月前,凯特·普客给她的朋友Jasper发了一条信息,内容与她的同事有关.普客19岁,在她的家乡华盛顿州斯波 ...

  3. 树莓派做一个聊天机器人

    首先来安装所需要的第三方库: pip3 install baidu-aip 为了播放音频,还需要安装播放器: sudo apt-get install omxplayersudo apt-get -y ...

  4. python制作聊天机器人原理_用 Python 来做一个聊天机器人吧!(一)

    在我的一个回答里,我提到了用 Python 搭建聊天机器人.从今天开始,我就带着大家从0开始搭建一个聊天机器人. (顺便说一句,我喜欢把链接像上面这样加在文字里,如果找不到文中所说的资源,可以看看周围 ...

  5. python制作聊天机器人_如何制作一个聊天机器人?

    这样,我们岂安科技的斜杠青年用1100字教你做一个聊天机器人出来吧.关键词Zulip.Python.以下,GO~ ------- Zulip是什么 Zulip是移动和桌面办公聊天解决方案开发商 一个强 ...

  6. 如何设计一个聊天机器人?谈谈聊天机器人技术栈

    如果你正在为一个客户设计一个聊天机器人(咨询,开发,原型设计--),这段对话可能听起来很熟悉: 客户:「实现我的机器人最佳技术的是什么?」 我:「不仅仅是一项技术,而是一系列的多种技术.每项技术都可以 ...

  7. 来做一个聊天机器人吧[1]

    来做一个聊天机器人吧[1] 前言 准备 GUI开发 聊天模块1.0版本 语音合成1.0版本 版本1.0 前言 课程设计自己选了这个课题,因为之前就对NLP(Natural Language Proce ...

  8. c语言编写对答机器人_来,你也可以用 C 语言写一个聊天机器人

    来,你也可以用 C 语言写一个聊天机器人 你是不是一直在面对着枯燥的 C 语言特性.摸索着前人写过的各种算法,不是因为自己的兴趣,而是依靠自身的毅力,学得很苦吧. 好吧,我们找一个好玩一点的东西,一起 ...

  9. 利用itchat写一个聊天机器人

    利用itchat写一个聊天机器人 聊天机器人 图灵机器人 需要的库 **自动回复私聊消息** **自动回复群聊消息** 结语: 聊天机器人 偶然在CSDN上看到大佬用20行教你写一个聊天机器人,觉得甚 ...

最新文章

  1. python3 字符串转数组 数组转字符串 切片操作
  2. api文档 luci_研究LuCI - 技术手札 - OSCHINA - 中文开源技术交流社区
  3. python threading 结束线程
  4. 深度学习、图像识别的基本概念
  5. showModalDialog模态对话框的使用详解以及浏览器兼容
  6. java调用perl脚本_Java中调用Perl脚本的应用
  7. PHP数据处理:合并数据、详情数据
  8. 数商云SCM供应链系统方案服务亮点:生产管理更智能、产业供应链协同管理更便捷
  9. 【原创】惠普 CQ35-222TX 笔记本电脑安装东皇 v3.2 Mac OS 详解
  10. Android--仿淘宝商品详情(继续拖动查看详情)及标题栏渐变
  11. 制作纯净系统U盘教程(详细版)
  12. Typora常用好看主题推荐!!!
  13. 安装程序无法打开注册表项 UNKNOWN\Components\…的简单解决办法
  14. 利用1.1.1.1进行DNS网络加速,仅需2分钟让网络更快
  15. mc服务器tps优化,[教程] 使用Openj9大幅降低MC的内存占用,提高FPS和TPS
  16. 未来生活进行时: 互联网”进化论”——达尔文与人工智能的故事?!
  17. 计算机数学ppt,数学主题ppt
  18. 在win8日历显示农历和节假日
  19. 单片机工程师轻松实现触摸屏人机界面的界面编程
  20. window上如何部署守护程序守护

热门文章

  1. C++版数据结构继承关系图
  2. 转png格式_CAD转PNG,你知道怎样转换成高质量清晰的黑白图片吗?
  3. axure9 邮件点击效果_总是收到无关的工作邮件?这个有意思的工具可以帮你消灭它们...
  4. C++继承时的名字遮蔽(二)
  5. dailykt爬取tushare 数据存入本地mysql
  6. python 正则学习笔记
  7. Python中的random模块
  8. 江西理工大学c语言程序设计,C语言程序设计
  9. 职业大揭秘,算法攻城狮在日常工作中都干了些啥?
  10. 在Tableau中去除选择高亮效果