Nurhachu Null原创

语音识别开源工具PyTorch-Kaldi:兼顾Kaldi效率与PyTorch灵活性

本文主要介绍用于语音识别的开源工具——PyTorch-Kaldi。

1 背景

杰出的科学家和工程师们一直在努力地给机器赋予自然交流的能力,语音识别就是其中的一个重要环节。人类对语音识别技术的研究从上世纪 50 年代开始就未曾停止。在长期的探索中,一次次重大的技术突破逐渐让语音识别技术进入我们的日常生活。今天的 ASR 技术水平是前所未有的。高性能的语音识别给我们带来了更多的生活体验,我们拥有了可以对话的智能数字助手;它也在逐步改善相关领域的生产力水平。

和很多伟大技术的应用一样,语音识别技术的背后也是很多模块的组合。对其实现流程的改进往往会从一定程度上节省开发成本,并且加快技术迭代的速度。Pytorch-Kaldi 的出现就是基于这样的动力。

1.1 语音识别系统的组成

图 1. 语音识别系统的结构

一个典型的语音识别系统如图 1 所示。它包含 4 个组成部分:信号处理和特征提取、声学模型、语言模型和解码搜索。声学模型是其中的核心部分,我们可以把它理解为说话声音和语言发音之间的映射。而信号处理和特征提取就是用于构建声学模型的材料了,它主要依靠数字信号处理相关的技术。语言模型则可以被看作是语言先验知识。语音识别的最终结果就是在声学模型得分和语言模型得分上进行搜索得到的。具体的内容这里不做展开。

在语音识别技术的发展史上,深度学习绝对是极具影响力的。可以说,没有对深度学习的引入,就不会有今天如此先进的语音识别引擎。

1.2 业界的基本现状

一个成功的语音识别系统真的是很难离开一众优秀开源框架的支撑,比如:HTK,Julius,CMU-Sphinx,PWTH-ASR,LIA-ASR 以及 Kaldi。后来居上的 Kaldi 独领风骚,拥有活跃的技术社区,被广泛的应用在语音识别技术的研究和系统开发中。据笔者了解,很多国内语音公司的语音识别系统也有着对 Kaldi 或多或少的依赖。图 2 是在本文写作的时,GitHub 上 Kaldi 项目的「盛景」。

图 2. kaldi-asr

但是,Kaldi 也有不尽如人意的地方,它依赖大量的脚本语言,而且核心算法使用 C++编写的,对声学模型的更新就不是一件容易的事情了,尤其是在需要改变各种神经网络的结构时。即便是拥有丰富经验的工程师,在调试的时候也会经历巨大的痛苦。当然,尽管如此,Kaldi 还是一项伟大的工作。

有问题存在,便有了改进的需要。Yoshua Bengio 团队成员 Mirco Ravanelli 等人开发了一个试图继承 Kaldi 的效率和 PyTorch 的灵活性的开源框架——PyTorch-Kaldi。相关的论文已经在 ICASSP 2019 上发表了,论文标题如图 3 所示。

图 3. PyTorch-Kaldi 论文首页

1.3 Why pytorch-kaldi?

正如论文提到的一句话,「The PyTorch-Kaldi project aims to bridge the gap between Kaldi and PyTorch」,PyTorch-Kaldi 就是为了弥补 PyTorch 和 Kaldi 之间的鸿沟。在 PyTorch 中实现声学模型,在 Kaldi 中执行特征提取、标签/对齐计算和解码。这也再次从侧面证明了 PyTorch 作为一个深度学习框架所具有的的卓越的灵活性和便利性。事实上,很多人都认为 PyTorch 比 TensorFlow 更加适合做研究工作。本文的第二部分将会重点介绍一下 PyTorch-Kaldi 开源工具。

2 PyTorch-Kaldi 简介

PyTorch-Kaldi 项目的结构如图 4 所示。正如前面所提到的,在这个项目中,PyTorch 和 Kaldi 在项目中的分工是比较明确的。主脚本 run_exp.py(后面称其为主脚本)是用 Python 写的,它负责管理 ASR 系统的所有阶段,包括特征和标签提取、训练、验证、解码和打分。目前版本(v0.2)的 PyTorch-Kaldi 实现了混合 DNN-HMM 的语音识别器。

图 4. PyTorch-Kaldi 项目结构

2.1 配置文件

主脚本以 INI 格式的配置文件为输入,这个配置文件在项目文档中有着很详细的描述。配置文件主要由以下 5 部分内容组成。

  • [exp]:这部分指定了一些高级信息,例如用于实验的文件夹、训练迭代次数、随机数种子,它也允许用户指定实验是在 CPU/GPU 或者多 GPU 上进行,下面是一个例子:

[exp] 
cmd = 
run_nn_script = run_nn 
out_folder = exp/TIMIT_liGRU_fmllr 
seed = 4234 
use_cuda = True 
multi_gpu = False 
save_gpumem = False 
n_epochs_tr = 24
  • [dataset]:这部分指定了特征和标签。包括它们的存储路径、窗口的特征以及数据集被分割成块的数量。下面是一个例子:

[dataset1] 
data_name = TIMIT_tr 
fea = fea_name=mfcc 
    fea_lst=quick_test/data/train/feats_mfcc.scp 
    fea_opts=apply-cmvn --utt2spk=ark:quick_test/data/train/utt2spk ark:quick_test/mfcc/train_cmvn_speaker.ark ark:- ark:- | add-deltas --delta-order=2 
ark:- ark:- | 
    cw_left=0 
    cw_right=0 

    fea_name=fbank 
    fea_lst=quick_test/data/train/feats_fbank.scp 
    fea_opts=apply-cmvn --utt2spk=ark:quick_test/data/train/utt2spk ark:quick_test/fbank/cmvn_train.ark 
ark:- ark:- | add-deltas --delta-order=0 ark:- ark:- | 
cw_left=0 cw_right=0 
fea_name=fmllr fea_lst=quick_test/data/train/feats_fmllr.scp 
fea_opts=apply-cmvn --utt2spk=ark:quick_test/data/train/utt2spk 
ark:quick_test/data-fmllr-tri3/train/train_cmvn.ark 
ark:- ark:- | add-deltas --delta-order=0 
ark:- ark:- | 
cw_left=0 
cw_right=0 
lab = lab_name=lab_cd 
lab_folder=quick_test/dnn4_pretrain-dbn_dnn_ali 
lab_opts=ali-to-pdf 
lab_count_file=auto 
lab_data_folder=quick_test/data/train/ 
lab_graph=quick_test/graph 
lab_name=lab_mono 
lab_folder=quick_test/dnn4_pretrain-dbn_dnn_ali 
lab_opts=ali-to-phones --per-frame=true 
lab_count_file=none 
lab_data_folder=quick_test/data/train/ 
lab_graph=quick_test/graph 
n_chunks = 5
  • [architecture] 这部分描述神经网络模型,下面是一个例子:

2.2 语音特征

  • [model]:这部分定义了神经网络的结合方式,下面是一个例子:

[model] 
model_proto = proto/model.proto 
model = out_dnn1=compute(liGRU_layers,fmllr) 
out_dnn2=compute(MLP_layers,out_dnn1) 
out_dnn3=compute(MLP_layers2,out_dnn1) 
loss_mono=cost_nll(out_dnn3,lab_mono) 
loss_mono_w=mult_constant(loss_mono,1.0) 
loss_cd=cost_nll(out_dnn2,lab_cd) 
loss_final=sum(loss_cd,loss_mono_w) 
err_final=cost_err(out_dnn2,lab_cd)
[forward] 
forward_out = out_dnn2 
normalize_posteriors = True
normalize_with_counts_from = lab_cd 
save_out_file = False 
require_decoding = True
  • [decoding]:这部分定义了解码参数,下面是一个例子:

[decoding]
 decoding_script_folder = kaldi_decoding_scripts/ 
 decoding_script = decode_dnn.sh 
 decoding_proto = proto/decoding.proto 
 min_active = 200 
 max_active = 7000 
 max_mem = 5000000 0 
 beam = 13.0 
 latbeam = 8.0 
 acwt = 0.2
  max_arcs = -1 
 skip_scoring = false 
 scoring_script = local/score.sh 
 scoring_opts = "--min-lmwt 1 --max-lmwt 10" 
 norm_vars = False

2.2 语音特征和标签

语音特征是使用 Kaldi 中的原生 C++库进行提取的。PyTorch-Kaldi 的一个特点就是可以管理多个语音特征流,用户在实际开发的过程中可以定义使用多个特征参数组合的模型。

用于训练声学模型的主要特征来自于语音特征和上下文无关的音素序列,这是通过 Kaldi 的音素决策树生成的。

2.3 chunk 和 minibatch 的组成

PyTorch-Kaldi 将数据集分成了多个块,每个块中的样本都是从整个语料库中随机抽样得到的。然后再训练的过程中每次迭代只使用一个小批量的数据,这也是神经网络优化的常用方法。

不过,小批量数据的聚集方式是由神经网络的结构决定的,对于普通的前馈模型而言,随机选择数据就行。但是对于 RNN 这类网络而言,minibatch 则必须由完整的句子组成。

2.4 DNN 声学模型、解码和打分

PyTorch-Kaldi 已经定义好了一些先进的神经网络模型,目前支持标准的 MLP、CNN、RNN、LSTM、GRU、Light GRU 等。实际上这部分就是神经网络模型的训练和优化。

在进行基于 HMM 的解码之前,声学模型产生的声学后验概率与其先验概率进行归一化之后便和语言模型生成的语言概率,常用的语言模型就是 n-gram 模型。然后使用波束搜索算法得到语音信号中的单词序列。最后使用 NIST SCTK 工具计算字错率(WER)。

原论文的实验部分展示了使用 PyTorch-Kaldi 进行的多组语音识别相关的实验,在一些数据集和任务上面还达到了目前最高的水平。

3 总结

就其整体架构和 Mirco Ravanelli 等人表现出来的「野心」来看,PyTorch-Kaldi 的潜力是比较大的。项目文档中关于下一个版本的描述是这样写的:「The architecture of the toolkit will be more modular and flexible. Beyond speech recognition, the new toolkit will be suitable for other applications such as speaker recognition, speech enhancement, speech separation, etc.」。当然,这只是一个工具而已,如果没有对语音识别技术的深刻理解,肯定是做不出更好东西的。许愿:有更多的人力和资源积极地投入到这个领域,帮助让 PyTorch-Kaldi 变得更好,或者打造出全新的比 PyTorch-Kaldi 更好的工具。

参考资料

[1] M. Ravanelli, T. Parcollet and Y. Bengio, "The Pytorch-kaldi Speech Recognition Toolkit," ICASSP 2019 - 2019 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), Brighton, United Kingdom, 2019, pp. 6465-6469.doi: 10.1109/ICASSP.2019.8683713

[2] D. Yu and L. Deng, Automatic Speech Recognition – A Deep Learning Approach, Springer, 2015.

[3] Kaldi 文档(kaldi-asr.org/doc/)(http://kaldi-asr.org/doc/%EF%BC%89)

[4] PyTorch-Kaldi Github 仓库(https://github.com/mravanelli/pytorch-kaldi)(https://github.com/mravanelli/pytorch-kaldi%EF%BC%89)

[5] 王赟. 语音识别技术的前世今生(https://www.zhihu.com/lives/843853238078963712%EF%BC%89)(https://www.zhihu.com/lives/843853238078963712%EF%BC%89%EF%BC%89)

入门机器学习自然语言处理计算机视觉其他智能领域
7
相关数据
先验概率技术

在贝叶斯统计中,某一不确定量p的先验概率分布是在考虑"观测数据"前,能表达p不确定性的概率分布。 它旨在描述这个不确定量的不确定程度,而不是这个不确定量的随机性。 这个不确定量可以是一个参数,或者是一个隐含变量(英语:latent variable)。

数字信号处理技术

数字信号处理(digital signal processing),简称DSP,是指用数学和数字计算来解决问题。 大学里,数字信号处理常指用数字表示和解决问题的理论和技巧;而DSP也是数字信号处理器(digital signal processor)的简称,是一种可编程计算机芯片,常指用数字表示和解决问题的技术和芯片。

后验概率技术

在贝叶斯统计中,一个随机事件或者一个不确定事件的后验概率是在考虑和给出相关证据或数据后所得到的条件概率。同样,后验概率分布是一个未知量(视为随机变量)基于试验和调查后得到的概率分布。“后验”在本文中代表考虑了被测试事件的相关证据。

映射技术

映射指的是具有某种特殊结构的函数,或泛指类函数思想的范畴论中的态射。 逻辑和图论中也有一些不太常规的用法。其数学定义为:两个非空集合A与B间存在着对应关系f,而且对于A中的每一个元素x,B中总有有唯一的一个元素y与它对应,就这种对应为从A到B的映射,记作f:A→B。其中,y称为元素x在映射f下的象,记作:y=f(x)。x称为y关于映射f的原象*。*集合A中所有元素的象的集合称为映射f的值域,记作f(A)。同样的,在机器学习中,映射就是输入与输出之间的对应关系。

先验知识技术

先验(apriori ;也译作 先天)在拉丁文中指“来自先前的东西”,或稍稍引申指“在经验之前”。近代西方传统中,认为先验指无需经验或先于经验获得的知识。先验知识不依赖于经验,比如,数学式子2+2=4;恒真命题“所有的单身汉一定没有结婚”;以及来自纯粹理性的推断“本体论证明”

语言模型技术

语言模型经常使用在许多自然语言处理方面的应用,如语音识别,机器翻译,词性标注,句法分析和资讯检索。由于字词与句子都是任意组合的长度,因此在训练过的语言模型中会出现未曾出现的字串(资料稀疏的问题),也使得在语料库中估算字串的机率变得很困难,这也是要使用近似的平滑n元语法(N-gram)模型之原因。

暂无评论
暂无评论~