赞
踩
本项目github链接:https://github.com/jzshq208886/wenet_asr
公司项目要求选择部署一种开源中文实时语音识别模型。需求总结如下:
语音识别(Automatic Speech Recognition,ASR)技术的定义为,将具有某种语言实际语义的音频自动转写为该语言的文本的技术。
传统语音识别模型分为两部分:声学模型和语言模型。声学模型负责将音频序列转化为音素序列,常见的音素比如汉语拼音、英文音标等,语言模型则负责将这些音素序列转化成文字序列。声学模型和语言模型在训练时并不需要耦合,可以独立训练。传统语音模型的劣势在于需要有发音字典,需要有音素的标注。
端到端(end to end, e2e)语音识别模型使用一体化的深度学习模型结构,直接将音频序列转化为文字序列。
资料参考:
一文入门端到端语音识别(多图详解)
语音识别根据训练时输入数据的方式分为流式识别与非流式识别,两者分别适用于实时场景和非实时场景。
流式识别,即持续接收音频流并实时转写为文字,其对于句中某一位置文字的预测仅基于其上文(已接收到的一部分音频流)。应用场景如实时会议字幕、输入法语音输入。
非流式识别,即将预录制的完整音频文件转写为文字,其对于句中某一位置文字的预测基于其上下文。应用场景如语音消息转文字等预录制音频转写。
技术实现上,流式识别与非流式识别模型的区别在于训练阶段使用训练数据的方法不同,即训练时某一位置输出的预测是基于上文还是上下文。
推理时,虽然也可以将实时接收到的非完整音频流,当作一段完整的短音频输入非流式识别模型,但这将导致模型在训练与推理阶段的不统一(非流式模型根据上下文预测某一位置的输出,然而实时语音流不具有完整的下文语义),推理效果相比训练阶段大幅下降。
换句话说,流式与非流式识别的根本区别在于,流式识别通过“仅使用上文语义进行训练”的方式,实现了语义不完整(话说一半)情况下的准确转写。
流式ASR模型接受单段音频并返回转写文字,因此想要实现实时更新字幕的效果,需要不断接收新的音频流,追加到旧音频流的末尾,并不断调用流式ASR模型,以实时更新转写结果。
该逻辑将引出以下问题:模型推理速度需要足够快。如果模型进行一次推理的周期大于接收新音频流的周期,则会导致模型计算“供不应求”,无法达到实时效果。使用指标“实时率”( R T F RTF RTF)来量化模型的实时效果:
R T F = T a u d i o t r a n s T a u d i o RTF = \frac{T_{audio\:trans}}{T_{audio}} RTF=TaudioTaudiotrans
其中, T a u d i o t r a n s T_{audio\:trans} Taudiotrans为模型转写一次已接收音频流的时间, T a u d i o T_{audio} Taudio为每次接收到的一段音频流的时间长度。当 R T F ≤ 1 RTF\leq1 RTF≤1时,模型可以达到实时效果,反之模型不能达到实时效果。
随着模型接收到新的音频流总长度增加,每次转写所需要的时间 T a u d i o t r a n s T_{audio\:trans} Taudiotrans增大,一段时间后将导致 R T F > 1 RTF>1 RTF>1。解决此问题的方法往往是,每隔一段时间将已接收音频流截断,固定截断点前的转写结果,只实时更新截断点后的转写结果。
资料参考:
流式语音识别原理和实现思路
模型选择是至关重要的一步。国内外知名开源ASR项目列举如下:
国外:Kaldi,Whisper(Open AI),DeepSpeech(Mozilla),Coqui,Julius,Vosk等
国内:WeNet,FunASR(阿里),PaddleSpeech(百度),MASR等
每个开源项目各有其擅长的领域。如语音识别界占据半壁江山的Kaldi引擎具有出色的底层逻辑和可扩展性,各种其他模型也具有高准确率、轻量级、跨平台等各自的优势。
考虑自身需求:
首先,以上项目全部支持离线部署在Linux CPU服务器,绝大多数支持流式;
其次,不考虑二次训练的前提下,国内预训练模型在中文转写效果上更加出色,首先考虑国内模型;
第三,优先考虑准确率,国内各项目paper展示的结果中,MASR字错率最低,WeNet紧随其后;
最后,WeNet工业开源落地较为成熟,具有强大社区与完整文档的支持,多家知名企业具有应用案例,因此最终选择WeNet。
接下来的需求是,将WeNet模型部署到内网服务器,并提供服务端与客户端分离的模型调用接口。实现思路如下:
服务端提供模型远程调用接口服务;客户端提供一个python模块,该模块中定义一个Recognizer类来实现模型的实时调用逻辑,用户可以引入该模块并实例化Recognizer对象,然后调用类方法,不断输入音频流以进行实时识别。
由于公司服务器不连外网,无法通过apt、pip或conda命令直接下载安装库包,因此需要在本地配置好conda环境,然后将环境迁移至服务器。
首先在本地创建一个与服务器相同系统的虚拟机(本例中为Ubuntu20.04),在虚拟机中装好服务端要用的conda环境,然后使用conda pack命令打包环境,得到一个<env-name>.tar.gz压缩包。将压缩包上传到服务器。
conda pack -n <env-name> # 本地打包名为<env-name>的conda环境
scp -P <port> <env-name>.tar.gz <username>@<ip>:/path/to/upload # 使用ssh协议将压缩包上传至服务器
在服务器conda环境目录下(本例为~/.conda/envs/)创建一个<env-name>文件夹,并将压缩包解压到其中,即可完成环境迁移。
mkdir ~/.conda/envs/<env-name> # 服务器在conda环境目录下创建<env-name>文件夹
tar -zxvf /path/to/<env-name>.tar.gz -C ~/.conda/envs/<env-name> # 将环境解压到<env-name>文件夹
conda activate <env-name> # 启动环境
提供远程接口服务有以下选择:
- 使用web框架,提供RESTful风格API。
RESTful 风格API以资源为导向,设计理念是每个网络资源都有唯一的URI,通过标准的HTTP方法操作。
优点: 1.标准化程度高,简洁易用;2.请求间独立,适用简单任务。
缺点: 1.基于HTTP协议,通信效率较低;2.复杂任务表达力较低。 开发框架选择(python):FastAPI,Flask,Django等。- 使用RPC框架,提供远程过程调用API。
RPC(Remote Procedure Call,远程过程调用)的设计初衷是像调用本地方法一样调用远程方法。
优点: 1.以方法调用为导向,可以隐藏通信细节,利用IDL提供类型检查等;2.多使用二进制通信协议,通信效率高。
缺点: 标准化程度低,搭建和使用复杂。
开发框架选择:gRPC等。
实时ASR由于对实时性有较高要求且频繁通信,故选择使用通信效率较高的gRPC框架。
使用python搭建gRPC服务时,需要使用Protocol Buffers数据语言定义服务接口和通信信息,然后将其编译为python代码,之后引用编译后的python代码进行服务内容定义和客户端调用。
本项目中,使用Protocol Buffers主要定义ASR模型调用和标点预测模型调用两种接口及其所需的数据通信,具体定义可见本项目源码的proto目录。
参考资料:
grpc 框架教程
gRPC 官方文档中文版
使用gRPC,引用Protobuf创建的服务接口,定义服务内容的标准方式为:创建一个继承自wenet_asr_pb2_grpc.WenetASRServicer的自定义类(wenet_asr_pb2_grpc.py是Protobuf编译生成的源码文件之一,WenetASRServicer字段系Protobuf定义),并在其中覆写方法以定义各服务接口的服务内容。
本项目中,主要需要覆写两个方法,分别用于封装WeNet ASR模型和ppasr标点预测模型的调用逻辑。同时服务端提供了加载WeNet模型时加入用户热词词典的功能。具体实现可见本项目源码的wenet_asr_server目录下的main.py文件。
客户端项目的目的为,提供一个名为Recognizer的python类,实现系统性的远程ASR接口调用,包括实时ASR调用逻辑的封装。用户可以像引用一个python库包一样引用该类,方便地通过接口输入实时音频流进行语音转写。
程序设计思路如下:
Recognizer类向用户提供两种调用逻辑,暴露以下方法作为主要接口:
Recognizer.recognize(),用于非实时模式下的单次ASR模型调用;
Recognizer.input(),用于实时模式下的音频流输入和实时逻辑自动执行。
此外,暴露以下方法或属性用于其他调控和获取:
Recognizer.connect() 和 Recognizer.disconnect(),用于连接和断开服务器;
Recognizer.start_streaming() 和 Recognizer.stop_streaming(),用于启动和结束实时模式;
Recognizer.result 属性,用于实时模式下获取当前时刻的转写结果信息。
其中,Recognizer.input() 方法在每次输入新数据流的同时,将自动执行实时ASR逻辑,包括调用ASR进行文本转写、文本截断固定、标点预测、标点截断固定等。以上全部结果信息可以通过 Recognizer.result 属性获取。该内在逻辑为客户端实时模式实现的核心。
关于“实时ASR逻辑”的解释,请参见本文章节2.2.2 实时语音识别逻辑原理。
客户端实现方式与使用方式分别可见本项目源码的wenet_asr_client目录下的 recognizer.py 和 README.md 文件。
本项目目前有以下两点改进思路:
欢迎下载使用本项目并提出建议,讨论更多的可能性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。