用TensorFlow实现文本分析模型,做个聊天机器人


用TensorFlow实现文本分析模型,做个聊天机器人

聊天机器人的架构简图
用 TensorFlow 实现 Chatbot 的模型
如何准备 chatbot 的训练数据
Chatbot 源码解读

1. 聊天机器人的架构简图

聊天机器人是可行的

我:chatbot,你好!

chatbot:你也好!

我:聊天机器人可行吗?

chatbot:你不要怀疑这是天方夜谭,我不就在这里吗?世界上还有很多跟我一样聪明的机器人呢,你听过IBM公司在2010年就研发出来了的Watson问答系统吗?它可比我要聪明100倍呢

我:噢,想起来了,据说Watson在智力竞赛中竟然战胜了人类选手。但是我了解到它有一些缺陷:因为它还只是对信息检索技术的综合运用,并没有进行各种语义关系的深刻计算,所以它能回答的问题也仅限于实事类的问题,所以它能赢得也就是知识类的智力竞赛,如果你给它出个脑筋急转弯,它就不行了

chatbot:是的呢,所以你任重道远啊

聊天机器人工作原理是什么

我:chatbot,我问的每一句话,你都是怎么处理并回答我的呢?

chatbot:我身体里有三个重要模块:提问处理模块、检索模块、答案抽取模块。三个模块一起工作,就能回答你的问题啦

我:是嘛,那么这个提问处理模块是怎么工作的呢?

chatbot:提问处理模块要做三项重要工作:查询关键词生成、答案类型确定、句法和语义分析。

我:那么这个查询关。。。

chatbot:别急别急,听我一个一个讲给你听。查询关键词生成,就是从你的提问中提取出关键的几个关键词,因为我本身是一个空壳子,需要去网上查找资料才能回答你,而但网上资料那么多,我该查哪些呢?所以你的提问就有用啦,我找几个中心词,再关联出几个扩展词,上网一搜,一大批资料就来啦,当然这些都是原始资料,我后面要继续处理。再说答案类型确定,这项工作是为了确定你的提问属于哪一类的,如果你问的是时间、地点,和你问的是技术方案,那我后面要做的处理是不一样的。最后再说这个句法和语义分析,这是对你问题的深层含义做一个剖析,比如你的问题是:聊天机器人怎么做?那么我要知道你要问的是聊天机器人的研发方法

我:原来是这样,提问处理模块这三项工作我了解了,那么检索模块是怎么工作的呢?

chatbot:检索模块跟搜索引擎比较像,就是根据查询关键词所信息检索,返回句子或段落,这部分就是下一步要处理的原料

我:那么答案抽取模块呢?

chatbot:答案抽取模块可以说是计算量最大的部分了,它要通过分析和推理从检索出的句子或段落里抽取出和提问一致的实体,再根据概率最大对候选答案排序,注意这里是“候选答案”噢,也就是很难给出一个完全正确的结果,很有可能给出多个结果,最后还在再选出一个来

我:那么我只要实现这三个模块,就能做成一个你喽?

chatbot:是的

聊天机器人的关键技术

我:chatbot,小弟我知识匮乏,能不能告诉我都需要学哪些关键技术才能完成我的梦想

chatbot:小弟。。。我还没满月。说到关键技术,那我可要列一列了,你的任务艰巨了:

1)海量文本知识表示:网络文本资源获取、机器学习方法、大规模语义计算和推理、知识表示体系、知识库构建;

2)问句解析:中文分词、词性标注、实体标注、概念类别标注、句法分析、语义分析、逻辑结构标注、指代消解、关联关系标注、问句分类(简单问句还是复杂问句、实体型还是段落型还是篇章级问题)、答案类别确定;

3)答案生成与过滤:候选答案抽取、关系推演(并列关系还是递进关系还是因果关系)、吻合程度判断、噪声过滤

聊天机器人的技术方法

我:chatbot,我对聊天机器人的相关技术总算有所了解了,但是我具体要用什么方法呢?

chatbot:看你这么好学,那我就多给你讲一讲。聊天机器人的技术可以分成四种类型:1)基于检索的技术;2)基于模式匹配的技术;3)基于自然语言理解的技术;4)基于统计翻译模型的技术。这几种技术并不是都要实现,而是选其一,听我给你说说他们的优缺点,你就知道该选哪一种了。基于检索的技术就是信息检索技术,它简单易实现,但无法从句法关系和语义关系给出答案,也就是搞不定推理问题,所以直接pass掉。基于模式匹配的技术就是把问题往已经梳理好的几种模式上去靠,这种做推理简单,但是模式我们涵盖不全,所以也pass掉。基于自然语言理解就是把浅层分析加上句法分析、语义分析都融入进来做的补充和改进。基于统计翻译就是把问句里的疑问词留出来,然后和候选答案资料做配对,能对齐了就是答案,对不齐就对不起了,所以pass掉。选哪个知道了吗?

我:知道了!基于自然语言理解的技术!so easy!妈妈再也不用担心我的学习!o(╯□╰)o

chatbot:好,那我的任务结束啦,我该走了,再见!

我:不要走——不要走——kajima——

在呼唤chatbot的声音中,我从梦中醒来,这时我才意识到这是一个梦,揉揉双眼,突然发现床边有一张纸,上面写着:

你会成功的!读了这篇文章的人也会成功的!加油!

--------------------------------------------

聊天机器人的工作流程大体为:提问-检索-答案抽取。

提问:就是要分析主人的问句中关键词,提问类型,还有真正想知道的东西。

检索:根据前一步的分析,去找答案。

答案抽取:找到的答案,并不能直接应用,还要整理成真正有用的,可以作为答案的回答。

涉及到的关键技术如图中所示。

看不清图的话,就是酱紫:

问句解析:
中文分词、词性标注、实体标注、概念类别标注、句法分析、语义分析、逻辑结构标注、指代消解、关联关系标注、问句分类、答案类别确定;

海量文本知识表示:
网络文本资源获取、机器学习方法、大规模语义计算和推理、知识表示体系、知识库构建

答案生成与过滤:
候选答案抽取、关系推演、吻合程度判断、噪声过滤


2. 用 TensorFlow 实现 Chatbot 的模型

之前有根据 Siraj 的视频写过一篇《自己动手写个聊天机器人吧》,

http://www.jianshu.com/p/d0f4a751012b

文章里只写了主函数的简单过程:Data-Model-Training,是用 Lua 实现的。

下面这篇文章是用 TensorFlow + tflearn 库 实现,在建模, 训练 和 预测等环节可以学到更多细节:

学习资源:自己动手做聊天机器人 三十八-原来聊天机器人是这么做出来的

http://www.shareditor.com/blogshow/?blogId=121

两篇的共同点是都用了 Seq2Seq 来实现。

LSTM的模型结构为:

细节的话可以直接去看上面那篇原文,这里 po 出建立模型阶段简要的流程图和过程描述:

  • 先将原始数据 300w chat 做一下预处理,即 切词,分为 问答对。

  • 然后用 word2vec 训练出词向量,生成二进制的词向量文件。

作为 Input data X 传入下面流程:

  • question 进入 LSTM 的 encoder 环节,answer 进入 decoder 环节,

  • 分别生成 output tensor。

  • 其中 decoder 是一个词一个词的生成结果,将所有结果加入到一个 list 中。

  • 最后和 encoder 的输出,一起做为下一环节 Regression 的输入,并传入 DNN 网络。


3. 如何准备 chatbot 的训练数据

学习资源:
自己动手做聊天机器人 三十八-原来聊天机器人是这么做出来的

http://www.shareditor.com/blogshow/?blogId=121

训练数据的生成过程如下:

  • 首先在 input file 里读取每一行,并根据 ‘|’ 拆分成 question 和 answer 句子。

  • 每个句子,都将 word 通过 word2vec 转化成词向量。

  • 每一句的向量序列都转化成相同维度的形式:self.word_vec_dim * self.max_seq_len

  • 最后 answer 构成了 y 数据,question+answer 构成了 xy 数据,再被投入到 model 中去训练:

model.fit(trainXY, trainY, n_epoch=1000,snapshot_epoch=False, batch_size=1)

代码如下:

def init_seq(input_file):

"""读取切好词的文本文件,加载全部词序列

"""

file_object = open(input_file, 'r')

vocab_dict = {}

while True:

question_seq = []

answer_seq = []

line = file_object.readline()

if line:

line_pair = line.split('|')

line_question = line_pair[0]

line_answer = line_pair[1]

for word in line_question.decode('utf-8').split(' '):

if word_vector_dict.has_key(word):

question_seq.append(word_vector_dict[word])

for word in line_answer.decode('utf-8').split(' '):

if word_vector_dict.has_key(word):

answer_seq.append(word_vector_dict[word])

else:

break

question_seqs.append(question_seq)

answer_seqs.append(answer_seq)

file_object.close()

def generate_trainig_data(self):

xy_data = []

y_data = []

for i in range(len(question_seqs)):

question_seq = question_seqs[i]

answer_seq = answer_seqs[i]

if len(question_seq) < self.max_seq_len and len(answer_seq) < self.max_seq_len:

sequence_xy = [np.zeros(self.word_vec_dim)] * (self.max_seq_len-len(question_seq)) + list(reversed(question_seq))

sequence_y = answer_seq + [np.zeros(self.word_vec_dim)] * (self.max_seq_len-len(answer_seq))

sequence_xy = sequence_xy + sequence_y

sequence_y = [np.ones(self.word_vec_dim)] + sequence_y

xy_data.append(sequence_xy)

y_data.append(sequence_y)

return np.array(xy_data), np.array(y_data)

4. Chatbot 源码解读

提炼出步骤如下:

其中 2. 准备数据, 3. 建立模型 就是上文着重说的部分。

    1. 引入包

    1. 准备数据

    1. 建立模型

    1. 训练

    1. 预测

1. 引入包

import sys

import math

import tflearn

import tensorflow as tf

from tensorflow.python.ops import rnn_cell

from tensorflow.python.ops import rnn

import chardet

import numpy as np

import struct

2. 准备数据

def load_word_set()
将 3000 万语料,分成 Question 和 Answer 部分,提取出 word。

def load_word_set():

file_object = open('./segment_result_lined.3000000.pair.less', 'r')

while True:

line = file_object.readline()

if line:

line_pair = line.split('|')

line_question = line_pair[0]

line_answer = line_pair[1]

for word in line_question.decode('utf-8').split(' '):

word_set[word] = 1

for word in line_answer.decode('utf-8').split(' '):

word_set[word] = 1

else:

break

file_object.close()

def load_vectors(input)
从 vectors.bin 加载词向量,返回一个word_vector_dict的词典,key 是词,value 是200维的向量。

def init_seq(input_file)
将 Question 和 Answer 中单词对应的词向量放在词向量序列中question_seqs, answer_seqs

def init_seq(input_file):

"""读取切好词的文本文件,加载全部词序列

"""

file_object = open(input_file, 'r')

vocab_dict = {}

while True:

question_seq = []

answer_seq = []

line = file_object.readline()

if line:

line_pair = line.split('|')

line_question = line_pair[0]

line_answer = line_pair[1]

for word in line_question.decode('utf-8').split(' '):

if word_vector_dict.has_key(word):

question_seq.append(word_vector_dict[word])

for word in line_answer.decode('utf-8').split(' '):

if word_vector_dict.has_key(word):

answer_seq.append(word_vector_dict[word])

else:

break

question_seqs.append(question_seq)

answer_seqs.append(answer_seq)

file_object.close()

def vector_sqrtlen(vector)
用来求向量的长度。

def vector_sqrtlen(vector):

len = 0

for item in vector:

len += item * item

len = math.sqrt(len)

return len

def vector_cosine(v1, v2)

用来求两个向量间的距离。

def vector_cosine(v1, v2):

if len(v1) != len(v2):

sys.exit(1)

sqrtlen1 = vector_sqrtlen(v1)

sqrtlen2 = vector_sqrtlen(v2)

value = 0

for item1, item2 in zip(v1, v2):

value += item1 * item2

return value / (sqrtlen1*sqrtlen2)

def vector2word(vector)

给定一个词向量,去 word-vector 字典中查找与此向量距离最近的向量,并记忆相应的单词,返回单词和 cosine 值。

def vector2word(vector):

max_cos = -10000

match_word = ''

for word in word_vector_dict:

v = word_vector_dict[word]

cosine = vector_cosine(vector, v)

if cosine > max_cos:

max_cos = cosine

match_word = word

return (match_word, max_cos)

3. 建立模型

class MySeq2Seq(object)

def generate_trainig_data(self)

由 question_seqs, answer_seqs 得到 xy_data 和 y_data 的形式。

def model(self, feed_previous=False)

用 input data 生成 encoder_inputs 和带GO头的 decoder_inputs。

将 encoder_inputs 传递给编码器,返回一个输出(预测序列的第一个值)和一个状态(传给解码器)。

在解码器中,用编码器的最后一个输出作为第一个输入,预测过程用前一个时间序的输出作为下一个时间序的输入。

4. 训练

def train(self)

用 generate_trainig_data() 生成 X y 数据,传递给 上面定义的 model,并训练 model.fit,再保存。

5. 预测

用 generate_trainig_data() 生成数据,用 model.predict 进行预测,predict 结果的每一个 sample 相当于一句话的词向量序列,每个 sample 中的每个 vector 在 word-vector 字典中找到与其最近的向量,并返回对应的 word,及二者间的 cosine。

if __name__ == '__main__':

phrase = sys.argv[1]

if 3 == len(sys.argv):

my_seq2seq = MySeq2Seq(word_vec_dim=word_vec_dim, max_seq_len=max_seq_len, input_file=sys.argv[2])

else:

my_seq2seq = MySeq2Seq(word_vec_dim=word_vec_dim, max_seq_len=max_seq_len)

if phrase == 'train':

my_seq2seq.train()

else:

model = my_seq2seq.load()

trainXY, trainY = my_seq2seq.generate_trainig_data()

predict = model.predict(trainXY)

for sample in predict:

print "predict answer"

for w in sample[1:]:

(match_word, max_cos) = vector2word(w)

#if vector_sqrtlen(w) < 1:

# break

print match_word, max_cos, vector_sqrtlen(w)

------------------------------

本人微信公众帐号: 心禅道(xinchandao)

本人微信公众帐号:双色球预测合买(ssqyuce)

优质内容筛选与推荐>>
1、iptables 命令介绍
2、[转]魔兽争霸3的自动拼接地形渲染方式
3、.NET也可以跨平台 Mono 2.10发布
4、C++虚函数和纯虚函数(2)
5、DirectX技术实现视频会议中的音频通信[转]


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号