语音识别基础:(三)完整的语音识别实验

语音识别基础:(三)完整的语音识别实验
2021年09月08日 11:12 语音之家SpeechHome

对语音识别有了大体印象,或许技术流程的细节、原理仍不是太清晰,此时大可以花些时间先行将每个知识点逐一弄明白,等到面面俱到了,这才出山,并期望有了一整套理论加持便能快速地实现一个语音识别系统。这样的线性思维适合一些考试,只要把教材的知识点摸得烂熟,一鼓作气,考个高分就很容易了。只是要真正地掌握一门技术,终究是绝知此事要躬行。我们修我们自己的房子,不断扩张自己的知识领地,所以需要尽快有个知识框架,无论粗糙、精致,首先保证骨架的完整性,然后不断添砖加瓦,一层一层地铺设,每一次都有新面貌、新视野。这一章从一个完整的语音识别实验开始,先对语音识别的基本步骤做到心中有数,随后才是局部的深化和装点。

使用Thchs30数据库,可用 Kaldi 快速实现一个基于 DNN-HMM 结构的语音识别系统。本章先行实现该系统的训练和解码,接下来几章简要介绍几大步骤以及每步生成的文件构成。为了更好地说明多个语音任务的训练步骤,作者将最上层训练脚本重新做了组织,其中所调用的脚本和Kaldi可执行命令与官方Kaldi 没有任何区别。组织后的脚本放于https://github.com/tzyll/kaldi 下的egs/cslt_cases中,下文的文件引用将省去这些路径,各子目录中的 steps 和 utils 软链接的是各个案例公用的标准目录,放于 egs/wsj 目录下, steps 包含的文件与系统训练和解码步骤直接相关,utils 包含一些可能会用到的实用工具,下文以 steps 和 utils 为开头的文件引用都来自这两个目录,不再标明相对出处。

3.1语音识别实验步骤

参照 README, Linux 下安装好 Kaldi,进入 asr_baseline,其中上层脚本 run.sh 包含了DNN-HMM 语音识别系统从前期数据准备到最后解码的整个过程,该脚本是语音识别各个步骤的封装,每个步骤的脚本又是对更底层细节的封装,归根结底,大部分命令会寻至 Kaldi 编译出来的 C++ 可执行程序,这些 C++ 可执行程序基本放在 kaldi/src 中以 bin 结尾的文件夹中,通过可执行文件的名称可望文生义,从而快速了解功能,代码本身和 Kaldi 文档则有详细介绍。通过 run.sh 中的代码注释可速览整个语音识别的流程,本小节剩余部分将对 run.sh 里的代码分段进行讲解,且为了阅读方便,删除了部分注释,但代码运行部分保持不变。

cmd.sh中对计算资源的使用进行了说明,包括是单机还是并行、内存占用等;path.sh里包含了Kaldi所需要的各种环境设置。值得注意的是,目前Kaldi使用的Python版本仍为2.0,Kaldi通过path.sh对此进行了设置。

语音识别系统训练的原始数据集有各种不同的组织形式,但都需要统一整理成 Kaldi 所需的目录结构及文件格式。第4.1节“数据准备”中将更详细地介绍Kaldi 所需的基本数据格式。本文使用的 Thchs30 数据集可以通过代码注释中的链接进行下载。

语音识别的任务是将语音转换成文本,所以除了上一步准备的音频数据及其相关信息,还需要准备语言相关信息(包括发音词典、语言模型等)。第4.1节“语言资料”中将会进行更详细的介绍。

这一步是声学特征提取,即将原始音频通过信号处理手段转换成机器更容易处理的形式。第4.2节“声学特征提取”中介绍了常用的FBANK、MFCC特征的提取方式。这里还计算了倒谱均值方差归一化(Cepstral Mean and Variance Normalization,CMVN)系数用于声学特征的规整化,该方法旨在提高声学特征对说话人、录音设备、环境、音量等因素的鲁棒性。

GMM-HMM的训练是一个不断引入新手段不断再磨炼的过程,新的模型站在旧的模型的肩膀上,循环往复。具体迭代优化形式为,旧的模型对数据进行标记,然后标记后的数据再来训练新的模型,这样旧的模型所学到的知识则通过标记的形式传递给了新的模型,是某种形式的迁移学习。图 3.1 展示了GMM-HMM反复训练的过程,以及其后利用其强制标记(或强制对齐)的结果再训练DNN的过程,具体的流程是:首先通过EM算法训练 GMM-HMM,然后利用训练好的 GMM-HMM 对数据进行强制标记(即打标签,标签为 Senone,因为刚开始只知道整条语音对应的整条文本,所以初始化标签是模糊的),再用强制标记后的数据重新训练 GMM-HMM(配置会有改变),循环往复,直到训练出一个较好的 GMM-HMM,并对数据进行强制标记;第二大步则是利用标签数据进行典型的 DNN 分类器训练;最后将 GMM-HMM 中计算发射概率的 GMM 替换为 DNN。

上面代码中引入的新手段分别是:

• 三音素(Triphone),考虑音素的上下文,三音素则只考虑左邻右舍各一个音素,比如同样是“a”,但因其上下文不一样,“a”便有了多个变种,每个变种都可以独立门户作为一个新的音素;

• LDA+MLLR,使用Linear Discriminant Analysis(LDA)和Maximum Likelihood Linear Regression(MLLR)技术对声学特征进行线性变换,使其对不同音素更加具有区分性;

• SAT,说话人自适应训练(Speaker Adaptive Training),具体采用了fMLLR(feature-space MLLR)方法,目的是提高语音识别系统的说话人无关性。

这一步定义了TDNN结构,并利用GMM-HMM强制标记的结果作为目标进行有监督学习。由于DNN-HMM相比于GMM-HMM结构,只是计算HMM中发射概率的方式发生了改变,所以解码图仍然可以使用GMM-HMM的。

最后在DNN-HMM的基础上进行了区分性训练,即更换了损失函数后对神经网络声学模型的参数又进行了微调优化,其解码过程仍与未区分性训练之前相同。

3.2语音识别实验的运行

根据服务器配置,设置cmd.sh,其中“train_cmd”、“decode_cmd”、“cuda_cmd” 分别表示训练任务、解码任务、GPU 训练任务可以调用的机器或机器集群。具体地,“run.pl”表示本机运行,“queue.pl”表示使用 Grid Engine集群,如 OGS/GE,并可通过运行qconf -sql查看服务器已有的集群配置文件,并将其作为 queue.pl 的参数。

执行 run.sh(通常后台形式,如nohup ./run.sh>run_asr.log &)可以从0到1实现整个语音识别系统的训练和解码,并可根据日志文件查看实验进度。日志是程序开发的重要文件,可通过阅读日志跟踪、了解系统的运行内容,并可根据日志快速定位、调试错误。Kaldi 每一个关键步骤都会生成日志,存放于输出目录中的 log 文件夹或以 .log 结尾的文件中,这些日志文件是学习和使用 Kaldi 必不可少的材料。

执行“.  ./path.sh”,使得当前 shell 下可以直接调用 Kaldi 编译出来的 C++ 可执行程序和相关脚本,方便进一步分析和使用,比如将中断的语句单独取出,并于命令行运行,可加快调试。当不加任何参数直接运行这些程序或脚本时,可打印使用方法。

3.3其他语音任务案例

语音相关的常规任务包括语种识别(Language Recognition)、说话人识别(Speaker Recognition)等,相关案例参见cslt_cases/{lre_baseline,sre_dvector,sre_ivector}此外,Kaldi 针对说话人识别还提供了 x-vector(egs/sre16)系统。

关于语音识别的端对端学习,相关案例可参见Github上的mozilla/DeepSpeech、srvk/eesen、espnet/espnet、facebookresearch/wav2letter 等。

3.4小结

本章通过语音识别实验的上层脚本,大体地介绍了 DNN-HMM 语音识别训练和解码的流程,通过这样的流程,不用深究模型设计和训练的具体细节,即可快速搭建一套语音识别系统。下一章将详细介绍数据的准备、神经网络模型的设计等内容,从而可以自定义要识别的语种、使用的语料量、模型结构等。

文章来源于清语赋

财经自媒体联盟更多自媒体作者

新浪首页 语音播报 相关新闻 返回顶部