随着语音交互的普及,我们首先用到的最多的就是语音技能,比如我们让智能音箱唱歌、查天气、讲笑话等,这些都是语音技能。今天我们就来聊聊如何从零到一的设计一个语音技能。
一、基础信息介绍
在设计语音技能之前,我们首先要掌握技能用到的一些基础定义,每家公司可能叫法上面会有区别,但是都大同小异。
1、基础定义
我们在聊聊语音技能常见的一些名词和定义,主要有领域(domain),意图(intent)和槽位(slot),这些都是语音技能必不可少的一些参数内容。
领域:听到这个词,我们就感觉到了约束性,其实领域这个词就是约束语音技能范围的意思。一般一个语音技能,会有一个明确的领域,剩下的内容都在这个领域里面做处理。
意图:顾名思义就是判断用户具体要做什么的意思,领域可以是一个大范围的事情,而意图是领域中的一个小分类。比如让机器人跳舞是一个领域的事情,那么“开始跳舞”和“停止跳舞”就是领域下的意图的事情。意图一般会非常明确,会有明确的边界,在自然语言处理中属于封闭域的问题。
槽位:根据槽位的有无,语音技能可以分为有槽位的和没有槽位的。槽位一般就是指我们前面说到的实体词,用来做信息抽取用的,补全和完善用户的意图。比如“唱首歌”就是没有槽位的语音技能,只需要知道是唱歌的意图就可以。而查天气是常用的有槽位的语音技能,除了识别出这是一个查天气的意图之外,机器人还要知道要查什么时间、什么地点的天气,时间和地点在这里就是槽位信息。有槽位信息的一般还会有默认槽位,就是没有槽位信息的时候,直接使用默认的槽位信息,从而保证语音技能的正常。常见的就是“天气预报”,默认的就是当地当天的天气。
![8b8eb26f9c62900c6ba0bacb2f65e87e.png](https://i-blog.csdnimg.cn/blog_migrate/9f64c5b5b7fd8c40c3cd36c0997dd651.jpeg)
2、底层逻辑
再聊聊一下目前语音技能的底层逻辑,基于什么能力实现的。
目前大部分做语音技能的公司,都是用正则表达式来写的,就是基于一些文本规则,作为约束条件,筛选出来明确的意图。抽取的槽位也是基于规则,或者穷举的方式。这样做的好处是改动方便,以及改动后的影响好评估,而且冷启动非常方便,甚至可以做到每天迭代。缺点也同样明确,泛化能力弱,没有学习能力。
也有一小部分公司已经开始使用算法做语音技能了。语音技能本质是一个意图识别的事情,而意图识别实际上又是一个分类问题,有基于传统机器学习的SVM,基于深度学习的CNN、LSTM、RCNN、C-LSTM等。槽位识别实际上是一种序列标记的任务,有基于传统机器学习的DBN、SVM,也有基于深度学习的LSTM、Bi-RNN等。用算法做的优点就是泛化能力强,有一定的学习能力;缺点就是成本高,适合复杂技能后期迭代的方向。
![f9e8bd746803189aec6071c048d66695.png](https://i-blog.csdnimg.cn/blog_migrate/e6a008072ef6f25c751cd884496f36e1.png)
二、语音技能的定义
在开始动手做语音技能的之前,要先对语音技能进行定义,知道技能的边界,要有明确的反馈逻辑在里面。我们这里用“查天气”这个烂大街,也是最典型的技能来举例子。
1、定义技能
我们要明白为什么做“查天气”这个技能,以及要做到多细。原因可能是我们就觉得这个技能很基础,用户都被教育过了,必须要有。也可能是我们看用户的交互日志,发现每天都有很多人有这个意图,现在是未满足状态,值得单拿出来作为一个技能。还可能是老板觉得竞争对手有了,我们也要做。
假设我们是觉得是技能很基础,必须要做。接下来我们就要考虑怎么定义这个技能,需要注意以下几点:
1)要明确技能的边界,就是那些query是该技能要识别的,需要有一个明确的定义。这个看起来很容易,其实执行起来会很纠结,因为自然语言本身就有一定的歧异性。就拿“查天气”举例子,比如“今天该穿什么?”、“明天能不能出去玩”算不算查天气的意图,都是要明确的。其实最让你纠结的往往是模糊的语义,算作技能也不为错,不算吧,又觉得用户可能有这个意思。所以明确的边界的时候,有三种处理逻辑:
- 只处理特别明确的意图,不care模糊的语义,比如只处理“明天天气”、“查一下天气”等这样的。
![2a247a5b679a0e1391725e4b9a50bd48.png](https://i-blog.csdnimg.cn/blog_migrate/fa8c1aaafb084bbb5685d08a31d5e363.jpeg)
- 模糊的意图也一起处理,都归为该技能,比如“明天该穿秋裤吗?”也属于该意图,和“明天天气”一起处理。
![2a247a5b679a0e1391725e4b9a50bd48.png](https://i-blog.csdnimg.cn/blog_migrate/fa8c1aaafb084bbb5685d08a31d5e363.jpeg)
- 还有一种精细化的处理,把明确的意图和模糊的意图分开处理,比如可以让明确意图直接执行,模糊意图选择反问,用户确认后执行。用户一旦确认后,以后这句话就归位了明确意图。比如用户问“今天穿什么出去好呢?”,计算机回答“您是不是想查询今天的天气?”,用户回答“是的”,计算机回答“今天北京气温20度,适合……”,然后下次用户问“今天穿什么出去好呢”,计算机就可以回答“今天北京气温20度,适合……”,反之亦然。当然如果模糊问的问法比较多,可以专门做意图优化。
![d46cb888f00bd82a88f8d4ab28c1d7f7.png](https://i-blog.csdnimg.cn/blog_migrate/eb73642c62bf76422f23fc635fe4f944.jpeg)
2)明确技能的领域、意图和槽位的信息。就拿“查天气”这个技能为例,领域我们一般设置为“weather”,但是意图定义就会有两种方案,一种是简单的,只有一个意图,比如意图也是“weather”,还有一种是精细化的处理,有若干饿意图,比如“北京空气质量?”算是“get_haze”,“今天会下雨吗?”算是“get_rain”等,就是每个不一样的问法,对应不同的意图。本质上越精细化的技能,给用户的体验会越好。
![98f971e6976a0bf759650b533dd3e3dc.png](https://i-blog.csdnimg.cn/blog_migrate/f2377e4822701a3d0a633aee6fd380db.jpeg)
3.)考虑技能内多轮的支持,以及支持的效果。由于自然语言先天是具有多轮属性的,很多时候需要借助上轮的信息,才能理解这句话,在做语音技能的时候,也要考虑到这方面的可能。比如用户先说“明天天气”,紧接着,用户又说“后天呢?”,这个时候是否考虑支持,都需要在定义技能的时候明确。像用户隔多久的多轮需要支持,支持的逻辑,我们这里就不展开了。
![5cc06c360e9ef1361e284e44bae1e1ed.png](https://i-blog.csdnimg.cn/blog_migrate/5ec02e80a2f905973b8b70ce5251d0ab.jpeg)
定义好了技能,就知道了这个技能能干什么,方便后面的测试同学测试,也知道未来要迭代的方向。一般如果没有数据支撑的话,建议先做最基础的就可以,边界越小越好。
2、触发技能反馈
反馈这块一方面依赖于产品底层的设计;另一方面依赖于产品形态,按照有无屏幕,可以简单的分为两种产品形态:有屏幕和没有屏幕。这两方面结合,才能设计出一个人性化的体验。
产品的底层设计要考虑意图要不要细化,比如“今天有雾霾吗?”和“今天天气怎么样?”这两种问法有没有必要分开处理,设置不一样的回复内容。还要考虑如果槽位超出技能的边界怎么处理,比如“三年后的天气预报”,这个时候我们需要怎么反馈,都是需要在语音技能定义的时候写清楚。
具体怎么展示,以及怎么回复,就要依赖于产品形态考虑。有屏幕的可能更多的信息会通过屏幕展示,语音只是做到一个提醒的效果,有些场景甚至都不需要语音,而没有屏幕的就要考虑语音播报的表达方式,要注意文字的长度,都播报那些内容,播报的先后顺序,甚至播报的句式的丰富度都要考虑。
技能的反馈,是用户直接能够感受到的,其重要心怎么强调都不为过,这块可以参考语音交互的设计规则。
![cabfe794af967476394b56b7af06253c.png](https://i-blog.csdnimg.cn/blog_migrate/5a856b9c81739dc08d41e6b6782c70b1.png)
三、数据的准备
前面说到的都是产品设计的时候要考虑到的问题,如果你把技能已经设计的差不多的时候,就可以准备这个意图的训练和测试数据,因为我们最终语音技能的开发是基于数据的,数据覆盖的越全面,技能的效果越好。
无论训练数据还是测试数据,简单可以把数据分为两部分,正例和反例。
1、正例数据
正例数据指的是正常的触发文本,就是你设计的触发query,像“明天天气怎么样”、“天气预报”、“查询天气预报”等,这些都是我们定义的“查天气”的意图。
一般在准备正例的数据时,最关心的是数据的来源,还有数据的丰富性。
如果是冷启动的话,建议团队内部,或者有专门的数据部门,进行人肉泛化,就是每个人自己写几条符合意图的触发query。这里亚马逊在做智能音箱的时候有一个要求,就是每个意图的正例训练集的数据,不能少于30条(测试数据也一样)。数据来源就是公司员工制造,数据丰富性就是依靠数据量标准约束。
如果是语音能力已经有了,每天都有大量的交互数据,我们就可以从真实的交互中拿数据。导出交互日志需要逐一标注,从中找到属于该意图的数据。这些数据的好处就是更加贴近用户真实的情况,缺点就是成本会比较高,但一般都是可控的。基于现有的交互数据标注,可以轻轻松松准备30该意图的数据,建议越多越好,100条以上为最佳。数据来源就是用户的交互,数据丰富性是依靠用户的。
![4441e668580fe2c5f991cbecff670cb2.png](https://i-blog.csdnimg.cn/blog_migrate/5c1555c708fa4fe54d086d4372651391.jpeg)
2、反例数据
反例数据指的是非该意图的数据,就是除了正例数据,理论上所有的数据都是反例数据。像“明天要是不下雨就好了”、“我知道现在在下雨”、“我不想查天气预报”等,这些都不是我们定义的“查天气”的意图。
很多时候,我们做语音技能的时候,是很容易忽略反例数据的。反例数据最好是能够有意图相关的关键词,数据量最好可以和正例数据一样多。在准备反例数据的时候,要注意一些意图相反的操作,比如“我不想查天气预报”,这是比较典型的反例数据。往往反例数据比正例数据要难收集,尤其是高质量的反例数据,一般交互日志是最好的资源。
![cc060149672cc27fdf5d004570ba1458.png](https://i-blog.csdnimg.cn/blog_migrate/9a7b3b1d739fe0c91899f5d3956d209a.png)
正例数据可以保证准确率,而反例数据可以减少误召回,提高召回率。
四、语音技能的实现
训练数据准备好之后,就是技能的实现了,这块需要工程师的支持。有些公司是工程师直接写语音技能的逻辑,有些公司是会提供一个平台,通过培训,让产品经理和运营同学也可以写。
这里就会用到一些基础能力,当一句query传过来,首先会使用中文分词对这句话进行分词,比如“北京明天天气怎么样”,会被分为“北京”“明天”“天气”“怎么样”,然后就是命名实体识别,比如“北京”就是地点实体,“明天”就是时间实体,对应的就是语音技能的槽位。最后就是匹配我们写的正则表达式,这里就不过多赘述,感兴趣的同学可以搜搜看。
中文分词:为什么叫中文分词呢?因为英文是以词为单位的,词和词之间是依靠空格和标点隔开的,而中文是以字为单位的,一句话的所有字是连在一起的。所以就需要算法把一句话切分成有意义的词,这就是中文分词,也叫切词,主要为了NLU后面处理做准备。了解锤子手机的人可能知道上面有一个叫做“大爆炸”的功能,就是基于该算法的。这是NLU最底层的能力,一般都是用的开源的算法,大家能力相差不大,基本可以保证准确率在90%以上。
![8166990d95731f99b7bb2d56fadd3d3c.png](https://i-blog.csdnimg.cn/blog_migrate/ee502f17a73f5b3d8d50304b8115c4c5.png)
命名实体识别:词性标注是把每个词的词性标注出来,而命名实体识别是指识别文本中具有特定意义的实体,包括人名、地名、时间等。一般来说,命名实体识别的任务就是识别出待处理文本中三大类(实体类、时间类和数字类)、七小类(人名、机构名、地名、时间、日期、货币和百分比)命名实体。这个一般会根据本身业务的需求进行调整,不做明确的限制。
![9e5535ff232d177c64c2cca27696b85c.png](https://i-blog.csdnimg.cn/blog_migrate/b927da335539facf53c06d5eebc6f285.png)
还有比较简单的实现方式,就是通过穷举实体+写正则的方式,而不需要用到模型去做处理。比如“查天气”这个技能,我们通过穷举的方式,把表达地点和时间的词语都穷举出来,然后分别存到词典里面。最后使用这两个词典写一些正则表达式,用来覆盖我们准备的训练集。这样做的的前提需要是实体词是可以穷举的,否者就会遇到召回率很低的问题。
除了实现方法,我们还要考虑实现过程的效率,以及实现效果怎么样。
五、测试验收效果
一般的语音技能开发会比较快,开发完成之后就是验收了,验收最关心的指标是精准率和召回率。
1、验收指标介绍
本质上就是计算机判断了一次,然后人工判断了一次,默认以人工判断的为真实标签,计算机判断的为预测标签,如下表:
![8a4914824584f99d5293388dada26d95.png](https://i-blog.csdnimg.cn/blog_migrate/107c01e0408077f63b8e0da8c6e3c354.png)
精准率(Precision):计算机认为对的数据中,有多少判断对了
精准率表示的是预测正例的样本中有多少是真正类。预测为正例有两种可能,一种就是真正类(TP),另一种就是假正类(FP)。公式表示如下:
![f687fbb25b813198a49bef3df1b09e25.png](https://i-blog.csdnimg.cn/blog_migrate/4918ff05db07cd28bd2e8fc5b5eb6b66.png)
召回率(Recall):正确的样本中,有多少被计算机找出来了
召回率表示的是样本中的正例有多少被预测正确了。也只有两种可能,一种是真正类(TP),另一种就是假反类(FN)。公式表示如下:
![6c413b5481b8518dfd3ea48df6c42077.png](https://i-blog.csdnimg.cn/blog_migrate/5e25cf11c6b78e99078f36f26780e672.png)
其实就是分母不同,一个分母是预测标签的正例数,另一个真实标签的正例数。一般情况这两个指标都会在0-1之间,越趋近于1,语音技能的效果越好。
![d7c8d59da7337ab963b468170f63462d.png](https://i-blog.csdnimg.cn/blog_migrate/851e2897badab00924d453eee7d2d3aa.png)
2、验收步骤
验收这里分为两个步骤,一个NLU批量验证,一个是端到端验证,都可以用测试集来验证。
NLU批量验证就是把测试集的query,全部通过语音技能的逻辑跑一边,一般用来验证技能在NLU上面的效果。通常这一步只会测试新增的技能,单独测试这个技能的效果,主要关心的是精准率和召回率,这一步理论上来说,必须这两个指标都要达到95以上。
端到端验证是模拟用户正常使用,需要把技能放在整个语音链路上面,来观察语音技能在实际情况中的表现。通常这一步才会发现一些问题,比如语音技能之间的冲突,甚至会发现ASR识别不对的情况。这一步主要关心响应时间、语音技能是否正常等情
况。
![d7bd6073be69e576f2b574c9410073ca.png](https://i-blog.csdnimg.cn/blog_migrate/762679d59408caa93bb3f0d38f38fb46.png)
测试一定要把好最后一道关卡,保证语音技能的精准率和召回率,同时也要测试技能之间优先级的关系,是否技能之间会出现优先级的问题。如果有多轮的语音技能,也要测试多轮的效果。
六、总结
做一个语音技能,产品首先要有一个明确的定义,其次就是基于产品定义准备训练集和测试集,然后基于训练集完成技能的开发,最后使用测试集进行验证。