点击上方“
LiveVideoStack”关注我们
▲扫描图中二维码或点击阅读原文
了解音视频技术大会更多信息
编者按:在贷后催收行业中,每个公司每天的录音量可达上万小时,因此语音识别功能对其非常重要。今天LiveVideoStack大会邀请到了洞听智能的张玉腾老师,为我们介绍在坐席辅助系统中,语音与文本的碰撞。
文/张玉腾
整理/LiveVideoStack
大家好!我是青岛洞听智能的算法工程师张玉腾,我们公司在去年四月份成立。在2016年,我们已经是联信集团的一个智能化部门,一直在做语音与文本相关的算法工作。
之前几位老师主要介绍了音视频直播的基础技术,而我今天分享的主要是偏向上层的应用,核心是语音转换文本后的相关技术。今天分享的坐席辅助系统是我们集团的一个主要产品,联信集团的主要业务是银行的信用卡贷后催收,集团的客户包括招商银行、浦发银行、中信银行等,互金领域的服务对象包括阿里巴巴(蚂蚁花呗、蚂蚁借呗)、美团、京东的白条等。当用户没有按时归还借款时,放贷公司首先进行内催,内催后仍未收回的案件将会委托给我们进行催收。在贷后催收行业,联信集团处于行业前三的位置,在全国有四十多家分公司,六千多名法务人员。法务人员每天的主要工作是打电话进行催收,所以我们有大量的语音数据。
01 坐席辅助系统介绍
首先,介绍坐席辅助系统中传统呼叫中心的痛点。传统坐席通过电话与客户沟通,并且需要手动地查询知识,即坐席在服务客户过程中需要打开多个文件,通过手动检索的方式查询知识点,操作繁琐且耗时。
我们想解决的第一个痛点是:新手岗前培训难,技能提升慢,离职率高。首先,存在业务知识多、复杂的问题,我们的业务主要是贷后催收,其中涉及到很多法律知识。有些客户逾期还款,可能是因为客户遇到了一些重大事故,比如家人生病,公司出现问题等,而并非客户不愿还款。此时,我们一方面要帮助银行追回借款,另一方面也要帮助客户(欠款人)解决生活中遇到的问题,因此我们建立了大量的知识库来帮助解决各类法律问题。新人的培训周期大概有两个月(新人指工作期限不满六个月的员工),而培训周期长也是一个较大的问题。此外,还有日常学习不及时的问题,这是因为银行的催收政策会根据央行的指示实时地变化,并且每个银行的催收策略不同,比如减免政策、逾期归还的最短时效等。催收政策可能在一个月内或半年内发生改变,如果不能及时地更新知识库,法务在催收的过程中会由于信息不对等而被客户投诉。最后,解决监控指导的问题是为了帮助管理层实时监督法务,使其在与客户沟通的过程中避免发生争吵,降低投诉率。
我们想解决的第二个痛点是:运营监控不及时,风险防范严重滞后。首先,大部分一线坐席的年龄在28岁以下,新人占比较高,每家分公司的新人占比大概在40%至60%之间,并且每个月会出现大量的人员变动。然后,工作内容比较单调,正常情况下每个坐席每天的通话时间在5至6个小时。此外,任务比较繁重,每个坐席每个月在手的案件量高达500,即每个月需要与500个欠款人沟通。再者,坐席的情绪波动较大,欠款人在被催收的过程中可能会抱怨和谩骂,坐席的情绪易受影响。最后,还有督导滞后的问题,质检和培训滞后,已发生的风险无法及时处理,负面影响很可能已经产生。
以下是我们提出的解决方案,主要面向坐席和管理者。首先,会将坐席和欠款人的对话实时翻译成文本。然后,根据翻译的文本分析客户意图,向坐席自动推送知识和话术。另外,黄色方框表示预警提醒,实时监控坐席与欠款人的对话,发现风险后实时给坐席预警,并通知监管人员,防止发生投诉。坐席地图方便管理者查看分公司或小组的坐席的工作情况,比如打了多少通电话,通话中没有解释清楚多少业务点。
解决方案的核心就是这个流程图。呼叫中心呼出后,在呼叫系统接入插件提取音频流。然后,对提取的音频流进行语音识别、语义理解和文本分析。最后,将其传送到坐席辅助系统的对话实时辅助和语音实时质检,并将提取出来的数据(客户画像、标签)推送到业务系统中。
适用的场景如图所示。
最后介绍系统集成。在各个系统中,只需要一套js代码并将其嵌入到CRM或业务系统中,就可以进行使用。
左图是预设,右图是核心部分,上面的催记是坐席在与欠款人沟通后,记录的欠款人的信息,比如是否有工作、工资、工作是否稳定、有无五险一金、婚姻状况等。这些信息以前需要坐席自己记录,现在可以直接生成,坐席可以一键复制。
核资推荐和应对施压是催收过程中的两大核心步骤。核资是为了了解欠款人的实际情况,应对施压是为了帮助坐席解决遇到的问题。
坐席可以快速检索知识库,这部分不详细介绍。
图的左边是实时翻译出的文字,上面是话术流程导航,每个业务有一套SOP流程,可以监督坐席在催收过程中是否有按照标准流程进行对话,其中绿色表示已完成该流程,灰色则表示没有完成该流程,因此可以实时监测坐席的催收是否合规。有些欠款人员是律师记者或保险人员,他们对相关法律比较熟悉,因此会提醒坐席在面对这类人群时要注意话术。
这里展示的是后台管理,可以看到每个坐席的通话状态。点击进入后,可以看到坐席当前的对话文本。
产品和技术的亮点如图所示。首先,我们自研了语音识别,后面会进行详细介绍。然后,我们还实现了多分类意图识别。另外,当前市场上已有很多坐席辅助系统,我们前期与循环智能进行了沟通,他们工作的核心是开发一个产品而没有深入一个行业。催收场景是比较复杂的,整个对话过程中的内容覆盖面很广,因为在与欠款人沟通时,可能会问到生活、工作、婚姻等。此时,需要构建的意图和知识库是非常庞大的,但市面上的坐席辅助系统只提供工具,需要自己进行知识的建立,而我们集团本身就有催收业务,因此建立了知识库。最后,我们现在还与清华大学的教授一起研究自生成模型推荐话术,后面再跟大家介绍。
02 实时语音技术
接下来,介绍我们遇到的技术问题和解决经验。
首先是铃声识别。整个对话会经过呼叫中心,电话接通前会有大段的铃声,由于业务的特殊性,电话接通率可能不足10%,因此会存在很多未接通的电话。电话未接通时,传统的呼叫中心返回的SIP码包括404、408和480等,根据状态码判断电话未接通的原因,比如停机、空号和暂停服务等,但无法定位准确的未接通原因,而要根据实际的铃声决定后续拨打的策略。在第一版中,我们通过语音识别来完成铃声识别,最大的问题是会给语音服务带来很大的压力。为了提高效果,后续我们采用了语音关键词识别技术,将停机、空号、关机等未接通电话的相关铃声的音频片段作为指令词来训练模型。现在得到的模型,1核支持80路并发,10核就可以覆盖一天40多万的电话拨打,识别单条1分钟录音耗时800ms,且识别准确率在99%以上。这里展示了一个录音,如果识别到类似的音频就判断为空号。
铃声识别的算法借鉴了关键词识别的算法结构,为了提高计算量,参考了图中展示的论文,将二阶卷积变成了时间卷积。二阶卷积中,需要从左到右进行相关操作,而在时间卷积中,只需完成一次从左到右的相关操作,故计算量减少了40倍。目前,整个模型的大小大约为5兆,系统的性能较高。
铃声识别的模型训练比较简单,只需将铃声片段提取出来即可,故难点在于铃声片段的提取。我们自己构建了CTC语音识别模型,大家若想使用相关模型,可以使用wenet或espnet中公开的模型。找到模型后,再采用ctc-segmentation方法提取铃声片段,得到关机、停机、通话中的音频片段。得到音频片段后,就可以构建铃声的数据集,提取出常见铃声的关键词后,还要提取其周边的关键词。然后,就可以进行模型的训练。
如图,提取了关键词为无法接通的音频片段作为数据集进行模型训练,所有的代码可以在图中展示的论文中得到。
我们从2019年开始做语音识别,这是因为集团有相关的需求,每天的录音量可达上万小时。我们最初买过阿里、腾讯等的语音识别产品,但其产品的识别率达不到我们的要求,于是我们开始自己做语音识别。最初,我们使用KALDI。然后,我们选择使用ESPnet,但ESPnet只能处理离线的任务,且必须GPU在上运行。wenet在2021年1月份开源,我们就开始使用wenet模型。
我的心得体会如下。
首先,离线模式下使用GPU更好,因为从我们的实践经验来看,实现BatchBeamsearch,性能会成倍提升(不过目前batch的搜索没有成熟的开源的方案,需要自己写),并且在单张2080ti上可实现100路并发,按单并发的成本计算,提升了40多倍,即大幅度地降低了成本。
其次,LM(语言模型)会带来负优化,大家使用在线语音识别时,可能会看到一些热词出现,这时通常会采用语言模型来优化场景里的关键词。我们分析了语言模型带来负优化的现象,发现造成这种现象的原因是我们的通话内容偏向通用领域(通话过程涉及生活中的各方面),所以语言模型会带来负优化。
另外,libtorch 1.10存在性能问题,在facebook上发布libtorch 1.10后,很多人反映使用时遇到了一些性能问题。官方表示libtorch 1.10会带来一定的性能提升,在第一次模型启动的推理过程中,会根据请求的数据大小进行算子的优化。但实际试验后,发现第一次算子优化的耗时非常长,会使性能降低3至4倍,并且优化结束后,速度仍很慢。后来我们发现,libtorch 1.9版本的性能最佳。
此外,libtorch全版本存在内存泄露的问题,长时间推理后内存会爆掉。针对这个问题,我们向wenet推送了一个内存优化的方案,即用jemalloc替换自带的malloc。
再者,pytorch和tensorflow偏向训练框架,在进行日常推理时,性能并不高,于是我们采用onnx进行了转换。采用onnx一方面是为了模型的统一,另一方面是因为onnx有onnxruntime,而onnxruntime是专门为推理设计的,因此能带来20%的性能提升。但在一些高核心的CPU上,进行多并发时,会带来5%左右的性能下降。这是因为普通的英特尔的CPU一般有16个物理核(32个虚拟核),我们可以做到32路并发,而之后我们在AMD机器(每个机器有64核)上进行64路并发时,发现性能下降。我们后来在网上发现其他人也提出了类似的问题,因此在高核心的机器上,我们仍采用pytorch。
最后,在AMD CPU上有AVX2指令集的可以通过fakeintel启用mkl对AVX2的优化。之前提到,英特尔会做开源的软件,但针对自己的CPU会有很多隐藏的优化。mkl是一个闭源的商业软件,对矩阵优化有20%的提升,但经mkl优化后的矩阵向量库在AMD CPU上会带来负优化的问题,对此可以通过预载入掩盖mkl对CPU的判断。
这是我们内部的一个测试集,左侧是使用的模型结构,叫做unified_conformer(wenet上的一个开源模型)。我们内部已经积累了1220小时催收语料,平均时长为5-15s,语速约12字/秒。可以看出语速非常快,这是因为通过快速对话可以减少欠债人的思考时间,给其带来压迫感,这也是由催收场景决定的。所有的录音来自于呼叫中心,音频格式都为8k 16bit。语言是中文普通话(会带有一点口音)。
测试数据集共1464条,共4.5小时,平均时间为11s。前两个wenetspeech的数据集是去年wenet社区开源的一万小时数据,是一个开源的最大的中文数据集,也在很多场景中取得了一些结果。这两个wenetspeech开源的模型的字错率都为16%,字准率分别约为85%和84%,故在我们的业务场景下无法使用这两个模型。后三行数据分别是在阿里的一句话识别API、讯飞的实时语音转写API和腾讯的录音文件识别API上得到的结果,效果最好的是阿里,其字错率为10.24%,字准率为91.31%。中间三行是我们自己测试的模型结果,可以看到,效果最好的是unified_conformer,其字错率为7.66%,字准率为93.6%。
这是使用jemalloc替换自身的malloc后,libtorch的内存泄露现象。左侧是替换以后的结果,右侧是没有替换的结果。实验场景是在40路并发下,模拟5次请求,每次请求停留10s至30s左右。从右侧的图可以看出,在5次并发后,内存被占用了12G,即占用的内存开始保持增长趋势,增长到12G时趋于稳定。从左侧的结果可以看出,使用gcmalloc替换后,占用的内存不会超过2G,基本保持在一个G左右,非常平稳。这个结果我们已经在社区提交,大家也对其进行了验证。
这是我们运行mkl库的加速的结果。上面一张图中,200代表当前实时语音请求的并发数,黄色的线表示延迟超过300ms的包,当黄色区域与绿色区域基本一致时,可以发现其中有一半的请求数的包的延迟大于300ms,即延迟较高。那么,如何使mkl库去识别CPU是否为intel的呢?可以直接写这样的代码,运行mkl时就会执行这个命令,并返回1(表示CPU是intel的),然后执行AVX2指令集的优化。
下面一张图是优化后的结果。300代表当前实时语音请求的并发数,可以看到没有一个超时。当并发数为600时,可以看到在后半段出现了超时的情况,但数量很少。在相同的计算资源条件下,在并发数为500的情况下,可以保证没有包的延迟超过300ms。
然后介绍语音增强。做语音增强是因为所有的语音来自于呼叫中心(无法接触坐席和欠债人的设备),由于坐席的设备质量参差不齐,有人的麦克风大概几十块至一百多块,会出现很多噪音,而欠债人方也可能因为信号不好带来天然噪音。语音增强主要是为了抑制噪声和增强人声,模型的核心思想是声源分离的思想,分离噪声和人声,最后得到两路音源,一路是纯噪声,一路是纯人声。图的下方展示了相关论文链接,大家可以自己搜索阅读。
03 自然语言处理技术
接下来,介绍自然语言处理技术。
之前的字准率大概为93%,即100个字中会有7个字出错,因此想用文本纠错的方法改正这些出错的字。我们基本每月优化一次语音识别功能,因为要积累一个月的数据来进行优化。但是,可以每两三天就进行一次文本的优化,即每两三天更新一次纠错模型,就可以满足大量新词出现的场景。文本纠错的模型比较通用,其判断哪些字是错误的,然后判断哪些字是正确的。我们的数据集中有很多是由于发音错误导致的错字,因此决定40%用相同发音的字符替换,30%用相似发音的字符替换,即70%的字符是用同音或相似音的字符去替换,从而得到错误数据。另外,5%用相似笔画的字符替换,这里也可以不进行此类替换,添加这项替换的作用是提高模型的泛化性。最后,10%随机替换,15%保持不变。构建得到数据集后,就可以采用模型进行实验。举个例子,最近刚解除封控,在我们这个领域,很容易将“风控”语音识别为“封控”,通过纠错可以将其改正。
然后介绍意图识别。意图识别是坐席辅助系统中耗时最久的工作。最初,我们从全国调用了30多个专家到总部来做坐席辅助系统。可以看到,目前分为两个大类,每个大类有140多个意图,总共为280多个意图。其实,和30多个专家讨论一个月后,我们最初决定设定1000个意图,但发现其长尾效应特别大,近600个意图的样本数量为几个或几十个,不超过一百个。因此在与专家讨论后,根据实际系统和产品的性质,最终决定设定140个意图。对于一些意图分类模型来说,140个意图是比较多的,尤其我们还是一个多意图模型(一句话可以包含几个意图)。
第一版模型是XLNET+魔改softmax,大家可以看一下魔改softmax中的第一条引用文献。普通的softmax中,可以设置阈值判断哪几个意图是多分类的,但魔改softmax可以使模型自动判断有几个意图是多分类的, 并且阈值是动态的。在第一版模型中,F1三成超过90%,四成超过85%。
我们想继续改造第二版模型,想做一个分层模型。首先,对140个意图进行归纳,然后定义21个大类,再在每个大类中分出几个小类。我们先做了大类模型,完成后发现准确率可达95%,然后再继续做21个小的分类模型。这样的思路是好的,但在上线时却出现了问题。这是因为,21个小模型加上1个大模型是22个模型,那么计算资源的消耗和机器成本就会非常高,需要很多GPU,实际部署比较困难。为了解决这个问题,我们思考如何将21个小的分类模型变得更小。对此,我们采用了p-tuning技术,固定BERT参数,只训练小规模的prompt参数(只有几兆),然后将其与BERT融合,即训练时与BERT参数一起训练,但BERT参数不变,只训练小规模的prompt参数,就可训练得到21个小的参数。在实际推理过程中,BERT参数(大概二百兆)加上21个小模型的参数(大概二百兆)组成约四百兆的模型,每个小模型得到了稳定的提升。训练完后,F1四成超过90%,六成超过85%。部署方案是从transformers到onnx,再到onnxruntime。
目前,在和黄老师一起做一个探索的工作。因为在完成坐席辅助系统中我们发现知识的构建耗费了40%的时间,于是我们思考若要在一个新公司中部署这一套新的系统,如何快速总结梳理该公司原有的业务知识。因此,我们决定和黄老师合作完成一个自然语言生成模型。黄老师已经探索了自然语言生成领域很多年,并开源了一个大模型。
目前,我们想与黄老师合作完成的一个目标是,利用公司历史的所有对话数据(十万人人对话)进行模型训练,使其自己学习对话策略和话术。这样在一个新公司中部署这套新系统时,只用让其学习该公司的历史对话语料,就可让机器自生成推荐对话策略和话术。这个想法还在实践中,大家若有兴趣也可参与进来。
最后,演示一下我们这套系统。
我今天的分享到此结束,谢谢大家!

▼识别二维码或猛戳下图订阅课程
喜欢我们的内容就点个“在看”吧!
继续阅读
阅读原文