怎样让程序更好地理解一句话的意思,是我最近一直在思考的问题。
尝试做语音助手最大的难题是,怎么理解用户说话的意图?用户说,我要打电话给张三。你得先让程序明白用户是想打电话,然后你才知道该让程序做什么。
因为要做的不是通用语音助手,要结合产品的业务和使用场景来解决问题。但不同行业之间业务相差很大,市面上很难有提供基于行业的解决文案。
语义分析
语音识别技术相对成熟,可以直接采用第三方解决方案。自己要做的是,在识别出语音后分析用户说的话,知道用户想要干什么?然后再让程序执行相关的业务。从语音业务流程来看,完整的业务过程有三段:
- 语音识别。
- 语义分析。
- 业务处理。
语音识别相当于人的耳朵,能听得到别人说的话以及在说什么,把语音转成文字。但对机器来说,知道人说话的内容和说话的意思是两回事。电话是什么,张三是什么,打是什么,它都不知道。不知道别人说话的意思,那就没法驱动业务,业务就没法往下走了。
怎么才能理解别人说话的内容,知道说话人的意图?通过某种方法来分析出一句话,或一段文字的意思,就是语义分析,它相当于人的大脑。
知道了别人说话的意思,就能告诉程序要处理什么业务,做什么事情了,就像人通过四肢解决问题。
用户说话的内容可能会千奇百怪,如果要分析用户说的每一句的意思,这是不现实的。但就产品而言,我就只关心与业务相关的内容。设计方案的要点是,从用语习惯上出发,监测业务关联敏感词。在敏感词基础上,设计一个合适的筛选器。
就像过滤东西一样,每个筛选器只能筛选出某些特定的话。提前定义好筛选器,有哪些意思包含哪些词语的话会被筛选中,那么只要一句话被筛选中,通过筛选器就能知道这句话的意思。也就是两步:
- 创建一个筛选器。
- 定义筛选器的意思。
这样,就做到了语义识别。怎么设计一个好的筛选器成了最关心的内容。在方案设计过程中,经历了三次改进。
一、关键词匹配
通过关键词的来预设业务是最开始想到的方案。比如说要判断用户有打电话的意图,
- 提前设定关键词「电话」。
- 将关键词「电话」与打电话的业务关联一起。
- 检测说话内容包含「电话」,就触发打电话的业务,调起拨打电话的界面。
这个方法的特点是判定逻辑容易设计,也很容易理解。如果我需要增加一个发短信语音识别,只需要在后台增加一个关键词「短信」,把它的同义词:信息,消息,也都放到一起。当用户说话内容包含其中任何一个词,我就认为用户是想要发短信。所以,业务配置起来也非常简单。
方案的缺点很明显,同一个关键词,可能会被多个不同意思的语句涵盖。打电话给张三,查看张三的电话,都包含「电话」,但是不同的意思。中间还尝试过使用多个关键词来匹配,但都无法避免关键问题:因为关键词的开放性,很难准确知道用户说话的意思。
二、ABNF 语法识别
想要改进「关键词匹配」方案的问题,需要对在关键词的开放性上做进一步的收敛。在找更合适的方案中,了解科大讯飞的私有语义方案,使用 ABNF 语法编写文件,来精准识别用户的语义。ABNF 是一套语法规范,是用来编程语言的。就像其它程序语言,是一套计算机语言规范。比如 C 语言,可以用来编程软件。
下面是科大讯飞用 ABNF 语法编写的一个天气查询的筛选器。
这个筛选器的设计逻辑是,列出用户可能会说的话语,当用户说的话与预设完全匹配时,就知道精准地知道用户的意图。利用 ABNF 语法可以尽可能多地涵盖用户可能说的话。
$main 变量定义了说话的内容,在 $main 变量右边有很多不同的变量,$want, $query等。不同变量之间有各种符号,|,[],()。不同符号表达了变量之间的不同的组合关系。
符号 | 表示或的意思。$weather = 天气 | 气候。意思说该变量可以为天气
,也可以是气候
。
符号 [] 表示可有可无的意思。$main 中的 [我] 意思是当用户说话的内容有我
,没有我
时都是可以的。
$main 就代表了多种变量不同关系的组合结果,当用户说不同的内容时,都能被匹配到,比如:
- 我想要查北京今天的天气。
- 查询广州后天天气情况。
- 查一下上海明天的气候怎么样。
第二个方案的好处是能通过完全匹配来精准了解用户说话内容的意思。不好的地方正好跟第一个方案是相对的,不够灵活。因为完全匹配,要想涵盖尽可能多的说法,就得尽可能地想到用户会说的方式,然后再将其转换成 ABNF 语法文件。
如何使用 ABNF 语法文件来定义筛选器成了一件棘手的工作。定义业务是类似编辑类的工作,属于管理员做的事情,但管理员不懂 ABNF 语法,做不了这个事情。
因为这个,我还设计一个GUI,希望让普通的人能通过界面的操作即可定义筛选器,后台会自动生成对应的 ABNF 文件,而不用直接编写复杂的 ABNF 源文件。
如上图,句式语法
是对说话内容的参数设置。管理员正常录入一句话,然后选中其中的某个字或词语即可指定它在句子中的对应同义词,以及与其它词语的组合关系。
选中「我要」,界面即自动会生成参数1,如果你指定「我要」是非必选,即表示,用户说的话语可包含或不包含「我要」这个字。选中「手机」,界面会自动生成参数2, 你可以指定参数2可引用一组词:手机,电视,空调,冰箱。当用户不说手机,说电视或空调,同样会识到。
在方案评估过程中还是放弃了这个方案:
- GUI 的配置降低了 ABNF 语法文件的编写门槛,但对没有编程基础的操作人员,理解起来并不简单。
- 完全匹配要求录入的语法得兼顾尽可能多的说法,否则系统无法识别用户说话的内容。这对任何人来说,都是一个巨大的挑战。
三、基于 Frame semantic 的语义框架
「关键词匹配」方案过于开放,「ABNF 语法」方案过于局限。如果在两者之间能求得一个平衡,就能找到一个更为合适的方案。我们的思考的思路:
- 一个正常人在表达自己的意图时,都会有业务对象。我想发短信给张三,短信是其中的业务对象。
- 业务对象包含很多细节信息。短信有发送人,内容,短信类型。
近一步探索,用户说,发短信,查看天气,打电话,都是典型的动宾短语,能基本表达出用户的意图。至于天气,电话,信息相关细节都是与宾语相关的,属于宾语的属性。
如果我们提前定义了信息的属性,以及每个属性可能对应的值。当用户提到的时候我们就能知道用户在说什么。(图)对短信来说,短信属性有,内容,发送人,消息类型等发。
像上面的短信,
- 我们认为它是一个领域,包含业务对象,以及与业务对象想关的数据和业务。比如「网购」,「酒店」,「智能家居」等。
- 对领域内的数据操作,能够表达了用户意图,一般是是动宾结构的形式。比如查询短信,发送短信。
- 领域内的对象都有自己的属性。短信有短信的属性,天气有天气的属性。
按照上面的分析可以建立一个简单的语义框架:
基于上面的语义框架就可以做更好的筛选器。
- 筛选器的核心就是意图的结构,一般是
动作
+业务
对象。不同筛选器因为业务的不同分属于不同的领域。 - 用户正常说完一句话后,使用自然语言处理对句子进行分词,句法分析,词性标。
- 对处理后的结果进行分析,识别出说话内容所属领域,提取其中的业务对象以及属性信息。
- 找到匹配的筛选器。
相对前两种方案,该方案在原理上能够更精准地理解语义,但在实践过程中也遇到了一些困难:
1)知识库的积累与丰富
语义的框架的基础是建立在已有的知识体系之上的。如果知识库里的苹果只是一种水果,当用户说想要一部苹果手机时,系统并不知道这里苹果不是水果,而是一个品牌。这意味着,要让系统能够知道某个词是什么意思,就是先要录入相关的信息。要想让系统理解用户说的尽可能多的词,并且能正常理解相应的意思,就得构建一个庞大的知识库。这个知识库里的信息,如何整理,归类,都是巨大的任务。
如果知识库的内容要靠人工来主动添加完善,这来速度相对于用户的需求来说,是非常慢。能不能让机器学习,爬取其它渠道的知识,然后进行清洗,筛选,加入到我们的库里面,这同样是个挑战,也是一件很有意思的思情。
2)领域的识别
前期的时候,领域不多,语义的识别困难不大。随着领域的增加,语义的理解可以会因为多个领域的存在产生干扰。每一次领域内容的丰富都要考虑对其它领域可能产生的干扰。
3)上下文的支持。
说深圳的天气怎么样,系统可以知道用户是想问深圳的天气。当用户接着说,那么东莞呢,系统就不一定知道了。系统只有在关联前一句话的情况下才能明白用户想问的内容,这要求系统要有关联上下文的能力。具有上下文的对话更符合正常人与人之间的自然沟通方式,不需要在每次表达意思的时候,都得完整地说出一句话。
语义分析对我来说是一个全新知识领域,之前没有过触,前段时间在摸过的过程中遇到了很多问题。特别感谢网友 @unbuilt 提供了很多的帮助,才有最后一套方案的构思。