腾讯DeepOcean原创文章:dopro.io/nlp_seq2seq…

智能机器人在生活中随处可见:iPhone里会说话的siri、会下棋的阿法狗、调皮可爱的微软小冰……她们都具有一定的智能,能够和人类进行交互。这些智能机器人非常神奇,看上去离我们也十分遥远,但其实只要我们动动手,便可以造一个属于自己的智能机器人。

本文将教你从零开始造出一个智障,不对是“智能聊天机器人"。

要造一个聊天机器人,首先你需要了解一些相关概念——自然语言处理(NLP),它是一门融语言学、计算机科学、数学于一体的科学,研究让电脑“懂”人类语言的方法。当然,它也包含很多分支:文本朗读、语音识别、句法分析、自然语言生成、人机对话、信息检索、信息抽取、文字校对、文本分类、自动文摘、机器翻译、文字蕴含等等等。

看到这里的朋友,千万别被这些吓跑。既然本文叫《从零开始造一个“智障”聊天机器人》那么各位看官老爷不懂这些也没有关系!跟着我的脚步一步一步做吧。


0x1 基本概念

这里涉及到的原理基础,没兴趣的看官老爷略过即可,不影响后续代码实现。

01|神经网络

人工智能的底层是”神经网络“,许多复杂的应用(比如模式识别、自动控制)和高级模型(比如深度学习)都基于它。学习人工智能,一定是从它开始。

那么问题来了,什么是神经网络呢?简单来说,神经网络就是模拟人脑神经元网络,从而让计算机懂得”思考“。具体概念在这里不再赘述,网络上有很多简单易懂的解释。

本文使用的的是循环神经网络(RNN),我们来看一个最简单的基本循环神经网络:

虽然图像看起来很抽象,但是实际很好理解。x、o、s是一个向量,x代表输入层的值,o代表输出层的值,s是隐藏层的值(这里其实有很多节点);U、V是权重矩阵,U代表输入层到隐藏层的权重矩阵,而V则代表隐藏层到输出层的权重矩阵。那么W是什么呢?其实循环神经网络的隐藏层的值s不仅仅由x、U决定,还会由上一次隐藏层的值s,而W就是上一次到隐藏层到这一次的权重矩阵,将其展开就是这样:

这样逻辑就清晰很多了,这便是一个简单的循环神经网络。而我们的智障,不对是“智能聊天机器人"便是使用循环神经网络,基于自然语言的词法分析、句法分析不断的训练语料,并把语义分析都融入进来做的补充和改进。

02|深度学习框架

适合RNN的深度学习框架有很多,本文的聊天机器人基于Google开源的Tensorflow,从GayhubGithub的starts数便可以看出,Tensorflow是一个极其火爆的深度学习框架,并且可以轻松地在cpu / gpu 上进行分布式计算,下面罗列了一些目前主流深度学习框架的特性,大家可以凭兴趣选择框架进行研究:

03|seq2seq模型

顾名思义,seq2seq 模型就像一个翻译模型,输入是一个序列(比如一个英文句子),输出也是一个序列(比如该英文句子所对应的法文翻译)。这种结构最重要的地方在于输入序列和输出序列的长度是可变的。

举个例子:

在对话机器中:输入(hello) -> 输出 (你好)。

输入是1个英文单词,输出为2个汉字。我们提(输入)一个问题,机器会自动生成(输出)回答。这里的输入和输出显然是长度没有确定的序列(sequences)

我们再举一个长一点的例子:

我教小黄鸡说“大白天的做什么美梦啊?”回答是“哦哈哈哈不用你管”。

Step1:应用双向最大匹配算法分词:双向分词结果,正向《大白天,的,做什么,美梦,啊》;反向《大白天,的,做什么,美梦,啊》。正向反向都是一样的,所以不需要处理歧义问题。长词优先选择,“大白天”和“做什么”。

Step2:以“大白天”举例,假设hash函数为f(),并设f(大白天)指向首字hash表项[大,11,P]。于是由该表项指向“3字索引”,再指向对应“词表”。

Step3:将结构体<大白天,…>插入队尾。体中有一个Ans域,域中某一指针指向“哦哈哈哈不用你管”。

这便是seq2seq的基本原理,原理和技术我们都有了,下一步就是将它实现出来!


0x2 语料准备

了解完一些前置基础,我们话不多说,直接进入造智能聊天机器人的阶段。首先我们需要准备相关训练的语料。

01|语料整理

本次训练的语料库是从Github上下载的(Github用于对话系统的中英文语料:https://github.com/candlewill/Dialog_Corpus)。我们下载其中的xiaohuangji50w_fenciA.conv(小黄鸡语料)进行我们的训练。

当我们下载完后打开发现,它这个语料库是这样的:

虽然这里面的文字、对话我们都能看懂,但是这些E、M、/都是些什么鬼?其实从图来看很容易理解,M即代表这句话,而E则代表一段对话的开始与结束。

我们拿到这些语料后,用代码将其按照问/答分为两类"Question.txt"、"Answer.txt":

1import re
2import sys
3def prepare(num_dialogs=50000):
4    with open("xhj.conv") as fopen:
5        # 替换E、M等
6        reg = re.compile("EnM (.*?)nM (.*?)n")
7        match_dialogs = re.findall(reg, fopen.read())
8        # 使用5W条对话作为训练语料
9        if num_dialogs >= len(match_dialogs):
10            dialogs = match_dialogs
11        else:
12            dialogs = match_dialogs[:num_dialogs]
13        questions = []
14        answers = []
15        for que, ans in dialogs:
16            questions.append(que)
17            answers.append(ans)
18        # 保存到data/文件夹目录下
19        save(questions, "data/Question.txt")
20        save(answers, "data/Answer.txt")
21def save(dialogs, file):
22    with open(file, "w") as fopen:
23        fopen.write("n".join(dialogs))

最终我们得到5W条问题与回答数据:

02|向量表映射建立

到这里,大家可能会问,那么这个"智能"聊天机器人是不是就是将我们输入的问题匹配Question.txt里面的问题,然后再从Answer.txt找到相应回答进行输出?

当然不会是这么简单,本质上聊天机器人是基于问句的上下文环境产生一个新的回答,而非是从数据库中拿出一条对应好的回答数据。

那么机器怎么知道该回答什么呢?此处借用一下谷歌的seq2seq原理图:

简单来说就是:我们输入的每一句话,都会被机器拆成词并向量化;这些词作为输入层的向量,与权重矩阵进行计算后到隐藏层,隐藏层输出的向量再与权重矩阵进行计算,得到最终向量。我们再将此向量映射到词向量库时,便可得到我们想要的结果。

在代码上实现比较简单,因为复杂底层逻辑的都由Tensorflow帮我们完成了,我们将词汇表进行最终的梳理:

 1def gen_vocabulary_file(input_file, output_file):  2    vocabulary = {} 3    with open(input_file) as f: 4        counter = 0 5        for line in f: 6            counter += 1 7            tokens = [word for word in line.strip()] 8            for word in tokens: 9                                # 过滤非中文 文字10                if u'u4e00' <= word <= u'u9fff':11                    if word in vocabulary:12                        vocabulary[word] += 113                    else:14                        vocabulary[word] = 115        vocabulary_list = START_VOCABULART + sorted(vocabulary, key=vocabulary.get, reverse=True)16        # 取前3500个常用汉字,vocabulary_size = 350017        if len(vocabulary_list) > vocabulary_size:18            vocabulary_list = vocabulary_list[:vocabulary_size]19        print(input_file + " 词汇表大小:", len(vocabulary_list))20        with open(output_file, "w") as ff:21            for word in vocabulary_list:22                ff.write(word + "n")23        ff.close复制代码

0x3 开始训练

01|训练

在我们的语料准备好之后,便可以开始我训练,其实训练本身是很简单的,其核心是调用Tensorflow的Seq2SeqModel,不断的进行循环训练。下面是训练的核心代码与参数设置:

 1# 源输入词表的大小 2vocabulary_encode_size = 3500 3# 目标输出词表的大小 4vocabulary_decode_size = 3500 5#一种有效处理不同长度的句子的方法  6buckets = [(5, 10), (10, 15), (20, 25), (40, 50)] 7# 每层单元数目 8layer_size = 256 9# 网络的层数。  10num_layers = 311# 训练时的批处理大小12batch_size =  6413# max_gradient_norm:表示梯度将被最大限度地削减到这个规范14# learning_rate: 初始的学习率15# learning_rate_decay_factor: 学习率衰减因子16# forward_only: false意味着在解码器端,使用decoder_inputs作为输入。例如decoder_inputs 是‘GO, W, X, Y, Z ’,正确的输出应该是’W, X, Y, Z, EOS’。假设第一个时刻的输出不是’W’,在第二个时刻也要使用’W’作为输入。当设为true时,只使用decoder_inputs的第一个时刻的输入,即’GO’,以及解码器的在每一时刻的真实输出作为下一时刻的输入。17model = seq2seq_model.Seq2SeqModel(source_vocab_size=vocabulary_encode_size, target_vocab_size=vocabulary_decode_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)1819config = tf.ConfigProto()20config.gpu_options.allocator_type = 'BFC'  # 防止 out of memory2122with tf.Session(config=config) as sess:23    # 恢复前一次训练24    ckpt = tf.train.get_checkpoint_state('.')25    if ckpt != None:26        print(ckpt.model_checkpoint_path)27        model.saver.restore(sess, ckpt.model_checkpoint_path)28    else:29        sess.run(tf.global_variables_initializer())3031    train_set = read_data(train_encode_vec, train_decode_vec)32    test_set = read_data(test_encode_vec, test_decode_vec)3334    train_bucket_sizes = [len(train_set[b]) for b in range(len(buckets))]35    train_total_size = float(sum(train_bucket_sizes))36    train_buckets_scale = [sum(train_bucket_sizes[:i + 1]) / train_total_size for i in range(len(train_bucket_sizes))]3738    loss = 0.039    total_step = 040    previous_losses = []41    # 一直训练,每过一段时间保存一次模型42    while True:43        random_number_01 = np.random.random_sample()44        bucket_id = min([i for i in range(len(train_buckets_scale)) if train_buckets_scale[i] > random_number_01])4546        encoder_inputs, decoder_inputs, target_weights = model.get_batch(train_set, bucket_id)47        _, step_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, False)4849        loss += step_loss / 50050        total_step += 15152        print(total_step)53        if total_step % 500 == 0:54            print(model.global_step.eval(), model.learning_rate.eval(), loss)5556            # 如果模型没有得到提升,减小learning rate57            if len(previous_losses) > 2 and loss > max(previous_losses[-3:]):58                sess.run(model.learning_rate_decay_op)59            previous_losses.append(loss)60            # 保存模型61            checkpoint_path = "chatbot_seq2seq.ckpt"62            model.saver.save(sess, checkpoint_path, global_step=model.global_step)63            loss = 0.064            # 使用测试数据评估模型65            for bucket_id in range(len(buckets)):66                if len(test_set[bucket_id]) == 0:67                    continue68                encoder_inputs, decoder_inputs, target_weights = model.get_batch(test_set, bucket_id)69                _, eval_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, True)70                eval_ppx = math.exp(eval_loss) if eval_loss < 300 else float('inf')71                print(bucket_id, eval_ppx)复制代码

02|实际问答效果

如果我们的模型一直在训练,那么机器怎么知道在什么时候停止训练呢?这个停止训练的阀值又靠什么去衡量?在这里我们引入一个语言模型评价指标——Perplexity。

① Perplexity是什么:

PPL是用在自然语言处理领域(NLP)中,衡量语言模型好坏的指标。它主要是根据每个词来估计一句话出现的概率,并用句子长度作normalize,公式为 :

S代表sentence,N是句子长度,p(wi)是第i个词的概率。第一个词就是 p(w1|w0),而w0是START,表示句子的起始,是个占位符。 
这个式子可以这样理解,PPL越小,p(wi)则越大,一句我们期望的sentence出现的概率就越高。

还有人说,Perplexity可以认为是average branch factor(平均分支系数),即预测下一个词时可以有多少种选择。别人在作报告时说模型的PPL下降到90,可以直观地理解为,在模型生成一句话时下一个词有90个合理选择,可选词数越少,我们大致认为模型越准确。这样也能解释,为什么PPL越小,模型越好。

对于我们的训练,其最近几次的Perplexity如下:

截止发文时,此模型已经训练了27h,其Perplexity仍然比较难收敛,所以模型的训练真的需要一些耐心。如果有条件使用GPU进行训练,那么此速度将会大大提高。

我们使用现阶段的模型进行一些对话,发现已经初具雏形:

至此,我们的“智能聊天机器人”已经大功告成!但不难看出,这个机器人还是在不断的犯傻,很多问题牛头不对马嘴,所以我们又称其为“智障机器人”。

0x4 结语

至此我们就从无到有训练了一个问答机器人,虽然它还有点”智障“不太理解更多的词汇,但是整体流程已经跑通,并且具有一定的效果。后面的工作就是不断的完善其中的算法参数语料了。其中语料是特别关键的部分,大概会占用到50%-70%的工作量,因为本文使用的是互联网上已经处理好的语料,省去了不少时间。事实上大部分开发人员的时间都在进行语料预处理:数据清洗分词词性标注去停用词等方面。

后续有机会再和大家分享语料预处理这一块。这里有一个可爱的二维码,大家记得关注哟~

相关文献与参考资料:

从机器学习谈起 (http://www.cnblogs.com/subconscious/p/4107357.html)

使用python实现神经网络 (http://www.wildml.com/2015/09/implementing-a-neural-network-from-scratch/)

循环神经网络 (https://zybuluo.com/hanbingtao/note/541458)

语言模型评价指标 (https://blog.csdn.net/index20001/article/details/78884646)

Tensorflow(https://github.com/google/seq2seq)

始发于微信公众号: 腾讯DeepOcean

从零开始造一个“智障”聊天机器人相关推荐

  1. 用机器学习拯救“智障”聊天机器人,谷歌开放分析平台Chatbase

    李杉 李林 编译整理 量子位 出品 | 公众号 QbitAI 当你做了一个网站.App.游戏,肯定会马上想到要加上统计分析功能,这早就成了互联网产品的标配组件,产品迭代.用户运营,都离不开它. 如果你 ...

  2. 居家洁士扫地机器人_如何避免买到“智障”扫地机器人,看这篇

    如何避免买到"智障"扫地机器人,看这篇 2020-10-21 14:12:33 5点赞 7收藏 3评论 不知道大家是不是也有这样的感受:每年都为给老娘买礼物伤透了脑筋,简直可以上升 ...

  3. TensorFlow应用:制作一个简单的聊天机器人

    现在很多卖货公司都使用聊天机器人充当客服人员,许多科技巨头也纷纷推出各自的聊天助手,如苹果Siri.Google Now.Amazon Alexa.微软小冰等等.前不久有一个视频比较了Google N ...

  4. 我的微信'智障聊天助手'的设计思路

    2019独角兽企业重金招聘Python工程师标准>>> 前言 每次写前言最费神,就是感兴趣想研究研究,有了一点点成果希望分享交流,如果能帮助别人就很好,如果有人指导一下就更好了.这次 ...

  5. 避免沦为“人工智障”,机器人还需找对语音交互的“打开方式”

    特定场景进行特定培训,这是打造更连贯.更自然的人机语音交互的一个解决途径. "iPhone的市场占有率是多少?" -"目前80%." "那华为的呢?& ...

  6. 从零开始升级基于RuleBased的聊天机器人

    这里记录从最基础的基于规则的聊天机器人,升级到基于逻辑的机器人,再升级到调用Google提供的API来让机器人能说.会听普通话. 最基本的完全基于规则式的问答:问什么就答什么,幼儿园水平. impor ...

  7. 写一个自动回复的聊天机器人

    要写一个自动聊天的机器人,底层必定离不开socket, TCP 是一个稳定.可靠的传输协议,常用于对数据进行准确无误的传输,socket里面有对它的封装. TCP 的概念 TCP 的英文全拼(Tran ...

  8. EOJ - 我决不会TLE (一个智障的题目)

    Time limit per test: 2.5 seconds Memory limit: 256 megabytes 题目描述 xxx 写了一份用 DFS 求有向无环图中顶点 1 到 n 最短路的 ...

  9. 手把手教你制作一个简单的聊天机器人(图灵api)

    前言:在无聊的时候打打游戏.听听歌还不如来找个人来陪你聊天,今天来教大家制作一个聊天机器人,这样就不会无聊了,在线聊天机器人地址借愁哥哥机器人(可能有点丑,大家将就一下 (

最新文章

  1. 7 种 Javascript 常用设计模式学习笔记
  2. 单链表-删除并释放以L为表头指针的单链表的所有结点(双指针法)
  3. 自研Spring容器,带你解析ioc内部原理
  4. iscroll动态加载数据完美解决方案
  5. WebGame 客户端 美术资源处理之PNG批量导出SWF
  6. gcvt字符串转换函数应用实例
  7. 深度学习-Tensorflow2.2-深度学习基础和tf.keras{1}-多层感知器(神经网络)与激活函数概述-04
  8. linux 14.04安装qt5.9,Ubuntu20.04安装Qt5.9.9+Qt creator
  9. TensorFlow安装中遇到的问题
  10. oracle用户sde老是锁定,关于ArcGIS10.0版本的SDE密码修改,账户锁定,SDE服务启动又停止等问题的解决...
  11. php扩展zval,PHP扩展开发(7):zval结构
  12. 2022最新开源分销商城小程序源码系统前端+后端+搭建教程
  13. 一些学习网址,centos镜像下载地址
  14. 罗马数字转化为阿拉伯数字
  15. python中面向对象编程简称为_Python-面向对象编程
  16. Dest0g3 520迎新赛WP
  17. 项目管理PV、EV、AC、BAC、EAC、ETC等计算
  18. 汇付国际为跨境电商赋能:做合规的跨境支付平台!
  19. 公共艺术与计算机论文题目,优秀公共艺术论文选题 公共艺术论文题目如何定...
  20. 计算机桌面闪动,电脑屏幕闪动怎么解决_电脑屏幕闪烁不停抖动修复方法-win7之家...

热门文章

  1. cart算法_【统计学】决策树模型大比拼!ID3/C4.5/CART算法哪个更好用?
  2. OpenCV4每日一练day3:运行OpenCV示例程序(物体跟踪)
  3. spss和python stata matlab_(SPSS,Matlab,stata,Python)相关性?
  4. 删除了几个月的照片能找回么_手机删除的照片如何恢复?一招教你解决
  5. java的joptionpane空白_java – JOptionPane无法正确显示?
  6. notebook python 已停止工作_Python/Jupyter Notebook初学遇到的一些问题总结(20201108)...
  7. linux是不是显示不了中文版,Linux为什么OpenOffice下不能显示中文
  8. matlab 柱状图_MATLAB作图实例:24:条形图
  9. C++socket编程(六):6.4 epoll多路复用IO高并发
  10. python语音分割_Python 牺牲性能以提升程序员的工作效率