语音助手之Kaldi实践

作者:禅与计算机程序设计艺术

1.简介

随着互联网的普及和传播,越来越多的人通过手机、平板电脑等设备使用语音交互。语音助手可以帮助用户更便捷地沟通、控制智能设备,从而实现信息处理效率的提升。近年来,基于深度学习和神经网络的语音识别技术在各个领域都取得了很大的成功,特别是在安卓系统上,谷歌推出的可穿戴助手ASR技术已经取得了不俗的成果。随着语音助手的普及,如何将这些语音技术应用到实际生产环境中并取得良好的效果,成为需要解决的重要课题。本文将介绍基于开源工具Kaldi的语音助手项目开发过程。

Kaldi是一个开源工具箱,用于构建语言模型(LM)、声学模型(AM)和整体语音系统,可以用于实现自动语音识别(ASR)、文本转语音(TTS)和语音合成(VC)。其功能包括特征抽取、声学建模、HMM-DNN 模型训练、解码器、Lattice Faster-CTC 后处理、集束搜索以及端到端训练流程。因此,Kaldi是一个强大的自然语言处理工具箱。

本文将详细介绍基于Kaldi的语音助手项目开发过程。首先会对Kaldi的功能进行简要介绍,然后详细阐述其实现原理,最后给出几个完整例子,展示不同场景下的开发用法。文章的主要读者是具有一定机器学习基础的AI/ML研究人员或工程师。

2.Kaldi概述

Kaldi是一个开源的语音处理工具箱,由斯坦福大学的科研团队发明。其包括特征抽取、声学模型和语言模型三大模块。其中特征抽取模块包括MFCC、CMVN、倒谱均衡化和加窗;声学模型模块包括WFSTs、WFSA、GMM HMM和DNN HMM,声学模型的训练通过统计方法、最大似然估计、共轭梯度下降算法和交叉熵代价函数来完成;语言模型模块包括n-gram模型、LM-LSTM模型和混合语言模型;整体语音系统包括声学模型和语言模型的结合,同时还可以实现端到端的训练。为了实现Kaldi的功能,作者设计了很多不同的功能组件。比如“解码器”模块负责在声学模型和语言模型的基础上,将声学模型输出的概率值转换成字符序列;“集束搜索”模块则利用动态规划算法计算最优的文本译码路径;还有“nnet3”框架、“srilm”语言模型库和“Openfst”动态库。因此,Kaldi支持许多高级功能,如分隔符消歧、声调修正、多任务学习、声纹识别、跨语言联合训练等。除此之外,Kaldi也被多个高校和公司所采用,包括Google、百度、微软、腾讯、清华、北航等。

3.Kaldi功能

Kaldi主要具有以下三个方面的功能:

  1. Kaldi 的特征抽取模块 MFCC、CMVN、倒谱均衡化和加窗提供了对音频信号的特征提取能力。它能够自动提取音频中的语音节奏信息、频域信息、相位信息、加窗信息等,对于语音识别系统的性能有着极大的影响。

  2. Kaldi 的声学模型模块 WFSTs、WFSA、GMM HMM 和 DNN HMM 对音频信号进行处理,得到对应的 HMM 参数,这些参数可以用于生成声学模型和声学解码,例如 GMM-HMM、DNN-HMM 和 WFST-based HMM。它们提供了一种简单有效的方式来建模音频信号,并且可以在不同的应用场景下使用。

  3. Kaldi 的语言模型模块 n-gram 模型、LM-LSTM 模型和混合语言模型提供对输入句子的概率计算能力。Kaldi 使用 n-gram 和 LM-LSTM 模型进行单词识别、词性标注和分割,也支持混合语言模型。这些模型使得 Kaldifor ASR 引擎具有较好的适应性和鲁棒性。

综上所述,Kaldi 具备以下几个特性:

  1. 灵活性:Kaldi 支持丰富的音频特征和声学模型,并提供了对训练数据准备、调参的完整支持。因此,可以使用 Kaldi 来快速搭建各种类型的语音识别系统。

  2. 方便性:Kaldi 提供了丰富的命令行工具,可以方便地使用 Python API 或脚本调用,无需编写复杂的代码。

  3. 易用性:Kaldi 可以运行在 Linux、Windows 和 Mac OS 上,并且支持多种编程语言,包括 C++、Python、MATLAB、Java 等。

4.Kaldi实现原理

Kaldi的实现原理可以分为声学模型和语言模型两部分。

声学模型

Kaldi的声学模型模块包括 WFSTs、WFSA、GMM HMM 和 DNN HMM,分别对应三种不同的声学建模方式。下面介绍一下每种声学模型的原理。

  1. WFSTs 声学模型
    WFSTs 是 Weighted Finite State Transducers(加权有限状态转换器),由苏黎世联邦理工大学 Wenzel 教授提出。WFSTs 是一组状态机,每个状态表示一个隐藏单元,状态间存在 transitions(转换)。每条 transition 记录了输出的 label (通常是一个音素)和对应的概率。WFSTs 有如下几点优点:

(1)可扩展性强:WFSTs 的状态数量和transition数量随输入数据的大小呈线性增长,因此可以处理规模庞大的语音数据。

(2)带宽效率高:WFSTs 只维护当前时刻的状态集合,仅存储必要的 transitions,因此内存开销小。

(3)灵活性强:WFSTs 可灵活地表示复杂的语言学和音韵学结构。

WFSTs 在 Kaldi 中的具体实现,包括:

(1)使用 Kaldi 的 nnet3 框架来训练 WFSTs 。训练时,首先根据音频特征计算出 log-energy,再使用 WFSTs 从 log-energy 中分离出上下文相关的特征。接着,使用 ARPA-format language model 对特征进行后处理,生成转移概率和状态打分。

(2)使用 Kaldi 的 gmm-hmm 插件来运行 WFSTs 声学模型。主要做法是,计算 frame-level posteriors,然后对每帧上的 posteriors 进行加权平均,得到最终的 state-level posteriors。

WFSTs 模型训练流程如下图所示:

  1. WFSA 声学模型
    WFSA (Weighted Finite Automaton) 模型是一种非常简单的声学模型。它的每个状态代表一个音素或子音素,状态之间的连接有唯一的输出。WFSA 模型有如下几点优点:

(1)内存开销低:WFSA 只维护当前时刻的状态集合,不需要存储全部的状态和连接,因此内存开销低。

(2)计算效率高:WFSA 仅需要遍历一次状态机即可完成音频的特征提取、声学建模,计算速度快。

(3)易于定制:WFSA 可以直接使用 Kaldi 的 HCLG 文件进行语言模型和解码。

WFSA 模型训练流程如下图所示:

  1. GMM HMM 声学模型
    GMM HMM 是广义混合高斯模型(Generalized Mixture of Gaussians Model)的简称。GMM 是一种概率分布,由多个正态分布组成,用来描述一段时间内随机变量的分布情况。GMM HMM 是将 GMM 建模到 HMM 上的一种声学建模方式。与 HMM 的状态对应 GMM 分布的个数,而状态间的转移是 GMM 的混合权重。

GMM HMM 模型有如下几点优点:

(1)数值稳定性好:GMM HMM 模型可以对参数进行收敛和准确估计,不会出现数值不稳定的情况。

(2)多元性强:GMM HMM 模型可以表示多元高斯分布,能够对不同性质的音频信号进行建模。

(3)简单性:GMM HMM 模型对系统的构建比较简单。

GMM HMM 模型训练流程如下图所示:

  1. DNN HMM 声学模型
    DNN HMM (Deep Neural Network Hidden Markov Model) 是 Deep Neural Networks and Hidden Markov Models(深层神经网络和隐马尔可夫模型)的缩写,它是一种高性能的声学建模方法。它使用多层神经网络作为声学模型,把 HMM 建模到神经网络上。

DNN HMM 模型有如下几点优点:

(1)多样性:DNN HMM 模型对不同类型和尺寸的音频信号都可以表现良好。

(2)鲁棒性:DNN HMM 模型对噪声、拓扑结构、系统依赖性等不确定因素都有良好的适应性。

(3)泛化能力强:DNN HMM 模型的泛化能力比较强,对新的数据也有很好的适应性。

DNN HMM 模型训练流程如下图所示:

总结:Kaldi 支持多种声学模型,选择合适的模型能够对声音信号进行更好的建模,提升系统的识别精度。

语言模型

Kaldi 的语言模型模块提供了对输入句子的概率计算能力,可以用于单词识别、词性标注和分割,也可以用于混合语言模型。下面介绍一下三种语言模型的原理。

  1. N-gram 模型
    N-gram 模型是一种通用的语言模型,将一个句子看作是由若干个单词构成的序列。N-gram 模型假设下一个单词只跟当前的若干个单词相关,与句子的其他部分独立。N-gram 模型通过分析前面若干个单词,预测当前单词的概率。由于历史原因,N-gram 模型又叫做 Morpheme-Counting Model。

N-gram 模型的训练过程包括:

(1)读取训练数据,包括句子集合。

(2)构造 N-gram 语言模型,统计每个 N-gram 的出现次数。

(3)计算模型概率,即每个单词的出现概率。

N-gram 模型在 Kaldi 中的具体实现,包括:

(1)使用 HMM-Grammar 工具来生成语法文件和字典文件。

(2)使用 KenLM 工具训练语言模型。

N-gram 模型训练流程如下图所示:

  1. LM-LSTM 模型
    LM-LSTM 模型是 LSTM 语言模型,由阿姆斯特朗大学李宏毅教授提出。它是一种递归神经网络的变体,其基本思想是将前一词或上下文的词嵌入到当前词的潜在空间中,并使用 LSTM 模型来生成当前词的可能的下一个词。

LM-LSTM 模型的训练过程包括:

(1)准备语言模型数据。

(2)使用 nnet3 的 chain 模块训练语言模型。

(3)使用 lmrescore 命令,对链式模型进行语言模型后处理,得到更好的结果。

LM-LSTM 模型在 Kaldi 中的具体实现,包括:

(1)使用 rnnlm 框架,训练 LSTM 语言模型。

LM-LSTM 模型训练流程如下图所示:

  1. 混合语言模型
    混合语言模型是指将不同语言模型组合成一个模型,对输入句子进行概率计算。Kaldi 中使用的混合语言模型包括 n-gram 和 LM-LSTM 模型。

混合语言模型的训练过程包括:

(1)准备混合语言模型数据。

(2)使用混合语言模型训练工具,合并多个语言模型。

(3)使用语言模型评估工具,评估最终的混合语言模型的准确率。

混合语言模型在 Kaldi 中的具体实现,包括:

(1)使用 scripts/nnet3/chain/build_mixture_langmodel.sh 生成混合语言模型。

混合语言模型训练流程如下图所示:

总结:Kaldi 提供了多种语言模型,选择合适的模型能够对输入句子进行更好的计算,提升系统的准确率。

5.Kaldi应用实例

本章节,以两个完整案例来展示 Kadli 的应用效果。第一个案例为一个基于 GMM HMM 声学模型的普通话识别系统;第二个案例为一个基于 RNNLM 的中文识别系统。

基于 GMM HMM 声学模型的普通话识别系统

在这一案例中,我们将展示如何基于 Kaldi 的 GMM HMM 声学模型搭建一个普通话识别系统。

数据准备

首先,我们需要准备一些普通话语音数据,包括录制的音频文件和对应的文本文件。

为了便于理解,我们可以从网易云的普通话语音数据集中下载一些语音文件。我们可以使用 wget 命令下载数据集:

wget http://deepspeech.bj.bcebos.com/zh_cn/dataset/train_clean_100.tar.gz -P /tmp/data/
cd /tmp/data && tar xvf train_clean_100.tar.gz && rm train_clean_100.tar.gz

下载完成后,文件夹 /tmp/data/ 下应该包含 train_clean_100 文件夹。该文件夹下包含 100 个 wav 文件,每一个文件都对应了一个普通话的句子。

我们可以查看一下其中一个文件的目录结构:

tree /tmp/data/train_clean_100/190004-00003.wav
/tmp/data/train_clean_100/190004-00003.wav
├── s1
│   ├── A
│   ├── B
│   └──...
└── text
├── A
│   ├── 发信站
│   ├── 发往
│   └──...
├── B
│   ├── 冀州区
│   ├── 择途站
│   └──...
└──...

其中,190004-00003.wav 为语音文件名称,s1text 为不同纬度的资源文件,分别存放了声学信息和文本。

特征提取

我们需要使用 Kaldi 的 MFCC 工具来提取特征。为了便于理解,我们可以先使用 Kaldi 的默认配置来提取特征:

steps/make_mfcc.sh --nj 20 --cmd run.pl \
--write-utt2spk $(pwd)/mfcc/utt2spk \
--mfcc-config conf/mfcc_hires.conf \
/tmp/data/train_clean_100 /tmp/data/train_clean_100/log mfcc || exit 1;

该脚本会生成 /tmp/data/mfcc,其中包含 MFCC 特征。/tmp/data/mfcc 文件夹包含两个重要文件:feats.scpcmvn.scp

  • feats.scp 文件保存了所有的特征,每一行包含一个 utterance ID 和对应的特征文件路径。
  • cmvn.scp 文件保存了所有特征的 CMVN 均值和标准差,每一行包含一个 utterance ID 和对应的 CMVN 文件路径。

如果觉得默认的 MFCC 配置不是太适合你的需求,可以修改配置文件 conf/mfcc_hires.conf

创建数据集

创建数据集之前,我们需要检查是否已经生成 MFCC 特征。如果没有,请按照上一步的指令生成特征。

我们可以使用下面的命令创建数据集:

utils/subset_data_dir.sh --first data/train_clean_100 --second data/train_clean_100../data/train data/train 20% || exit 1;
utils/fix_data_dir.sh../data/train || exit 1;

该脚本创建一个新的目录 ../data/train,它包含训练数据的一半。为了防止过拟合,这里只选取了 20% 的数据用于验证。

声学模型训练

训练声学模型之前,我们需要检查数据集是否已经准备妥当。如果没有,请按照上一步的指令创建数据集。

我们可以使用下面的命令训练声学模型:

./run.sh --stage 1 --stop-stage 10 \
exp/tri1_ali exp/tri1_denlats exp/tri1 \
data/train data/lang exp/tri1_db

这个命令会启动训练,并生成声学模型。生成的文件如下:

  • exp/tri1/final.mdl: 模型文件。
  • exp/tri1_denlats/lat.*.gz: 拐点文件。
  • exp/tri1/tree: 决策树文件。
  • exp/tri1/graph: 概率图文件。

训练结束之后,我们可以使用 local/score.sh 命令来评估声学模型的准确率:

./local/score.sh --cmd "run.pl" exp/tri1 data/train || exit 1;

语言模型训练

我们可以用同样的方法训练中文识别模型。但是需要注意的是,对于中文语音数据,我们需要使用结巴分词工具来切词,以保证训练过程中不会发生错误。

除了声学模型,我们还需要训练中文语言模型。我们可以参考如下脚本:

./run.sh --stage 2 --stop-stage 2 \
exp/tri1_ali exp/tri1_denlats exp/tri1 \
data/train data/lang exp/tri1_db || exit 1;./run.sh --stage 3 --stop-stage 4 \
exp/tri2a_pron exp/tri1_denlats exp/tri1_ali \
data/train data/lang exp/tri2a_db || exit 1;./run.sh --stage 5 --stop-stage 5 \
exp/tri2a_ug exp/tri2a_ali exp/tri1_denlats \
data/train data/lang exp/tri2a_db || exit 1;./run.sh --stage 6 --stop-stage 6 \
exp/tri3a_comb exp/tri2a_ug exp/tri2a_ali \
data/train data/lang exp/tri3a_db || exit 1;./run.sh --stage 7 --stop-stage 8 \
exp/tri3b_word_trainee exp/tri2a_ug exp/tri2a_ali \
data/train data/lang exp/tri3a_db || exit 1;./run.sh --stage 9 --stop-stage 9 \
exp/tri4_phone_trainee exp/tri2a_ug exp/tri2a_ali \
data/train data/lang exp/tri3a_db || exit 1;./run.sh --stage 10 --stop-stage 10 \
exp/tri5a_am_trainee exp/tri2a_ug exp/tri2a_ali \
data/train data/lang exp/tri3a_db || exit 1;

这个脚本会训练中文语言模型。训练完毕之后,我们可以使用 local/decode.sh 命令来测试模型的准确率:

./local/decode.sh --acwt 1.0 --max-active 7000 --beam 10.0 --lattice-beam 0.8 --cmd "$decode_cmd" exp/tri5a_am_trainee/graph_tgpr data/test || exit 1;

该脚本会生成字词级别的结果,我们还可以使用 utils/int2sym.pl 命令来还原到文本形式。

至此,我们完成了基于 Kaldi 的 GMM HMM 声学模型的普通话识别系统的搭建和测试。

基于 RNNLM 的中文识别系统

本章节,我们将展示如何基于 Kaldi 的 RNNLM 搭建一个中文识别系统。

数据准备

首先,我们需要准备一些中文语音数据,包括录制的音频文件和对应的文本文件。

wget http://www.openslr.org/resources/38/data_thchs30.tgz -P /tmp/data/
cd /tmp/data && tar xvf data_thchs30.tgz && rm data_thchs30.tgz
mkdir /tmp/data/thchs30_audios
mv /tmp/data/data_thchs30/*/*.wav /tmp/data/thchs30_audios
rm -rf /tmp/data/data_thchs30

下载完成后,文件夹 /tmp/data/thchs30_audios 下应该包含 440 个 wav 文件,每一个文件都对应了一句中文语句。

文本处理

import jiebawith open('example.txt', 'r') as f:
for line in f.readlines():
words = list(jieba.cut(line))
print(" ".join(words))

这样,我们就可以对中文文本进行分词处理。

特征提取

我们需要使用 Kaldi 的 MFCC 工具来提取特征。为了便于理解,我们可以先使用 Kaldi 的默认配置来提取特征:

steps/make_mfcc.sh --nj 20 --cmd run.pl \
--write-utt2spk $(pwd)/mfcc/utt2spk \
--mfcc-config conf/mfcc_hires.conf \
/tmp/data/thchs30_audios /tmp/data/thchs30_mfcc || exit 1;

该脚本会生成 /tmp/data/thchs30_mfcc,其中包含 MFCC 特征。/tmp/data/thchs30_mfcc 文件夹包含两个重要文件:feats.scpcmvn.scp

创建数据集

创建数据集之前,我们需要检查是否已经生成 MFCC 特征。如果没有,请按照上一步的指令生成特征。

我们可以使用下面的命令创建数据集:

utils/subset_data_dir.sh --first thchs30_audios --second thchs30_audios../data/train data/train 20% || exit 1;
utils/fix_data_dir.sh../data/train || exit 1;

该脚本创建一个新的目录 ../data/train,它包含训练数据的一半。为了防止过拟合,这里只选取了 20% 的数据用于验证。

语言模型训练

我们可以用同样的方法训练中文识别模型。但是需要注意的是,对于中文语音数据,我们需要使用结巴分词工具来切词,以保证训练过程中不会发生错误。

除了声学模型,我们还需要训练中文语言模型。我们可以参考如下脚本:

./run.sh --stage 2 --stop-stage 2 \
exp/rnnlm_tdnnf/tri2a_ali exp/rnnlm_tdnnf/tri1_denlats exp/rnnlm_tdnnf/tri1 \
data/train data/lang exp/rnnlm_tdnnf/tri1_db || exit 1;./run.sh --stage 3 --stop-stage 4 \
exp/rnnlm_tdnnf/tri3a_pron exp/rnnlm_tdnnf/tri1_denlats exp/rnnlm_tdnnf/tri2a_ali \
data/train data/lang exp/rnnlm_tdnnf/tri3a_db || exit 1;./run.sh --stage 5 --stop-stage 5 \
exp/rnnlm_tdnnf/tri3b_word_trainee exp/rnnlm_tdnnf/tri1_denlats exp/rnnlm_tdnnf/tri2a_ali \
data/train data/lang exp/rnnlm_tdnnf/tri3a_db || exit 1;./run.sh --stage 6 --stop-stage 6 \
exp/rnnlm_tdnnf/tri4_phone_trainee exp/rnnlm_tdnnf/tri1_denlats exp/rnnlm_tdnnf/tri2a_ali \
data/train data/lang exp/rnnlm_tdnnf/tri3a_db || exit 1;./run.sh --stage 7 --stop-stage 7 \
exp/rnnlm_tdnnf/tri5a_am_trainee exp/rnnlm_tdnnf/tri1_denlats exp/rnnlm_tdnnf/tri2a_ali \
data/train data/lang exp/rnnlm_tdnnf/tri3a_db || exit 1;./run.sh --stage 8 --stop-stage 8 \
exp/rnnlm_tdnnf/tri6a_lm_librispeech_asr001 exp/rnnlm_tdnnf/tri5a_am_trainee/graph_tgpr_small data/train data/lang exp/rnnlm_tdnnf/tri6a_lm_librispeech_asr001_db || exit 1;

这个脚本会训练中文语言模型。训练完毕之后,我们可以使用 local/decode.sh 命令来测试模型的准确率:

./local/decode.sh --acwt 1.0 --max-active 7000 --beam 10.0 --lattice-beam 0.8 --cmd "$decode_cmd" exp/rnnlm_tdnnf/tri6a_lm_librispeech_asr001/graph_tgpr data/test || exit 1;

该脚本会生成字词级别的结果,我们还可以使用 utils/int2sym.pl 命令来还原到文本形式。

至此,我们完成了基于 Kaldi 的 RNNLM 的中文识别系统的搭建和测试。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部