语音识别之PyTorch-Kaldi详细教程

本文介绍PyTorch-Kaldi。Kaldi是用C++和各种脚本来实现的,它不是一个通用的深度学习框架。如果要使用神经网络来梯度GMM的声学模型,就得自己用C++代码实现神经网络的训练与预测,这显然很难实现并且容易出错。我们更加习惯使用Tensorflow或者PyTorch来实现神经网络。因此PyTorch-Kaldi就应运而生了,它使得我们可以利用Kaldi高效的特征提取、HMM模型和基于WFST的解码器,同时使用我们熟悉的PyTorch来解决神经网络的训练和预测问题。阅读本文前需要理解HMM-DNN的语音识别系统、WFST和Kaldi的基本用法。

架构

了解了Kaldi的基本用法,Kaldi最早设计是基于HMM-GMM架构的,后来通过引入DNN得到HMM-DNN模型。但是由于Kaldi并不是一个深度学习框架,我们如果想使用更加复杂的深度学习算法会很困难,我们需要修改Kaldi里的C++代码,需要非常熟悉其代码才能实现。而且我们可能需要自己实现梯度计算,因为它不是一个Tensorflow或者PyTorch这样的框架。这样就导致想在Kaldi里尝试不同的深度学习(声学)模型非常困难。而PyTorch-Kaldi就是为了解决这个问题,它的架构如图下图所示,它把PyTorch和Kaldi完美的结合起来,使得我们可以把精力放到怎么用PyTorch实现不同的声学模型,而把PyTorch声学模型和Kaldi复杂处理流程结合的dirty工作它都帮我们做好了。

23213

简介

PyTorch-Kaldi的目的是作为Kaldi和PyTorch的一个桥梁,它能继承Kaldi的高效和PyTorch的灵活性。PyTorch-Kaldi并不只是这两个工具的粘合剂,而且它还提供了用于构建现代语音识别系统的很多有用特性。比如,代码可以很容易的插入用户自定义的声学模型。此外,用户也可以利用预先实现的网络结果,通过简单的配置文件修改就可以实现不同的模型。PyTorch-Kaldi也支持多个特征(feature)和标签(label)流的融合,使用复杂的网络结构。 它提供完善的文档并且可以在本地或者HPC集群上运行。

下面是最新版本的一些特性:

  • 使用Kaldi的简单接口
  • 容易插入(plug-in)自定义模型
  • 预置许多常见模型,包括MLP, CNN, RNN, LSTM, GRU, Li-GRU, SincNet
  • 基于多种特征、标签和网络结构的复杂模型实现起来非常自然。
  • 简单和灵活的配置文件
  • 自动从上一次处理的块(chunk)恢复并继续训练
  • 自动分块(chunking)和进行输入的上下文扩展
  • 多GPU训练
  • 可以本地或者在HPC机器上运行
  • TIMIT和Librispeech数据集的教程

依赖

Kaldi

我们首先需要安装Kaldi,读者请参考官方文档进行安装和学习Kaldi的基本用法。

安装好了之后需要把Kaldi的相关工具加到环境变量中,比如把下面的内容加到~/.bashrc下并且重新打开终端。

export KALDI_ROOT=/home/lili/codes/kaldi
PATH=$KALDI_ROOT/tools/openfst:$PATH
PATH=$KALDI_ROOT/src/featbin:$PATH
PATH=$KALDI_ROOT/src/gmmbin:$PATH
PATH=$KALDI_ROOT/src/bin:$PATH
PATH=$KALDI_ROOT/src/nnetbin:$PATH
export PATH

读者需要把KALDI_ROOT设置成kaldi的根目录。如果运行copy-feats能出现帮助文档,则说明安装成功。

安装PyTorch

目前PyTorch-Kaldi在PyTorch1.0和0.4做过测试,因此建议安装这两个版本的,为了提高效率,如果有GPU的话一定要安装GPU版本的PyTorch。

安装

使用下面的代码进行安装,建议使用virtualenv来构建一个干净隔离的环境。

git clone https://github.com/mravanelli/pytorch-kaldi
pip install -r requirements.txt

TIMIT教程

获取数据

数据可以在这里获取,注意这是要花钱的。因此没有这个数据的读者建议实验后面免费的Librispeech数据集。

我个人认为LDC这样收费其实是不利于这个行业发展的。计算机视觉方向能有这么快的发展,我觉得ImageNet数据集是有非常大贡献的。对于语音识别和NLP领域,学术界很多都使用LDC的数据集来做实验,即使还有其它免费的数据源(其实以前几乎没有,现在慢慢有一些了),用这些数据集做的使用学术界也不认可。这相当于设置了一个科研的门槛——不花钱购买LDC的数据就无法进入这个圈子。虽然说数据的价钱对于一个实验室来说并不贵,但它的购买方式也非常麻烦,尤其是对于外国人来说。里面有一些免费的数据,但是它并不直接提供下载,而是要讲过相当复杂的注册,提交申请,过了N多天之后才会给一个下载链接,网站还做得巨卡无比!

NLP很多数据集比如CTB树库等也是LDC提供的,因此也存在同样的问题。不过好在现在流行End-to-End的系统,那些语言学家感兴趣的中间步骤比如词性标注、句法分析其实并没有太多用处。当然这是我的个人看法,Frederick Jelinek曾经说道:”每当我开除一个语言学家,语音识别系统就更准了!” 我觉得也可以这样说:每当系统减掉一个中间环节,NLP系统也更加准确!

使用Kaldi进行训练

原理回顾

Kaldi是传统的HMM-GMM,我们希望用神经网络来替代其中的GMM声学模型部分。声学模型可以认为是计算概率𝑃(𝑋|𝑞)P(X|q),这里q表示HMM的状态,而X是观察(比如MFCC特征),但是神经网络是区分性(discriminative)模型,它只能计算𝑃(𝑞|𝑋)P(q|X),也就是给定观察,我们可以计算它属于某个状态的概率,也就是进行分类。当然,根据贝叶斯公式:

𝑃(𝑋|𝑞)=𝑃(𝑞|𝑋)𝑃(𝑋)𝑃(𝑞)∝𝑃(𝑞|𝑋)𝑃(𝑞)P(X|q)=P(q|X)P(X)P(q)∝P(q|X)P(q)

因为P(X)是固定的,大家都一样,所以可以忽略。但是我们还是需要除以每个状态的先验概率𝑃(𝑞)P(q),这个先验概率可以从训练数据中统计出来。

那现在的问题是怎么获得训练数据,因为语音识别的训练数据是一个句子(utterance)的录音和对应的文字。状态是我们引入HMM模型的一个假设,世界上并没有一个实在的物体叫HMM状态。因此我们需要先训练HMM-GMM模型,通过强制对齐(Force-Alignment)算法让模型标注出最可能的状态序列。对齐后就有了状态和观察的对应关系,从而可以训练HMM-DNN模型了,Kaldi中的HMM-GMM模型也是这样的原理。我们这里可以用PyTorch-Kaldi替代Kaldi自带的DNN模型,从而可以引入更加复杂的神经网络模型,而且实验起来速度更快,比较PyTorch是专门的神经网络框架,要实现一个新的网络结构非常简单。相比之下要在Kaldi里用C++代码实现新的神经网络就复杂和低效(这里指的是开发效率,但是运行效率也可能是PyTorch更快,但是这个只是我的猜测)。当然我们也可以先训练HMM-DNN,然后用HMM-DNN来进行强制对齐,因为HMM-DNN要比HMM-GMM的效果好,因此它的对齐也是更加准确。

Kaldi训练

原理清楚了,下面我们来进行Kaldi的训练,但是训练前我们需要修改几个脚本。

读者如果有TIMIT数据集,在运行前需要修改一些脚本里的路径,下面是作者的修改,供参考。 首先需要修改cmd.sh,因为我是使用单机训练,所以需要把queue.pl改成run.pl。

lili@lili-Precision-7720:~/codes/kaldi/egs/timit/s5$ git diff cmd.sh
diff --git a/egs/timit/s5/cmd.sh b/egs/timit/s5/cmd.sh
index 6c6dc88..7e3d909 100644
--- a/egs/timit/s5/cmd.sh
+++ b/egs/timit/s5/cmd.sh
@@ -10,10 +10,10 @@
 # conf/queue.conf in http://kaldi-asr.org/doc/queue.html for more information,
 # or search for the string 'default_config' in utils/queue.pl or utils/slurm.pl.
 
-export train_cmd="queue.pl --mem 4G"
-export decode_cmd="queue.pl --mem 4G"
+export train_cmd="run.pl --mem 4G"
+export decode_cmd="run.pl --mem 4G"
 # the use of cuda_cmd is deprecated, used only in 'nnet1',
-export cuda_cmd="queue.pl --gpu 1"
+export cuda_cmd="run.pl --gpu 1"

接着修改修改run.sh里的数据路径timit变量修改成你自己的路径,另外我的机器CPU也不够多,因此把train_nj改小一点。

lili@lili-Precision-7720:~/codes/kaldi/egs/timit/s5$ git diff run.sh
diff --git a/egs/timit/s5/run.sh b/egs/timit/s5/run.sh
index 58bd871..5c322cc 100755
--- a/egs/timit/s5/run.sh
+++ b/egs/timit/s5/run.sh
@@ -28,7 +28,7 @@ numLeavesSGMM=7000
 numGaussSGMM=9000
 
 feats_nj=10
-train_nj=30
+train_nj=8
 decode_nj=5
 
 echo ============================================================================
@@ -36,8 +36,8 @@ echo "                Data & Lexicon & Language Preparation
 echo ============================================================================
 
 #timit=/export/corpora5/LDC/LDC93S1/timit/TIMIT # @JHU
-timit=/mnt/matylda2/data/TIMIT/timit # @BUT
-
+#timit=/mnt/matylda2/data/TIMIT/timit # @BUT
+timit=/home/lili/databak/ldc/LDC/timit/TIMIT
 local/timit_data_prep.sh $timit || exit 1
 
 local/timit_prepare_dict.sh

最后我们开始训练:

cd kaldi/egs/timit/s5
./run.sh
./local/nnet/run_dnn.sh
强制对齐

我们有两种选择,第一种使用HMM-GMM的对齐来训练PyTorch-Kaldi,对于这种方式,训练数据已经对齐过了(因为训练HMM-DNN就需要对齐),所以只需要对开发集和测试集再进行对齐:

cd kaldi/egs/timit/s5
steps/align_fmllr.sh --nj 4 data/dev data/lang exp/tri3 exp/tri3_ali_dev
steps/align_fmllr.sh --nj 4 data/test data/lang exp/tri3 exp/tri3_ali_test

但是更好的是使用HMM-DNN来做对齐,作者使用的是这种方式,这就需要对训练集再做一次对齐了,因为之前的对齐是HMM-GMM做的,不是我们需要的。

steps/nnet/align.sh --nj 4 data-fmllr-tri3/train data/lang exp/dnn4_pretrain-dbn_dnn exp/dnn4_pretrain-dbn_dnn_ali
steps/nnet/align.sh --nj 4 data-fmllr-tri3/dev data/lang exp/dnn4_pretrain-dbn_dnn exp/dnn4_pretrain-dbn_dnn_ali_dev
steps/nnet/align.sh --nj 4 data-fmllr-tri3/test data/lang exp/dnn4_pretrain-dbn_dnn exp/dnn4_pretrain-dbn_dnn_ali_test
修改PyTorch-Kaldi的配置

我们这里只介绍最简单的全连接网络(基本等价与Kaldi里的DNN),这个配置文件在PyTorch-Kaldi根目录下,位置是cfg/TIMIT_baselines/TIMIT_MLP_mfcc_basic.cfg。从这个文件名我们可以猜测出这是使用MFCC特征的MLP模型,此外cfg/TIMIT_baselines目录下还有很多其它的模型。这个我们需要修改其中对齐后的目录等数据,请读者参考作者的修改进行修改。

diff --git a/cfg/TIMIT_baselines/TIMIT_MLP_mfcc_basic.cfg b/cfg/TIMIT_baselines/TIMIT_MLP_mfcc_basic.cfg
index 6f02075..6e5dc5d 100644
--- a/cfg/TIMIT_baselines/TIMIT_MLP_mfcc_basic.cfg
+++ b/cfg/TIMIT_baselines/TIMIT_MLP_mfcc_basic.cfg
@@ -15,18 +15,18 @@ n_epochs_tr = 24
 [dataset1]
 data_name = TIMIT_tr
 fea = fea_name=mfcc
-	fea_lst=/home/mirco/kaldi-trunk/egs/timit/s5/data/train/feats.scp
-	fea_opts=apply-cmvn --utt2spk=ark:/home/mirco/kaldi-trunk/egs/timit/s5/data/train/utt2spk  ark:/home/mirco/kaldi-trunk/egs/timit/s5/mfcc/cmvn_train.ark ark:- ark:- | add-deltas --delta-order=2 ark:- ark:- |
+	fea_lst=/home/lili/codes/kaldi/egs/timit/s5/data/train/feats.scp
+	fea_opts=apply-cmvn --utt2spk=ark:/home/lili/codes/kaldi/egs/timit/s5/data/train/utt2spk  ark:/home/lili/codes/kaldi/egs/timit/s5/mfcc/cmvn_train.ark ark:- ark:- | add-deltas --delta-order=2 ark:- ark:- |
 	cw_left=5
 	cw_right=5
 	
 
 lab = lab_name=lab_cd
-	lab_folder=/home/mirco/kaldi-trunk/egs/timit/s5/exp/dnn4_pretrain-dbn_dnn_ali
+	lab_folder=/home/lili/codes/kaldi/egs/timit/s5/exp/dnn4_pretrain-dbn_dnn_ali
 	lab_opts=ali-to-pdf
 	lab_count_file=auto
-	lab_data_folder=/home/mirco/kaldi-trunk/egs/timit/s5/data/train/
-	lab_graph=/home/mirco/kaldi-trunk/egs/timit/s5/exp/tri3/graph
+	lab_data_folder=/home/lili/codes/kaldi/egs/timit/s5/data/train/
+	lab_graph=/home/lili/codes/kaldi/egs/timit/s5/exp/tri3/graph
 	
 
 n_chunks = 5
@@ -34,18 +34,18 @@ n_chunks = 5
 [dataset2]
 data_name = TIMIT_dev
 fea = fea_name=mfcc
-	fea_lst=/home/mirco/kaldi-trunk/egs/timit/s5/data/dev/feats.scp
-	fea_opts=apply-cmvn --utt2spk=ark:/home/mirco/kaldi-trunk/egs/timit/s5/data/dev/utt2spk  ark:/home/mirco/kaldi-trunk/egs/timit/s5/mfcc/cmvn_dev.ark ark:- ark:- | add-deltas --delta-order=2 ark:- ark:- |
+	fea_lst=/home/lili/codes/kaldi/egs/timit/s5/data/dev/feats.scp
+	fea_opts=apply-cmvn --utt2spk=ark:/home/lili/codes/kaldi/egs/timit/s5/data/dev/utt2spk  ark:/home/lili/codes/kaldi/egs/timit/s5/mfcc/cmvn_dev.ark ark:- ark:- | add-deltas --delta-order=2 ark:- ark:- |
 	cw_left=5
 	cw_right=5
 	
 
 lab = lab_name=lab_cd
-	lab_folder=/home/mirco/kaldi-trunk/egs/timit/s5/exp/dnn4_pretrain-dbn_dnn_ali_dev
+	lab_folder=/home/lili/codes/kaldi/egs/timit/s5/exp/dnn4_pretrain-dbn_dnn_ali_dev
 	lab_opts=ali-to-pdf
 	lab_count_file=auto
-	lab_data_folder=/home/mirco/kaldi-trunk/egs/timit/s5/data/dev/
-	lab_graph=/home/mirco/kaldi-trunk/egs/timit/s5/exp/tri3/graph
+	lab_data_folder=/home/lili/codes/kaldi/egs/timit/s5/data/dev/
+	lab_graph=/home/lili/codes/kaldi/egs/timit/s5/exp/tri3/graph
 	
 
 n_chunks = 1
@@ -53,18 +53,18 @@ n_chunks = 1
 [dataset3]
 data_name = TIMIT_test
 fea = fea_name=mfcc
-	fea_lst=/home/mirco/kaldi-trunk/egs/timit/s5/data/test/feats.scp
-	fea_opts=apply-cmvn --utt2spk=ark:/home/mirco/kaldi-trunk/egs/timit/s5/data/test/utt2spk  ark:/home/mirco/kaldi-trunk/egs/timit/s5/mfcc/cmvn_test.ark ark:- ark:- | add-deltas --delta-order=2 ark:- ark:- |
+	fea_lst=/home/lili/codes/kaldi/egs/timit/s5/data/test/feats.scp
+	fea_opts=apply-cmvn --utt2spk=ark:/home/lili/codes/kaldi/egs/timit/s5/data/test/utt2spk  ark:/home/lili/codes/kaldi/egs/timit/s5/mfcc/cmvn_test.ark ark:- ark:- | add-deltas --delta-order=2 ark:- ark:- |
 	cw_left=5
 	cw_right=5
 	
 
 lab = lab_name=lab_cd
-	lab_folder=/home/mirco/kaldi-trunk/egs/timit/s5/exp/dnn4_pretrain-dbn_dnn_ali_test
+	lab_folder=/home/lili/codes/kaldi/egs/timit/s5/exp/dnn4_pretrain-dbn_dnn_ali_test
 	lab_opts=ali-to-pdf
 	lab_count_file=
  • 17
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

训灼说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值