飞桨PaddleSpeech语音技术课程— 语音识别-流式服务
Posted 小湉湉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了飞桨PaddleSpeech语音技术课程— 语音识别-流式服务相关的知识,希望对你有一定的参考价值。
FastAPI websocket 流式语音识别服务
0. 背景
流式语音识别(Streaming ASR)或者在线语音识别(Online ASR) 是随着输入语音的数据不断增加,实时给出语音识别的文本结果。与之相对的是非实时或者离线语音识别,是传入完整的音频数据,一次给出整个音频的语音识别文本结果。
训练完一个流式的语音识别模型之后,需要将流式语音识别模型封装成一个服务,使用者通过网络访问流式语音识别服务实时获取音频的文本内容。
流式语音识别服务在实时字幕,视频直播,实时会议转写,输入法等场景都有大规模的应用。
1. Websocket 协议
在流式语音识别中,客户端client和服务端server需要进行长时间进行数据交互,client端不断地将数据传入到服务端,server需要将实时识别的文本返回给client端,因此client需要和server保持长时间的网络连接。
PaddleSpeech采用Websocket协议,保证client和server可以长时间保持网络连接。
WebSocket 协议支持全双工通信,client端和server端可以在一个网络连接上收发消息,使用WebSocket协议,可以实现client不断地向server端发送数据,进行实时语音识别。
# 下载流式ASR的demo视频
!mkdir -p work/source/
!test -f work/source/streaming_asr_demo.mp4 || wget -c https://paddlespeech.bj.bcebos.com/demos/asr_demos/streaming_asr_demo.mp4 -P work/source/
import IPython.display as dp
from IPython.display import html
html_str = '''
<video controls width="600" height="360" src="">animation</video>
'''.format("work/source/streaming_asr_demo.mov")
dp.display(HTML(html_str))
2. 测试服务
2.1 PaddleSpeech 流式协议
在PaddleSpeech中,client端使用websocket协议与server建立连接进行通信。
PaddleSpeech中client与server端的通信协议如下图所示。
在PaddleSpeech流式服务协议主要由三个部分组成,即建立链接握手,数据处理,结束链接握手。
2.1 建立链接握手
在语音识别流式服务中,client端和server端需要建立长链接。
在语音识别流式服务中,client是语音识别业务的请求方,因此client需要主动和server端建立连接,client根据PaddleSpeech的流式协议主动发送建立链接的握手信息,协议过程如下图所示:
建立握手的详细步骤如下所示:
-
Client 需要发送 WebSocket 协议用于握手的 HandShake 信息,Server一直阻塞等待 WebSocket 的握手信息;
每当Server端接收到一个WebSocket 协议的HandShake之后,会开启一个线程用于处理该请求,同时继续阻塞等待下一个握手信息。 -
Server 接收到 WebSocket的握手信息后,等待 Client 的命令;
Server 端会进入到等待命令的循环中,根据client的命令进行处理。 -
Client 发送开始 start 命令信息,Server只有接收到 start 命令之后才会让 server 进行语音识别相关准备工作。
当server完成准备工作之后,将该链接的准备情况发送给client。 -
Server 接收到开始信息之后,创建处理音频的Session,并把能否创建Session的信息发送给Client
经过上述4个步骤之后,表示client和server建立了流式语音识别的连接,同时server已经为语音识别做好的必要的准备工作。
2.2 数据处理
在流式语音识别中,最核心的部分是数据处理的部分。数据处理包括client端数据处理和server端数据处理部分。
client负责将音频以数据流的形式发送给server端。
server不断地接收到client发送的数据,对接收到的数据进行处理。
-
Client 接收到流式连接Handler创建信息,如果Handler创建成功,则开始发送chunk音频数据。
-
Server 接收到音频数据信息之后,开始处理音频,处理好之后将结果返回给Client端。
Server在处理音频的这段时间,Client 禁止发送数据,直到Server给Client 响应; -
Client 接收到 Server 的识别结果之后,开始发送下一个chunk音频数据;
-
Server端接收到最后一个chunk之后,开始识别。
Client 发送最后一个chunk之后,只要没有发送结束session的信息,server则默认还有数据未发送结束。
下面针对client和server分别进行讲解:
2.2.1 client数据处理
client接收到Handler创建成功之后,需要将数据一批一批发送给server端,我们称每一批数据为一个chunk或者一个数据包。
数据包的大小,推荐为200ms左右。
假设一个数据包的样本点为 L L L, 整个音频的样本点为 N N N,音频数据被切割为 n n n 个数据包,则client的逻辑如下所示:
需要注意的是,如果最后一个数据包的长度不满足 L L L 的长度,也是可以发送过去。
2.2.2 server数据处理
server 端接收到 client 的数据之后,就需要进行处理,包括提取特征,声学模型推理,CTC解码等一系列操作。
2.2.2.1 提取特征
server端获取音频的样本数据之后,提取音频特征,如Fbank特征等。而提取Fbank特征的时候,通常帧长是25ms或者20ms,帧移10ms。
我们以帧长25ms,帧移为10ms为例,图示提取特征过程:
在上图中,当接收到数据包2时,提取第三帧frame3的特征时,需要用到数据包1里面的数据,因此在数据包1提取好特征之后,需要缓存一部分音频的样本数据。如果不缓存数据包1的音频样本点,那么在提取特诊时,会丢失很多音频数据,导致最后识别的结果变差。
2.2.2 声学模型推理
server端提取好特征之后,需要将音频特征送入到声学模型中进行解码,以获取每个每个声学符号似然概率。以conformer模型为例,在conformer模型的前两层有下采样层,其网络结构处理如下所示:
从上图中可以看到,声学模型推理时,每7帧语音数据组成一个声学模型解码的chunk,每两个chunk是有3帧的重叠部分。
与提取音频特征过程类似,当一个chunk(7帧语音数据)处理结束之后,需要将剩下的数据缓存起来,等待接收到下一个数据包,重新组合成一个新的chunk数据,送入到声学模型中。
在conformer模型中,可以配置一次解码的chunk的数据 n n n,即将 n n n 个chunk数据组合在一起进行一起解码,这就要求解码时,系统缓存的数据帧至少有
( n − 1 ) ∗ 4 + 7 = 4 n + 3 (n-1) * 4 + 7 = 4n + 3 (n−1)∗4+7=4n+3
2.2.3 CTC 解码
声学模型对语音帧数据进行解码之后,得到每个语音识别建模符号的似然概率,然后使用CTC对每个解码符号进行解码,详细的解码过程可以参考我们aistudio教程中流式训练的部分。
2.3 结束连接握手
当client发送完最后一个数据包之后,client知道整个音频已经发送结束,需要结束这次流式语音识别的过程。server端并不知道client是否还有数据包需要发送,因此需要client端主动发送数据包发送结束的握手信息,server端接收到该信息后知道整个音频已经接收完成,不需要接收新的数据。详细的处理步骤如下所示:
-
Client 发送 Finished 信息,表示已经发送完音频,此时Server将最后缓存数据进行处理结束,得到最后的解码结果,然后销毁 Handler
-
Server 发送给 Client 信息,表示收到 Finished 信息,Client可以关闭连接,
如果有缓存的音频信息未处理完,server识别该缓存音频同时也发送识别结果。
最后缓存解码的结果,可以使用语言模型,或者attention模型进行rescoring进行优化,因此server端发送最后的Finished握手时,也发送最后一次解码结果。
-
Client 收到信息之后,关闭连接,结束本次会话。
3. FastAPI 流式语音识别实战
3.1 安装 PaddleSpeech
实战FastAPI 流式ASR 的过程时,需要安装最新版本的PaddleSpeech
下载好测试音频之后,通过PaddleSpeech的cli方式访问服务。
# 1. 安装 PaddleSpeech
!pip install -U paddlespeech==1.0.1
# 2. 安装 PaddleAudio
!pip install paddleaudio==1.0.0
# 3. 安装 uvicorn==0.18.3 (防止版本问题引发错误)
!pip install uvicorn==0.18.3
!unzip PaddleSpeech-r1.0.zip
!wget https://paddlespeech.bj.bcebos.com/Parakeet/tools/nltk_data.tar.gz
!tar zxvf nltk_data.tar.gz
# punc
!paddlespeech_server start --conf PaddleSpeech/demos/streaming_asr_server/conf/punc_application.yaml &> punc.log &
# asr
!paddlespeech_server start --conf PaddleSpeech/demos/streaming_asr_server/conf/ws_conformer_wenetspeech_application.yaml &> asr.log &
3.2 client 发送数据
!wget -c https://paddlespeech.bj.bcebos.com/PaddleAudio/zh.wav
!ls ./zh.wav
# asr
!paddlespeech_client asr_online --server_ip 127.0.0.1 --port 8090 --input ./zh.wav
# asr + punc
!paddlespeech_client asr_online --server_ip 127.0.0.1 --port 8090 --punc.server_ip 127.0.1 --punc.port 8190 --input ./zh.wav
飞桨PaddleSpeech语音技术课程— 语音唤醒
(以下内容搬运自飞桨PaddleSpeech语音技术课程,点击链接可直接运行源码)
1. KWS 概述
随着人工智能的飞速发展,市场上推出了各式各样的智能设备,AI 语音的发展更是使得语音助手成为各大智能终端设备必不可少的软件。语音是人类与设备最直接的交互方式,不需要和实物接触,可远程操控,对于人们来说是最方便自然的交流方式。
自动语音识别(Automatic Speech Recognition, ASR)是一种将语音转化为文字的技术,是人与机器、人与人自然交流的关键技术之一。ASR 是人与智能设备交互的入口,它的功能就是让设备”听懂“人类的语言,从而能够根据识别到的内容去完成人类想要让它做的事情。
语音唤醒(Keyword Spotting, KWS)是语音识别的入口,如何高效、准确地对用户指令给出反应成为这一技术的最重要的目标。
下图是 IPhone 中 Siri 语音助手的交互示意图,总体上可分为以下三个步骤:
- 麦克风持续检测声音信号
- 逐帧对声音信号进行特征提取和模型预测
- 当接收到一个完整的 “Hey Siri” 的语音时,此刻模型的得分达到最大值,触发唤醒事件
1.1 产品应用
Apple 广告中 Siri 语音助手的交互演示视频。
点击播放
1.2 KWS、ASR 和声音检测
KWS、ASR 和声音检测的关系:
- KWS VS ASR:KWS 可以看作是一类特殊的 ASR,他只识别声音中的固定的关键词。ASR 需要语言模型来理解一段声音中的文字,而 KWS 仅需关注固定样式的发音。从模型输入输出的角度看,KWS 输入音频,输出是判别结果;ASR 输入音频,输出是文字序列。
- KWS VS 声音检测:KWS 和声音检测都是捕获特定的声音,KWS 注重语音中的关键词,而声音检测的范围更为宽泛,可以是自然界中的语音,工业领域里机器产生的声音,人类的哭声,尖叫声等异常声音。从模型输入输出的角度看,KWS 和声音检测都是输入音频,输出判别结果。
2. 适用于 KWS 的模型
2.1 传统 HMM 模型
与语音识别 ASR 类似,KWS 可以用传统的 HMM 模型完成建模和识别,模型结构上是也是声学模型加解码器。
基于 HMM 的 KWS 模型和传统 ASR 模型的区别:
- 声学模型:KWS 只需关注少量的音素,对于其他发音可以当作 Filler 处理,因此声学模型的类别数可以做到很低,譬如在单音素建模下只需要 10 个以内;而 ASR 面向所有的发音,音素全,因此声学模型类别数会大很多。
- 解码器:ASR 的解码器是一个 FST,输入声学模型的结果输出文字序列;而 KWS 的解码器是一个 FSA,如果到达最终状态可以给出一个得分作为唤醒词的分数,解码图的大小相对于 ASR 会小很多。
2.2 端到端模型
2.2.1 基于后验概率平滑的模型
在 2014 年的文章 Small-footprint keyword spotting using deep neural networks 中,作者提出了一种基于神经网络加后验概率平滑的 KWS 方法,该方法利用词粒度来建模声学模型,可分为以下四个执行步骤:
- 特征提取和重叠:对音频信号进行逐帧的特征提取,预测每一帧的声学概率分布时,加入了上下文信息(前30帧+后10帧)后作为模型输入。
- 声学模型:对叠加了上下文信息的频域特征进行声学概率分布的预测,模型总共有 N 个标签,其中标签 0 为 Filler,将与唤醒词的发音无关的归类至此。
- 后验概率平滑:从声学模型得到整段音频的声学概率分布后,采用滑窗的进行后验概率平滑的计算,这么做可以去除一些噪音,增强鲁棒性。
- 唤醒词得分计算:引入一个得分窗口,在窗口内统计除 Filler 外的所有声学概率的最大值,通过累乘和开方的计算方式得到最终得分。
2.2.2 基于 Max-Pooling Loss 的模型
在 2017 年的文章 Max-Pooling Loss Training of Long Short-Term Memory Networks for Small-Footprint Keyword Spotting 中,作者提出了一种基于 Max-Pooling Loss 的 KWS 模型训练方法。
这种方法可以看作是从帧级别的训练方式转向段级别的训练方式,如下图所示,蓝色填充的帧是唤醒词,在训练阶段,模型对于唤醒词片段的得分取决于某一帧中的最高的得分;而非唤醒词片段,为了保证所有帧的得分都足够低,则需要关注所有的帧。这种得分可以看作是基于声学得分的 Max-Pooling。
有了这个训练方式,我们直接地对唤醒词进行端到端的建模,具体模型可以采取 RNN-based、CNN-based 和 Attention-based 可对音频特征序列建模的模型。PaddleSpeech 中的 examples/hey_snips 采用了
Multi-scale Dilated Temporal Convolutional 模型,通过 Max-Pooling Loss 的训练方法实现了在 Snips 数据集上的训练和评估。
3. 实践:KWS模型训练和评估
PaddleSpeech 提供了 MDTC 模型在 Snips 数据集上的从训练到评估的全流程脚本,在此章节中将对一些重要步骤做讲解,如需完整执行训练和评估,可以根据 example 中的文档提示运行脚本,详情请参考:examples/hey_snips/kws0
3.1 环境准备
下载 PaddleSpeech 代码并安装所需依赖:
本教程要求paddlepaddle >= 2.2.2
的环境,并需要 Clone PaddleSpeech Repo的代码(因网络访问问题可能需要等待较长时间,此处直接提供 PaddleSpeech r1.0 分支的代码压缩包):
!unzip work/PaddleSpeech-r1.0.zip
额外依赖:
!pip install scipy resampy soundfile tqdm colorlog pathos dtaidistance sklearn yacs loguru matplotlib
进入 example 目录:
%cd PaddleSpeech-r1.0/examples/hey_snips/kws0/
3.1 数据集准备
Snips 数据集需要用户自行申请下载:keyword-spotting-research-datasets
该数据集包含不同英语口音的约 11,000 “Hey Sinips” 的关键词的音频和 86,500(约96小时) 的其他发音的负样本。正负样本的音频均在相同的说话人、录音设备和环境噪音等条件下录制的,防止模型在训练的过程中关注非关键词相关的特征。数据集的切分和具体数量由下表所示:
Train | Dev | Test | ||
---|---|---|---|---|
Positive | Utterances | 5,876 | 2,504 | 25,88 |
Speakers | 1,179 | 516 | 520 | |
max / speaker | 10 | 10 | 10 | |
Negative | Utterances | 45,344 | 20,321 | 20,821 |
Speakers | 3,330 | 1,474 | 1,469 | |
max / speaker | 30 | 30 | 30 |
数据集下载完成后,解压至/PATH/TO/DATA/hey_snips_research_6k_en_train_eval_clean_ter
目录。
3.2 训练
修改conf/mdtc.yaml
中的data_dir
为'/PATH/TO/DATA/hey_snips_research_6k_en_train_eval_clean_ter'
,只想数据集目录,配置CUDA_VISIBLE_DEVICES
启动 CPU/单卡/多卡训练。
CUDA_VISIBLE_DEVICES=0,1 ./run.sh conf/mdtc.yaml
3.3 指标
针对使用场景和训练样本不均衡问题,通常对 KWS 模型通常关注 False Reject 和 False Alarm 指标。在测试集中,通过对不同的唤醒得分阈值下,对模型的指标进行考察。得到每个阈值的样本判别结果后,可以绘制 DET(Detection Error Tradeoff) 曲线:
在此 example 中,我们考察模型在 False Reject 为每小时1次的前提下,False Alarm 的情况,该数值越小则表示模型越好。在下图的结果中,可以确定唤醒阈值为 0.83,此时的 False Alarm Rate为 0.003559(数据由 example 中训练后的模型得到,重新跑后可能会有细微不同):
通过上述的训练后得到模型的 checkpoint,并确定了唤醒阈值为 0.83,在此通过正负样本两段音频的输入进行预测,感兴趣的朋友也可以尝试自己提供录音音频进行预测(采样率为 16000 的单通道 wave 文件)。
3.4.1 音频预览
keyword.wav
: 正样本,包含唤醒词 Hey Snips 的发音。non-keyword.wav
: 负样本。
import IPython
IPython.display.Audio('/home/aistudio/work/keyword.wav')
IPython.display.Audio('/home/aistudio/work/non-keyword.wav')
3.4.2 模型加载
加载从上述训练过程中得到的模型参数文件,完成预测模型的加载。
加入 Python Package 的搜索路径。
import os
import sys
sys.path.insert(0, os.path.abspath('../../..'))
sys.path.insert(0, os.path.abspath('../../../audio'))
加载 MDTC 模型,模型结构的参数与 /examples/hey_snips/kws0/conf/mdtc.yaml 保持一致。
import paddle
from paddlespeech.kws.models import MDTC
from paddlespeech.kws.models.mdtc import KWSModel
# Model
backbone = MDTC(
stack_num=3,
stack_size=4,
in_channels=80,
res_channels=32,
kernel_size=5,
)
model = KWSModel(backbone=backbone, num_keywords=1)
kws_checkpoint = '/home/aistudio/work/kws.pdparams'
model.set_state_dict(paddle.load(kws_checkpoint))
model.eval()
3.4.3 获取预测结果
通过对测试音频进行特征提取和模型前向计算,获取唤醒得分,并通过对比唤醒阈值得到判别结果。
特征提取的参数与 /examples/hey_snips/kws0/conf/mdtc.yaml 保持一致。
音频加载和特征计算:
import paddleaudio
from paddleaudio.compliance.kaldi import fbank
feat_func = lambda waveform, sr: fbank(
waveform=paddle.to_tensor(waveform).unsqueeze(0),
sr=sr,
frame_shift=10,
frame_length=25,
n_mels=80)
keyword_feat = feat_func(
*paddleaudio.load('/home/aistudio/work/keyword.wav'))
non_keyword_feat = feat_func(
*paddleaudio.load('/home/aistudio/work/non-keyword.wav'))
print(keyword_feat.shape, non_keyword_feat.shape)
获取音频的预测得分:
keyword_logits = model(keyword_feat.unsqueeze(0))
keyword_score = paddle.max(keyword_logits).numpy().item()
print(keyword_score)
non_keyword_logits = model(non_keyword_feat.unsqueeze(0))
non_keyword_score = paddle.max(non_keyword_logits).numpy().item()
print(non_keyword_score)
从阈值( 0.83)和音频的预测得分的比较重容易看出,keyword.wav
判别为唤醒,而 non-keyword.wav
为非唤醒。
4. 参考文献
[1] https://machinelearning.apple.com/research/hey-siri
[2] Chen, Guoguo et al. “Small-footprint keyword spotting using deep neural networks.” 2014 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP) (2014): 4087-4091.
[3] Wang, Zhiming et al. “Small-footprint Keyword Spotting Using Deep Neural Network and Connectionist Temporal Classifier.” ArXiv abs/1709.03665 (2017): n. pag.
[4] Coucke, Alice et al. “Efficient Keyword Spotting Using Dilated Convolutions and Gating.” ICASSP 2019 - 2019 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP) (2019): 6351-6355.
[5] Hou, Jingyong et al. “The NPU System for the 2020 Personalized Voice Trigger Challenge.” ArXiv abs/2102.13552 (2021): n. pag.
[6] Sun, Ming et al. “Max-pooling loss training of long short-term memory networks for small-footprint keyword spotting.” 2016 IEEE Spoken Language Technology Workshop (SLT) (2016): 474-480.
请点击此处查看本环境基本用法.
Please click here for more detailed instructions.
5. 关注 PaddleSpeech
请关注我们的 Github Repo,非常欢迎加入以下微信群参与讨论:
- 扫描二维码
- 添加运营小姐姐微信
- 通过后回复【语音】
- 系统自动邀请加入技术群
P.S. 欢迎关注我们的 github repo PaddleSpeech, 是基于飞桨 PaddlePaddle 的语音方向的开源模型库,用于语音和音频中的各种关键任务的开发,包含大量基于深度学习前沿和有影响力的模型。
以上是关于飞桨PaddleSpeech语音技术课程— 语音识别-流式服务的主要内容,如果未能解决你的问题,请参考以下文章
定制音库成本骤降98%,PaddleSpeech小样本语音合成方案开源了!
语音识别系列︱paddlespeech的开源语音识别模型测试