手把手教你写一个中文聊天机器人


本文来自作者 赵英俊(Enjoy)  GitChat 上分享 「手把手教你写一个中文聊天机器人」,阅读原文查看交流实录。

文末高能

编辑 | 哈比

一、前言

发布这篇 Chat 的初衷是想和各位一起分享一下动手来做聊天机器人的乐趣,因此本篇文章适合用于深度机器学习的研究和兴趣发展,因为从工业应用的角度来看使用百度、科大讯飞的 API 接口会更加的适合。

在这篇文章中,希望和大家一起共同交流和探索动手实践的乐趣,当然也欢迎大神来做深度的探讨以及吐槽。

这篇 Chat 的基础源代码来自互联网,我进行了综合优化和部分代码的重写,我也会在这边文章发布的同时将所有源代码上传到 Git 分享出来,这样在文章中我就不占用篇幅贴出全部的源代码,大家可以从 Git 上 pull 下来对照着文章来看。

二、系统设计思路和框架

本次系统全部使用 Python 编写,在系统设计上遵循着配置灵活、代码模块化的思路,分为数据预处理器、数据处理器、执行器、深度学习模型、可视化展示五个模块。

模块间的逻辑关系大致为:

1)数据预处理是将原始语料进行初步的处理以满足于数据处理模块的要求;

2)执行器是整个系统引擎分别在运转的时候调用数据处理器、深度学习模型进行数据处理、模型训练、模型运作等工作;

3)深度学习模型是一个基于 TF 的 seq2seq 模型,用于定义神经网络并进行模型计算;

4)可视化展示是一个用 Flask 前端框架写的简单的人机交互程序,在运行时调用执行器进行人机对话。

整体的框架图如下:

本系统利用 seq2seq 模型的特点,结合 word2vec 的思路(当然这样做有点简单粗暴,没有进行分词),将训练语料分为 Ask 语料集和 Replay 语料集,并根据一定的比例分为训练语料集和验证语料集,然后就是 word2vec。

这些步骤都是在数据预处理器和数据处理器中完成的,关于训练语料集与验证语料集的数量都是做成可外部配置的,这也是这个系统我认为设计的最合理的地方(可惜不是我设计的)。

在完成数据的处理后,执行器就会根据训练模式(这也是外部可配置的)来调用 seq2seq 进行神经网络的创建,神经网络的超参数同样也是可以外部进行配置的。

在进行训练过程中,使用 perprelixy 来计算模型的 loss,通过自动的调整 learning rate 来逐步的取得最优值,当 learning rate 减少为 0 达到最优值。

最后,就是可视化展示模块启动一个进程调用执行器来实时在线提供聊天服务,在语句输入和输出利用 seq2seq 的特点,直接将输入 seq 转换成 vec 作为已经训练好的神经网络,然后神经网络会生成一个 seq 向量,然后通过查询词典的方式将生成的向量替换成中文句子。

在神经网络模型这里,TF 有 GRU 和 LSTM 模型可供选择,我比较了 GRU 和 LSTM 的训练效果,发现还是 GRU 比较适合对话语句场景的训练,这点在后面的代码分析环节会详细解释。

三、源码结构

  • data_utls.py(数据预处理器)包含的函数 convert_seq2seq_files,将主程序分好的 ask 语料集和 response 语料集转换成 seq2seq 文件,以便数据处理器进行进一步的数据处理。对于整个系统,一般该数据预处理器只需要运行一次即可。

  • prepareData.py(数据处理器)包含的函数:create_vocabulary、convert_to_vector、prepare_custom_data、 basic_tokenizer、initialize_vocabulary,这些函数的作用分别是创建字典、句子转向量、根据超参定制化训练数据、基础数据标记、初始化词典。

  • execute.py(执行器)包含的函数:get_config、read_data、create_model、train、 self_test、init_session、decode_line,这些函数的作用分别是获取配置参数和超参、读取数据、创建模型、训练模型、模式测试、初始化会话、在线对话。

  • seq2seqmodel.py(深度机器学习模型)包含的函数:_init、sampled_loss、seq2seq_f、step、get_batch,这些函数的作用分别是程序初始化、loss 函数、seq2seq 函数、拟合模型、获取批量数据。

  • app.py(可视化展示模块)包含的函数: heartbeat、reply、index,这些函数的作用分别是心跳、在线对话、主页入口。

四、源码讲解

1. 数据预处理器(data_utls.py)

首先在代码的头部声明代码编码类型,#!/usr/bin/env Python #coding=utf-8,然后导入所需的依赖:

import os import random import getConfig

os 是提供对 python 进行一些操作系统层面的工具,比如 read、open 等,主要是对文件的一些操作。

random 是一个随机函数,提供对数据的随机分布操作。getConfig 是外部定义的一个函数,用来获取 seq2seq.ini 里的参数配置。

接下来是对原始语料的处理,主要是通过语句的奇偶行数来区分问答语句(由于语料是电影的台词,所以直接默认是一问一答的方式),然后把区分后的语句分别存储下来,保存为 ask 文件和 response 文件。

gConfig = {} gConfig=getConfig.get_config() conv_path = gConfig['resource_data'] if not os.path.exists(conv_path):exit() convs = []  # 用于存储对话集合 with open(conv_path) as f:one_conv = []        # 存储一次完整对话for line in f:line = line.strip('\n').replace('/', '')if line == '':continueif line[0] == gConfig['e']:if one_conv:convs.append(one_conv)one_conv = []elif line[0] == gConfig['m']:one_conv.append(line.split(' ')[1])ask = []        # 用来存储问的语句 response = []   # 用来存储回答的语句 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]) #因为这里的 i 是从 0 开始的,因此偶数为问的语句,奇数为回答的语句else:response.append(conv[i])

然后调用 convert_seq2seq_files 函数,将区分后的问答语句分别保存成训练集和测试机,保存文件为 train.enc、train.dec、test.enc、test.dec。convert_seq2seq_files 函数就不一一贴出来,可以对照源码来看。

2. 数据处理器(prepareData.py)

编码声明和导出依赖部分不再重复,在真正的数据处理前,需要对训练集和测试集中的一些特殊字符进行标记,以便能够进行统一的数据转换。

特殊标记如下:

PAD = "__PAD__"#空白补位 GO = "__GO__"#对话开始 EOS = "__EOS__"  # 对话结束 UNK = "__UNK__"  # 标记未出现在词汇表中的字符 START_VOCABULART = [PAD, GO, EOS, UNK] PAD_ID = 0#特殊标记对应的向量值 GO_ID = 1#特殊标记对应的向量值 EOS_ID = 2#特殊标记对应的向量值 UNK_ID = 3#特殊标记对应的向量值

接下来定义生成词典函数,这里及后续的函数只贴出函数名和参数部分,详细的代码参见 Git 上的源码:

def create_vocabulary(input_file,output_file):

这个函数算法思路会将 input_file 中的字符出现的次数进行统计,并按照从小到大的顺序排列,每个字符对应的排序序号就是它在词典中的编码,这样就形成了一个 key-vlaue 的字典查询表。

当然函数里可以根据实际情况设置字典的大小。

def convert_to_vector(input_file, vocabulary_file, output_file):

这个函数从参数中就可以看出是直接将输入文件的内容按照词典的对应关系,将语句替换成向量,这也是所有 seq2seq 处理的步骤。

因为完成这一步之后,不管原训练语料是什么语言都没有区别了,因为对于训练模型来说都是数字化的向量。

def prepare_custom_data(working_directory, train_enc, train_dec, test_enc, test_dec, enc_vocabulary_size, dec_vocabulary_size, tokenizer=None):

这个函数是数据处理器的集成函数,执行器调用的数据处理器的函数也主要是调用这个函数,这个函数是将预处理的数据从生成字典到转换成向量一次性搞定,将数据处理器对于执行器来说实现透明化。

working_directory 这个参数是存放训练数据、训练模型的文件夹路径,其他参数不一一介绍。

3. seq2seq 模型

seq2seq 模型是直接参照 TF 官方的源码来做的,只是对其中的一些 tf 参数针对 tf 的版本进行了修正。

如 Chat 简介中说的,考虑到大家的接受程度不一样,本次不对代码、算法进行太过深入的分析,后面我会开一个达人课专门详细的进行分析和相关知识的讲解。

class Seq2SeqModel(object):

前面的导入依赖不赘述了,这里在开头需要先定义一个 Seq2SeqModel 对象,这个对象实现了一个多层的 RNN 神经网络以及具有 attention-based 解码器,其实就是一个白盒的神经网络对象,我们只需拿来用即可。

详细的可以参阅 http://arxiv.org/abs/1412.7449 这篇 paper。

关于 paper 这次不做解读。

def __init__(self, source_vocab_size, target_vocab_size, buckets, size,num_layers, max_gradient_norm, batch_size, learning_rate,learning_rate_decay_factor, use_lstm=False,num_samples=512, forward_only=False):

这个函数是整个模型的初始化函数(父函数),这个函数里面会定义多个子函数,简单来讲这个函数执行完之后就完成了训练模型的创建。

这个函数中的大部分参数都是通过 seq2seq.ini 文件进行朝参数配置的,其中 use_lstm 这个参数是决定是使用 gru cell 还是 lstm cell 来构建神经网络,gru 其实是 lstm 的变种。

我两个 cell 都测试了,发现在进行语句对话训练时使用 gru cell 效果会更好,而且好的不是一点。

由于时间的缘故,只对超参 size、num_layers、learning_rate、learning_rate_decay_factor、use_lstm 进行简单对比调试,大家有兴趣的话可以自己进行调参,看看最优的结果值 preprlexity 会不会小于 10。

sampled_loss、seq2seq_f、step、get_batch 这些子函数不一一的讲了,大家可以百度一下,都有很详细的解释和讲解。如果需要,我会在达人课里对这些子函数进行讲解。

4. 执行器

_buckets = [(1, 10), (10, 15), (20, 25), (40, 50)]

这个 bukets 的设置特别关键,也算是一个超参数,因为这个关系到模型训练的效率。

具体设置的时候,有两个大原则:尽量覆盖到所有的语句长度、每个 bucket 覆盖的语句数量尽量均衡。

def read_data(source_path, target_path, max_size=None):

这个函数是读取数据函数,参数也比较简单,其中 max_size 这个参数默认是空或者是 None 的时候表示无限制,同时这个参数也是可以通过 seq2seq.ini 进行设置。

def create_model(session, forward_only):

这个函数是用来生成模型,参数简单。model 的定义也只有一句:

   model = seq2seq_model.Seq2SeqModel( gConfig['enc_vocab_size'], gConfig['dec_vocab_size'], _buckets, gConfig['layer_size'], gConfig['num_layers'], gConfig['max_gradient_norm'], gConfig['batch_size'], gConfig['learning_rate'], gConfig['learning_rate_decay_factor'], forward_only=forward_only)

这里可以看到,模型的生成直接调用了 seq2seq_model 中的对象 Seq2SeqModel,将相应的参数按照要求传进去就可以。

具体这个对象的作用以及详细的细节如前面所说可以参照具体的 paper 来研究,但是作为入门的兴趣爱好者来说可以先不管这些细节,先拿来用就可以了,主要关注点建议还是在调参上。

 def train():

train 函数没有参数传递,因为所有的参数都是通过 gconfig 来读取的,这里面有一个特殊的设计,就是将 prepareData 函数调用放在 train() 函数里。

这样做的话就是每次进行训练时都会对数据进行处理一次,我认为这是非常好的设计,大家可以参考,因为这个可以保证数据的最新以及可以对增长的数据进行训练。具体代码如下:

     enc_train, dec_train, enc_dev, dec_dev, _, _ = prepareData.prepare_custom_data(gConfig['working_directory'],gConfig['train_enc'],gConfig['train_dec'],gConfig['test_enc'],gConfig['test_dec'],gConfig['enc_vocab_size'],gConfig['dec_vocab_size'],tokenizer=None)

def self_test():和 def init_session(sess, conf=’seq2seq.ini’): 这两个函数分别是进行测试以及初始会话用的。

由于 TF 的特殊机制,其每次图运算都是要在 session 下进行的,因此需要在进行图运算之前进行会话初始化。

def decode_line(sess, model, enc_vocab, rev_dec_vocab, sentence):

这个函数就是我们整个对话机器人的最终出效果的函数,这个函数会加载训练好的模型,将输入的 sentence 转换为向量输入模型,然后得到模型的生成向量,最终通过字典转换后返回生成的语句。

由于执行器包含多种模式,因此我们在最后加上一个主函数入口并对执行模式判断,

if __name__ == '__main__': if gConfig['mode'] == 'train':# start trainingtrain() elif gConfig['mode'] == 'test':# interactive decodedecode()else:print('Serve Usage : >> python3 webui/app.py') #当我们使用可视化模块调用执行器时,需要在可视化模块所在的目录下进行调用,而是可视化模块由于包含很多静态文件,所以统一放在 webui 目录下,因此需要将执行器与可视化模块放在同一个目录下。print('# uses seq2seq_serve.ini as conf file')#seq2seq_serve.ini 与 seq2seq.ini 除了执行器的模式外所有配置需要保持一致。

5. 可视化展示模块

def heartbeat():

由于可视化展示模块需要账期在线运行,为了了解运行状态加了一个心跳函数,定时的输出信息来 check 程序运行情况。

def reply():

这个函数是人机对话交互模块,主要是从页面上获取提交的信息,然后调用执行器获得生成的回答语句,然后返回给前端页面。

其中有一点设计可以注意一下,就是在进行语句转向量的过程中,为了保证准确识别向量值,需要在向量值中间加空格,比如 123,这个默认的会识别成一个字符,但是 1 2 3 就是三个字符。

因此在获取到前端的语句后,在传给执行器之前需要对语句进行字符间加空格处理,如下:

req_msg=''.join([f+' ' for fh in req_msg for f in fh])def index():

这个函数是可视化展示模块的首页加载,默然返回一个首页 html 文件。

另外,由于 TF 的特殊机制,需要在可视化模块中初始化会话,这样执行器才能运行,如下:

   import tensorflow as tfimport executesess = tf.Session()sess, model, enc_vocab, rev_dec_vocab = execute.init_session(sess, conf='seq2seq_serve.ini')

最后和执行器一样,需要加一个主函数入口启动可视化模块,并配置服务地址和端口号,如下:

if (__name__ == "__main__"): app.run(host = '0.0.0.0', port = 8808)

五、训练过程和最优值

六、最优效果展示

展示地址:http://115.231.97.140:8999/      

近期热文

《Jenkins 与 GitLab 的自动化构建之旅》

《通往高级 Java 开发的必经之路》

《谈谈源码泄露 · WEB 安全》

《用 LINQ 编写 C# 都有哪些一招必杀的技巧?》

《机器学习面试干货精讲》

《深入浅出 JS 异步处理技术方案》

《敏捷教练 V 形六步法实战:从布朗运动到深度协作》


「阅读原文」看交流实录,你想知道的都在这里

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/72309.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP聊天机器人的搭建(chatbot)(一)

最近在学习研发QA系统,本人单纯想记录一下。项目源码和思路主要参考知乎专栏: PyTorch搭建聊天机器人(一)词表与数据加载器 - 知乎 PyTorch搭建聊天机器人(二)定义seq2seq网络前向逻辑 - 知乎 PyTorch搭…

全国大学生英语竞赛培训:听力、词汇语法、完形填空、阅读理解、翻译、改错、IQ题、大小作文、语法等部分快速得分、备战策略和获奖技巧讲解!

目 录 大学生英语竞赛培训——介绍、学习网址 PPT课件、视频——下载 【2020-04-05】P1 如何准备大学生英语竞赛才能拿奖?2:27:54【竞赛简介vs赛前准备】 【2020-04-11】P2 如何快速搞定大英赛大小作文?2:06:18【大小作文,快速表达~】 …

【情态动词练习题】 May 与 might

1. May 1.Sarah may be ill 2.We may not go out. 3.It may not rain. 4.We may buy a car. 5.Joe may not be at home 6.Anna may need help. 注:情态动词一定要加上动词原型 7.The baby may be hungry. 8.I may not change my job. 9.She may be married. 10.He…

初中七年级上计算机试题答案,人教版初中英语七年级英语上册期中模拟试题检测(含答案)...

原标题:人教版初中英语七年级英语上册期中模拟试题检测(含答案) 七年级英语期中测试题 1、 单项选择(10分) ( ) 1. 下列单词中含有相同元音因素的是_____. A that; these B thank; that C this; those ( ) 2. --________, Gina! --Good morning, Bob! A Good aftern…

初中七年级下英语期中备考单选题(外教出题并附助教经典解析)

1. My sister bought a present _______ her best friends birthday. A. for B. of C. about D. to 选出正确的介词,完成句子。问题: 我妹妹买了一份礼物给她最好的朋友庆祝生日。 答案:A 解析:在这个句子中,“fo…

ChatGPT 爆火 小扎、马斯克纷纷迎战ChatGPT

ChatGPT爆火,直接改变了整个硅谷大厂的格局。近日,马斯克、小扎也纷纷下场,正式对ChatGPT宣战!据The Information报道,马斯克最近几周与一直在于AI研究人员接洽,希望组建一个新的研究实验室,以开…

百度「文心一言」阿里「通义千问」腾讯的AI将会叫什么呢

阿里于昨天2023.4.7下午上线通义千问,与ChatGPT类似,同样是基于语言模型训练的人工智能聊天平台。通义千问的核心功能分为四个大类:撰写短文、职场助理、电影脚本和写封邮件。 通义千问通义千问https://tongyi.aliyun.com/ 首页如下&#xf…

ChatGPT Chat Assist: 职场智能利器,提升工作效率

在当今快节奏的职场生活中,高效率是每位职场人士追求的目标。而现代科技的进步,尤其是人工智能的快速发展,为我们带来了前所未有的便利。ChatGPT作为一款智能核心,推出了Chat Assist插件,成为职场中的得力助手&#xf…

ChatGPT通过图灵测试,图灵测试要重写

近日《Nature》在一篇文章中称,ChatGPT 已经攻破了图灵测试,是时候要启用其他新的方法来评估人工智能技术了。 图灵测试(Turing Test)是著名计算机科学家阿兰图灵在1950年提出的一个测试,用于判断机器是否能展现出与人类智能相当的能力。其测试方法是: 让…

亚马逊云科技X创业邦:专访图灵量子创始人金贤敏

假设你现在正准备去相亲,走进了一家坐满人的咖啡厅。你拿着Ta的照片,一个个地审视着每一个人,看谁对得上号,结果找了半个小时也没找到和照片相似的那个人。这时候,相亲对象给你打来了电话,告诉你Ta正坐在靠窗的角落里,你的目光顺着ta的指引,一下子就找到了Ta。 验证一个问题的答…

马斯克与马云对谈:一场科技乐观主义者的尬聊

硅谷Live / 实地探访 / 热点探秘 / 深度探讨 即使同为技术信徒,话不投机也在所难免。 本文由极客公园原创,阅读更多内容请关注极客公园微信公众号(ID:GeekPark) 在昨天开幕的 2019 世界人工智能大会上,阿里…

马斯克:我抽大麻、藐视SEC,但我是个好CEO

原文:CBS《60分钟》编译:新浪科技 樵夫 导语:特斯拉CEO马斯克(Elon Musk)被人称为天才,人们都认为他非常具有远见卓识。 但是今年,相比于他的卓越才华,人们似乎更加关注他的行为&…

马云对话马斯克:福报变成了一周工作12小时?

燃财经(ID:rancaijing)整理 作者 | 刘景丰 编辑 | 魏佳 8月29日,2019世界人工智能大会(WAIC)在上海举行。开幕式后,美国卡内基梅隆大学计算机学院院长汤姆米切尔(Tom Mitchell)、腾讯…

Vicuna:斯坦福开源一个性能相当于90%ChatGPT的聊天机器人

自从Meta公司发布LLaMA以来,围绕它微调和开发的模型越来越多,这得益于它的性能和效果,2023年3月份发布的草泥马(Alpaca)是由Meta的LLaMA 7B微调而来的全新模型,仅用了52k数据,性能约等于GPT-3.5…

3万多人学习,对ChatGPT最接地气的解读 | 万维钢 × 刘江直播精选

7 月 25 日晚 8 点,科学作家、知识传播大神万维钢老师做客“图灵八点半”栏目,与图灵联合创始人刘江总编共同解读 ChatGPT,围绕 ChatGPT 的基本原理(以及当今世界“最硬核的大脑” Stephen Wolfram 的作品《这就是ChatGPT》&#…

chatgpt赋能python:Python电影:了解Python编程语言的最佳电影

Python电影:了解Python编程语言的最佳电影 Python编程语言在IT行业中广为人知。它常被用于数据分析、人工智能、Web开发等领域。但是你知道吗?Python还成为了一种电影的主题。在这篇文章中,我们将介绍一些关于Python的最佳电影,并…

Spark综合练习——电影评分数据分析

文章目录 引言ChatGPT生成测试数据:今天给大家带来一个Spark综合练习案例--电影评分补充: 采用DSL编程的详尽注释版总结 引言 大家好,我是ChinaManor,直译过来就是中国码农的意思,俺希望自己能成为国家复兴道路的铺路人,大数据领域的耕耘者,一个平凡而不平庸的人。 …

女儿米菲儿的照片

2011年3月26日,我们的宝贝女儿出生啦。 女儿非常乖而且聪明,出生第三天我就抓拍到一种微笑表情的照片。 小米菲儿出生第三天的照片: 小米菲儿出生第九天的照片:

游戏公司奇葩富豪身家仅次许家印,征集长腿美女生娃,女友房产超百套

2018年6月,多益网络在香港联交所递交了招股书。这是一个令人瞠目结舌的巨兽,背后传奇老板徐波也浮出水面。 招股书显示,多益网络在最近三年的营业收入分别达到16.29亿元、15.52亿元和19.34亿元,最后一年涨幅达到24.6%。换言之&…

超级玛丽——请你用字符画的形式输出超级玛丽中的一个场景。

#include<stdio.h> int main() {printf( " *****\n" " ****\n" " ####…#.\n" " #…###…##…\n" " ###…###### ### ### \n" " … #…# #…#\n" " ######### #.#.# #.#.#\n" " ########## …