【自然语言处理】 构建文本对话系统

构建文本对话系统的框架如下:
在这里插入图片描述
根据聊天系统目的功用的不同,可分成三大类型:

闲聊式机器人:较有代表性的有微软小冰、微软小娜、苹果的 Siri、小 i 机器人等,主要以娱乐为目的。
**知识问答型机器人:**知识问答型系统,比如 Watson 系统最早在 2011 年的问答节目 Jeopardy 上击败了所有人类选手,赢得百万美金的奖金(当然,Watson 不止有知识问答的功能)。
**任务型机器人:**以完成某一领域的具体任务为导向,在工业界上应用较广泛,如订票系统、订餐系统等。

任务型聊天机器人以帮助用户完成某项具体任务为导向,进行单轮或多轮对话。比较简单的场景,比如智能家居,机器人能理解并执行主人指令即可,无需进行多轮对话。而在一些比较复杂的场景下,比如预订餐馆、预订机票、预定电影票等,需要的关键信息较多,按照人类的用语习惯,也不太可能一言以概述,因此常常需要机器人与用户间多轮交互后才能完成任务。

对于基于任务型的多轮对话系统,一般有四大模块:

自然语言理解(Spoken Language Understanding,SLU)
对话状态追踪(Dialogue State Tracking,DST)
对话策略(Dialogue Policy Learning,DPL)
自然语言生成(Natural Language Generation,NLG)

SLU 模型的目标是识别用户的意图(Intention Detection)以及词槽填充(Slot Filling)。用通俗的语言来讲,就是获取一些完成任务的关键信息,比如“我想吃西餐,价位便宜点的”这句话,其意图是“找餐厅”,词槽是“菜系”和“价格”,词槽值分别是“西餐”和“便宜”。DST 的目的是保存并更新对话状态,包括一些历史对话信息、当前的词槽填充情况、数据库查询结果等等。DP很好理解,即基于当前的对话状态及用户输入作出合适的反馈,而 NLG 则是将这个反馈转化为自然语言的过程。在具体实践中,可以把这四个模块当作单独的子任务来做,也可以用端到端的方式将四者串行,用一些深度学习、强化学习的技术实现系统搭建。

另外,我们也可以基于公开的聊天机器人平台根据个人需要搭建对话系统。比较具备代表性的平台有:百度的 AI 开放平台、海森智能的 ruyi.ai 系统、脸书的 wit.ai 平台、亚马逊的 Lex 等。这些平台实现了基本的技术构建和封装,只需要用户根据特定任务做一些较简单的诸如意图及词槽定义、相关模型选择的工作即可快速搭建聊天系统。

对话系统的关键技术

检索技术
基于检索技术的问答设计思想很简单,即收集问答对,机器根据用户输入查找到相似度最高的相关问题,而后将其答案返回。比如用户输入“上火的时候能不能吃荔枝?”,机器通过在现有的问答集里检索到相似度最高的问题“上火了可以吃荔枝吗?”,并且返回其正确答案“上火的时候不宜吃荔枝”。 那么,如何找到相似的问题?一般分为两种方法:

基于字符串的相似度匹配:用于字母语言,具体方法有编辑距离计算、杰卡德距离算法或者基于 Lucene 提供的模糊查询方法。值得一提的是,虽然中文不是由字母构成,但也可以拆分成具体的有限数量的字根(比如五笔输入法所采用的字根拆解法),并且由于汉语作为表意语言,源于图画,很多字根蕴含了非常丰富的语义内容,从这个角度来讲,基于字根的中文相似度匹配也是一种值得尝试的方法。
基于语义的相似度匹配:主要从词向量的角度来考虑,基于 Word2Vec 模型或者效果更好 GloVe、ELMo、Bert 等将词向量化后再进行后续运算。由于最终目的是句子间的相似度匹配,在词向量的基础上,我们还需要用一定方法表征句向量。常见的方法有将句子里的所有词向量均值平均,或者加权平均(越常见的词权重越小),比如 SIF 加权模型、词移距离模型。另外,类似于 Word2Vec 训练词向量的方法,以三个相邻的句子为一个三元组,直接训练句向量,代表模型有 Skip-Thought Vector, Quick-Thought Vectors。而在有监督的框架下,人们也尝试用多任务学习的方法,从各个不同的任务角度对同一句话进行编码,得到迁移性比较强的句向量,比如谷歌在 2018 年推出的 Universal Sentence Encoder。
有了这些文本相似度匹配的方法后,系统搭建似乎很简单,看起来只要找到那个相近的句子就行了。而实际上,假如问答集里的数据量特别大,导致匹配过程耗时太长怎么办?为了提高系统效率,人们一般用一些粗粒度的算法得到一些候选句子(Ranked phrases),比如应用倒排索引 (Inverted Index) 找到存在输入句子中至少一个单词的所有句子作为候选项,之后再用更精细化的算法从候选者里找答案。这其实类似于一些大企业招聘人才,由于求职者众多,往往根据学校背景筛选出一批候选者,再从中作全面系统的考察后找到最合适的人才,这其中难免会遗漏掉一些真正的人才,但整体而言这是比较兼顾资源耗费与准确性的方式。

意图识别和词槽填充
对于用户的输入,任务型聊天系统的第一任务便是自然语言理解,一般包括意图识别和词槽填充。

所谓意图识别,即解析出说话者的目的为何,实际上就是一个文本分类问题。比如一个订餐馆的系统,对于用户输入而言,可以有咨询信息、提供信息、表示感谢、拒绝推荐等意图。在比较简单的任务中,依靠一些规则的定义便可以识别出相关意图。在场景较复杂,意图种类较多,用户输入多变的情况下,往往应用机器学习的方法预测意图。

词槽,即与意图相关的一些必要属性,我们需要从用户输入中找到这些属性值。比如“天气查询”这个意图的词槽或者说必要属性有两个:时间和地点,机器需要用户提供这两方面的信息之后才能给出准确的回答。词槽填充其实是一个序列标注任务,一般用 BIO 作为标记法,B 为词槽值的开始(Begin),I 为延续(Inside),O 为无关信息(Outside)。

应用于序列标注的概率图模型有隐马尔科夫模型(Hidden Markov Model, HMM)、最大熵马尔科夫模型(Maximum Entropy Markov Model, MEMM)、条件随机场(Conditional Random Field, CRF)。其中,虽然 CRF 训练代价大,复杂度高,但与前两者相比,特征设计灵活,考虑全局性,效果更好。另外,循环神经网络也常应用于序列标注,能够自动学习输入语句间的隐含特征,但也有可能忽略一些很明显的标注关系层面的特征。因此,很多研究者选择把将以上两类方式结合进行序列标注,以发挥各自的优势。

虽然意图识别和词槽填充看上去是两个单独的任务,但实际上两者的关联性较大,只设计一个模型同时获取这两方面的信息会有不错效果。比如,常见的设计结构有 Bi-LSTM+CRF,结合循环神经网络和条件随机场,其中序列的每一步输入经由 LSTM 层和 CRF 层后输出为标记,而序列最终输出为意图。

对于初次接触意图识别和词槽填充这两个术语的朋友而言,往往因为其“表达的专业性陌生性”而感到难以理解。简单地说,人与人之间的对话,涉及的便是两个层面,中心思想和具体细节,在双方得到这两方面信息的基础上,对话才能顺利进行。对应地,中心思想指的便是意图识别,具体细节则是词槽填充。其实所有的术语和白话文一样,都是符号,只要和生活场景联系上了,也就不难理解了。

对话管理
对话管理包括对话状态追踪以及对话策略,前者相当于人脑的记忆功能,用于存储及更新所有的对话信息,后者相当于人脑的语言反馈机制,根据当前情形做出合理的动作反应,这是任务型对话系统中最关键也是最难操作的部分。同样地,这里也涉及到一系列术语,比如对话追踪、对话策略等,这里不再举例讲解,读者们可以结合生活中的对话行为去思考,相信会有更深的体会。

首先,关于对话状态追踪,需要定义对话状态的组成成分,只要是有助于对话策略的信息都可以予以追踪。一般可以概括为四大方面的信息:历史对话信息、当前用户行为、词槽填充情况、数据库查询情况。其中词槽填充情况是至关重要的信息,直接决定了对话进行的程度,比如针对“天气查询”的意图,“地点”和“时间”两个词槽都已填充,那么马上就能回复天气情况以结束对话。

然而,词槽填充并不是件简单的事,这是由于自然语言存在极大的不确定性或者说多样性。来看这样一句话:“我想吃西餐,不过好像有点油,日餐也行,如果没有的话中餐也好。”

在这句话中“西餐”、“日餐”和“中餐”均有出现,用户到底想吃什么的目的也不是很明确,那么到底要选哪一个作为词槽“菜系”的值?这似乎很难判断。所以实际上词槽值的输出不应该是某一确定的单一值,而是所有可能值的概率分布,比如输出 [0.4,0.4,0.2] 分别作为想吃西餐、日餐和中餐的概率。由于用户在对话过程中也会改变主意,此概率还会随着对话的进行产生变化。

有了对话状态后,根据一定策略产生行为的机制就叫对话策略。最直观的方式是设定一系列的规则,比如用有限自动状态机的方式实现此模块。比如要完成某一意图需要填充三个词槽 slot1、slot2 和 slot3 的信息,那么可以制定诸如此类的状态转移规则:没有 slot 被填充的时候,机器询问 slot1 的情况;slot1 被填充的情况下,机器询问 slot2 的情况;slot1, slot2 被填充的情况下,询问 slot3 的情况……总而言之,规则需要覆盖所有可能情况,以保证测试的时候不出纰漏。

所以,当场景稍微复杂点的时候,规则设计就会变得很繁琐;当有新的意图或词槽时,更改规则也非常麻烦;另一方面,从用户体验来看,机器的回馈也显得很呆板。而如果用深度学习的方法来习得机器行为,显然数据是一个很大的障碍,在多轮对话领域,数据量少、质量不高、缺少标签等都是短时间内难以解决的问题。

对话设计
在任务型多轮对话任务中,一个架构完备的多轮对话体系的构建不只涉及到众多算法,还需要考虑很多业务上的细节,比如多轮对话可能的流程、有哪些意图、有哪些槽位等,因此需要对业务非常地熟悉。在这里,以槽的设计所需注意的要点为例进行说明。

刚接触多轮对话的有些朋友可能会觉得,槽的设计不是很简单嘛,把对话场景中涉及到的关键实体罗列出来不就可以了?比如做一个医疗方面的对话,那么应该就有疾病、食物、药物、症状等。事实上,槽的罗列整理是一方面,难点在于如何整理清楚槽的类型、填充方式以及槽之间的关系。

首先,槽可分为必填与非必填。比如在一个订餐的系统中,餐馆位置、菜品、人数等应该是必填槽,而顾客或者餐馆的更多其它情况并非必填槽,比如顾客性别年龄、餐馆装修风格等,这些信息对于订餐的完成并非必须。

再比如,槽又可以分为词槽与接口槽:

词槽:利用用户话中关键词填写的槽。
接口槽:利用用户画像以及其他场景信息填写的槽。
比如,在应用端已知一个在杭州登录的用户问:“明天天气怎么样?”那么机器助手可以默认问的是“杭州”的天气,这里就没有利用对话中的信息而是应用了场景信息。因此,同一个槽,可能会存在多种填槽方式,并且有优先级。比如,在应用端已知一个在杭州登录的用户问:“我后天要坐火车去上海,有票吗?”那么机器助手如何来获取出发地这个槽位的信息呢,能否直接默认为“杭州”呢?肯定不行。在这里涉及到订票这一施行动作,对准确度要求当然比天气高,因此需要进行确认:“您是从杭州出发么?”但如果是用户主动提供出发地为杭州,二次确认就会显得冗余。所以在这里,出发地信息可来源于用户主动提供的优先级高于用户默认所在地信息。

而对于槽之间的关系,可分为平级槽和依赖槽。举个例子,在订车票的场景中,需要知道用户“出发的时间”、“地点”、“目的地”、“座位种类”——这四个槽之间,没有任何依赖关系。而在旅游规划的场景中,比如去“海边”和“山里”,每一个选择都会有影响到后续对话发展(预定“海景房”还是“山景房”),也即每个槽的填写结果会影响其它槽的填写。如果错将平级槽采用依赖槽关系来管理,就会出现信息的丢失。比如 A、B、C,三者本为平级槽关系,但却将其用 A->B->C 的依赖槽关系来管理,那即便用户问句中包含填写 B、C 槽组的信息,也可能会由于 A 槽组的未填写而造成 B、C 槽组的填写失败。如果错将依赖槽采用平级槽的关系来管理,就会出现信息的冗余,比如 A、B、C三者的关系为 A、A1->B、A2->C,那即便用户将值 A1 填入槽组 A 后,却仍然需要向用户询问本不需要的 C 槽组的填写信息。

以上仅是对话设计中的一小部分工作,在真正的应用中还需要考虑横向广泛纵向深度的方面,因此想成为一个合格的算法工程师,对业务的理解必不可少。

写在最后:
NLP 各方面:https://github.com/fighting41love/funNLP
NLP 开源项目:https://github.com/yongzhuo/nlp_xiaojiang

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

007的米奇妙妙屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值