mxs 2019-07-01
本文是面向 Web 开发者所写,介绍关于“语音交互”的一篇科普性质的文章,希望借助这篇文章,让开发者了解到什么是语音交互,以及在 YodaOS 中的技术实现。不了解YodaOS是什么?点击这里回顾。
作为 Web 开发者,相信在面试时常会被问到“浏览器输入 URL 后发生了什么”这样的经典问题,这可以很好地帮助工程师理解整个 Web 的技术栈。因此,本文将从“若琪,今天杭州的天气”这一句话开始,尽量让大家了解到语音交互的技术全貌。
以下章节涉及到的知识点包括(可以先自行查阅之):
语音激活(VT)
语音识别(ASR)
语义理解(NLP)
语音合成(TTS)
技能(Skill)——语音交互时代的应用
如下是一次语音交互的流程:
从上图可以看出:
首先,用户(人类)通过智能设备上的麦克风获取音频信号数据(PCM),通过“语音激活”模块,它能正确地将带有激活词的数据转换成云端可以识别的音频数据(如 opus)。
接下来,由 VUI Client 会通过 WebSocket 上传音频数据,云端通过一系列算法最终将语音数据转换为两部分结果:
ASR:纯文本,表示这段音频数据中用户所说的话,比如“若琪今天的天气”。
NLP:JSON 格式的数据,NLP 也叫语义理解,通常会把每句话解析成两个重要的信息:
一部分叫意图(Intent),表示用户说这句话的目的,比如“今天的天气”,那我们可以获得这句话的意图是 query_weather,表示在查询天气;
另一部分是具体信息(Slots),一般在 JSON 中是一个对象,比如“今天的天气”会在 slots.date 中为 today,表示这句话询问的是“今天”的天气,类似也可以有像 slots.location 用于表示询问的是“哪儿”的天气;
ASR 和 NLP 会由 VUI Runtime 下发到具体的技能(Skill),每个技能通过解析 Intent 和 Slots 来做对应的处理,最终生成为一段文本,由 TTS 回复给用户。
以上其实就是一次语音交互的基本流程,接下来会按照不同的模块分别介绍。
语音激活流程
在说明这个流程之前,先来看看为什么需要它。当用户与一个智能设备交互时,由于媒介是语音(空气),所以不像传统的 GUI(PC、平板、手机)设备,它们拥有一个独自的媒介平台(如屏幕、按键等),所以交互目标是明确的。而生活中空气是共享的,这意味着在交互之前,首先要判断用户是不是想要跟你说话,比如你说“帮我开下灯”,可能是让你老婆去开灯,而不是若琪。
无论 Alexa、若琪、小爱同学,还是天猫精灵,首先需要给设备取一个悦耳的名字,这样设备才能知道你是否在跟他/她说话,这——便是语音激活模块要做的事儿。
然而要做好这么一件简单的事儿,可不容易。
首先我们先了解下什么是 PCM,维基百科上中文名称叫:脉冲编码调制。很难理解对吧?如果要把整个概念说清楚,可能需要涉及到很多声学和信号学的知识,这里就不拓展了。简单来说,你可以把它理解为,就是你说了一段话(语音),然后被麦克风记录成了二进制数据,这个原始数据就叫 PCM。
了解了 PCM 后,再来了解2个概念:
AEC,自音源消除。在智能音响上,通常都有麦克风和扬声器,麦克风在收音时,会把当前设备通过扬声器播放的声音也记录下来,因此 AEC 所做的就是从 PCM 数据中将设备本身播放的音频消除,减少干扰。
VT,激活算法模块。将 AEC 后的音频数据输入到 VT 算法/模型中,他会给出激活分数,最后通过分数判断是否激活。
在激活模块内部,从麦克风获取到语音数据后,会经过 AEC 处理,将扬声器的声音消除,接着会通过其他降噪算法去除一些额外的噪音,然后将最终处理过后的音频数据输入 VT,若模型判断是激活的话,就会把后面的(音频)数据通过 VUI Client 上传到云端 ASR,直到云端判断用户说完了并结束本次交互。
NLP 下发流程
前面讲到从云端会下发两种类型的数据:ASR 与 NLP,然而对于云端是如何生成这些数据并没有提及,这里我们先稍微讲解一些 NLP 的部分。
在 ASR 将音频数据转成文本后,会输入给 NLP 服务(云端),那么云端是怎么把一段简单的文本解析成本地想要的 JSON 格式呢?NLP 引入了领域(Domain)概念,比如“若琪,今天的天气”会被划分为天气领域,“我要开灯”是 IoT 领域,“我要听歌”则是音乐领域,类似的领域还有故事、时间、百科、导航和聊天等。
可以简单理解为领域是一个大的范围,表示用户想要跟设备交流的主题,而意图则是在这个主题下一个个需要设备帮助完成的任务。比如“今天的天气”,主题是天气,而意图则是问询。
有了“领域”的概念后,当我们获取到一串文本后,会经过预先训练好的模型以及预先定义好的规则和说法,就能判断出这段文本命中了什么领域和意图,然后再从文本中抽出相关的信息。最后将所有信息组合成一个完整的 JSON 下发至设备端。设备通过每条 NLP 命中领域或技能的不同,下发给对应的技能进行处理。
CloudApp
语音交互的输出相对比较单一,基本上是:TTS、音效以及多媒体音乐,因此这里有一个 VUI Rendering 的概念。
在 GUI 时代,渲染的输出是屏幕的可交互的控件、声音以及视频。而 VUI 就是 TTS、音效和多媒体音乐,相对于 GUI 来说,已经相当简单了。因此与 Web 一样,VUI 渲染也分为服务端渲染和本地渲染:
本地渲染:本地应用直接处理 NLP 数据,在本地执行业务逻辑,并最终播放 TTS、音效以及多媒体音乐。
服务端渲染:NLP 服务会将数据先发送到预先配置好的一台服务器,在服务器上执行业务逻辑,并分别将 TTS、音效和多媒体音乐转换为一条条指令(Directive),返回给 NLP 服务,最后设备端收到后,只需按照指令执行对应的操作即可。
一般把在服务端渲染的应用称作 CloudApp,其优点当然是实时更新,开发快捷简便,与 Web 的运行机制类似,缺点则是能力受限,拓展依赖于协议本身的能力(类似于浏览器沙箱)。
技能生命周期
无论是本地还是云端,技能跟我们所熟知的页面一样具有其生命周期,VUI Runtime 会对其进行管理。在 YodaOS 中,每个技能可以由两种方式启动:
用户通过语音 NLP 下发命令并启动。
用户通过设备按键触发启动。
每个技能在启动后,就会处于技能栈栈顶,表示当前技能正在运行,此时可以控制设备的输出内容(TTS、音效与多媒体)。与此同时,若之前有技能已经在运行,则会将之前的技能销毁或暂停,这取决于他是即时(CUT)还是场景(SCENE)技能:
CUT:用于单次会话的技能,因此有新的技能命中时,会毫不犹豫地结束(销毁)该技能。
SCENE:用于长时间需要交互的技能,如音乐播放、游戏或有声故事。若新请求的技能是 CUT,会先暂停上一个技能,待 CUT 结束后恢复;若新请求的技能是 SCENE,则会替换掉之前的技能。
总结
其实一次语音交互远不止上文所提到的这些流程,这里也仅仅希望抛砖引玉,以及给对于语音交互感兴趣的工程师分享出我们在做 YodaOS 的一些心得,最后欢迎大家关注我们的 VUI Runtime:https://github.com/yodaos-pro...。