用 TensorFlow 做个聊天机器人

本文结构:

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

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

学习资源: [自己动手做聊天机器人 九-聊天机器人应该怎么做] (http://www.shareditor.com/blogshow/?blogId=73)

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

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

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

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

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

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

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

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

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


2. 用 TensorFlow 实现 Chatbot 的模型

之前有根据 Siraj 的视频写过一篇《自己动手写个聊天机器人吧》, 文章里只写了主函数的简单过程:Data-Model-Training,是用 Lua 实现的,详细的代码可以去他的github 上学习

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

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

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

LSTM的模型结构为:

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

  • 先将原始数据 300w chat 做一下预处理,即 切词,分为 问答对。
  • 然后用 word2vec 训练出词向量,生成二进制的词向量文件。

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

  • question 进入 LSTM 的 encoder 环节,answer 进入 decoder 环节,
  • 分别生成 output tensor。
  • 其中 decoder 是一个词一个词的生成结果,将所有结果加入到一个 list 中。
  • 最后和 encoder 的输出,一起做为下一环节 Regression 的输入,并传入 DNN 网络。

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

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

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

  • 首先在 input file 里读取每一行,并根据 ‘|’ 拆分成 question 和 answer 句子。
  • 每个句子,都将 word 通过 word2vec 转化成词向量。
  • 每一句的向量序列都转化成相同维度的形式:self.word_vec_dim * self.max_seq_len
  • 最后 answer 构成了 y 数据,question+answer 构成了 xy 数据,再被投入到 model 中去训练:

代码如下:

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 源码解读

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

这篇文章在 github 上的源码:

提炼出步骤如下:

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

  1. 引入包
  2. 准备数据
  3. 建立模型
  4. 训练
  5. 预测

1. 引入包

import sysimport mathimport tflearnimport tensorflow as tffrom tensorflow.python.ops import rnn_cellfrom tensorflow.python.ops import rnnimport chardetimport numpy as npimport 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,再保存。

def train(self):
        trainXY, trainY = self.generate_trainig_data()
        model = self.model(feed_previous=False)
        model.fit(trainXY, trainY, n_epoch=1000, snapshot_epoch=False, batch_size=1)
        model.save('./model/model')        return model

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)

END.

原文发布于微信公众号 - PPV课数据科学社区(ppvke123)

原文发表时间:2017-06-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏星回的实验室

特征选择的评估方法

数据挖掘中,特征选择的过程就是计算特征与样本观测结果的相关性。卡方检验和互信息是用得较多的计算方法。

1731
来自专栏大数据挖掘DT机器学习

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

不错的学习聊天机器人的资源,不知道小伙伴们有没有去学习呢。 自己动手做聊天机器人教程 http://www.shareditor.com/blogshow...

4655
来自专栏深度学习自然语言处理

【论文笔记】中文词向量论文综述(二)

一、Improve Chinese Word Embeddings by Exploiting Internal Structure

1143
来自专栏张俊红

python数据科学-多变量数据分析

总第87篇 01|写在前面: 在前面我们研究了单列(变量)数据情况,现实中的案例大多都是多列(变量)的,即影响一件事情的因素有多个,我们除了要看单列数据以外还需...

4006
来自专栏算法channel

铁粉巨献:某独角兽公司数据挖掘工程师岗位 2000字面试总结

感谢铁粉:地球球长,奉献出自己的面试经验,面经总结的非常认真、详细、用心。这是球长同学第二次无私地奉献他的面经了,上一篇面经 ( @all: 新浪 机器学习算...

990
来自专栏AI科技评论

视频 | 论文最爱的变分自编码器( VAE),不了解一下?

AI 科技评论按:喜欢机器学习和人工智能,却发现埋头苦练枯燥乏味还杀时间?这里是,雷锋字幕组编译的 Arxiv Insights 专栏,从技术角度出发,带你轻松...

4467
来自专栏ATYUN订阅号

机器学习中的问题解决方案:解析解vs数值解

这篇文章会让你了解为什么没人能告诉你要使用什么算法,或如何为特定的数据集配置算法。另外,找到好的数据、算法、配置实际上是应用机器学习的难点,也是你需要集中解决的...

1365
来自专栏AI2ML人工智能to机器学习

一步一步走向锥规划 - LP

一般来说凸优化(Convex Optimization, CO)中最一般的是锥规划 (Cone Programming, CP) 问题, 前面我们介绍了最简单...

1022
来自专栏AI科技大本营的专栏

AI技术讲座精选:用端到端训练模型进行语法智能纠错

现有的拼写检查系统可以识别拼写错误,但无法识别出语法错误,本文的亮点在于使用流行的 seq2seq + attention 模型,在大规模拼写检查数据集上进行训...

6199
来自专栏专知

【专知-PyTorch手把手深度学习教程06】NLP-Word Embedding快速理解与PyTorch实现: 图文+代码

【导读】主题链路知识是我们专知的核心功能之一,为用户提供AI领域系统性的知识学习服务,一站式学习人工智能的知识,包含人工智能( 机器学习、自然语言处理、计算机视...

98112

扫码关注云+社区

领取腾讯云代金券