深入浅出语音识别-yesno例子剖析

源文件如下:


#!/bin/bash


train_cmd="utils/run.pl"


decode_cmd="utils/run.pl"


#========== 下载原始数据文件并解压到waves_yesno目录========


if [! -d waves_yesno ]; then
   wget http://www.openslr.org/resources/1/waves_yesno.tar.gz || exit 1;
   tar -xvzf waves_yesno.tar.gz || exit 1;
fi

train_yesno=train_yesno
test_base_name=test_yesno

#========== 删除上一次运行生成的文件==================
rm -rf data exp mfcc

#======== 第一步:数据准备 =========================
local/prepare_data.sh waves_yesno
local/prepare_dict.sh
utils/prepare_lang.sh --position-dependent-phones false data/local/dict "<SIL>" data/local/lang data/lang
local/prepare_lm.sh

#========第二步:语音信号特征抽取 ====================
for x in train_yesno test_yesno; do
  steps/make_mfcc.sh --nj 1 data/$x exp/make_mfcc/$x mfcc
  steps/compute_cmvn_stats.sh data/$x exp/make_mfcc/$x mfcc
  utils/fix_data_dir.sh data/$x
done

#========第三步:单音模型训练 =======================
steps/train_mono.sh --nj 1 --cmd "$train_cmd" --totgauss 400 data/train_yesno data/lang exp/mono0a

#========第四步:生成解码矩阵 =======================
utils/mkgraph.sh data/lang_test_tg exp/mono0a exp/mono0a/graph_tgpr

#========第五步:解码 =============================
steps/decode.sh --nj 1 --cmd "$decode_cmd" exp/mono0a/graph_tgpr data/test_yesno exp/mono0a/decode_test_yesno
for x in exp/*/decode*; do
  [ -d $x ] && grep WER $x/wer_* | utils/best_wer.sh;
done

########################################################################

下面再对每一步进行详细探究:

#======== 第一步:数据准备 =========================

  1. local/prepare_data.sh waves_yesno
  2. local/prepare_dict.sh
  3. utils/prepare_lang.sh --position-dependent-phones false data/local/dict "<SIL>" data/local/lang data/lang
  4. local/prepare_lm.sh

prepare_data.sh脚本带一个参数,参数为存放语音数据文件的目录名。这个脚本做了以下几件事情:

  • 创建data和data/local目录
  • 在local目录下生成音频文件列表文件waves_all.list,存放语音数据文件的文件名列表 
  • 再把waves_all.list一分为两个文件waves.train和waves.test,前者用来训练,后者用来测试。
  • 用waves.train生成对应的scp文件,文件名为train_yesno_wav.scp
  • 用waves.test生成对应的scp文件,文件名为test_yesno_wav.scp
  • 用waves.train生成对应的txt文件,文件名为train_yesno_wav.txt
  • 用waves.test生成对应的txt文件,文件名为test_yesno_wav.txt
  • 将input目录下的task.arpabo拷贝到data/local目录下,改名为lm_tg.arpa
  • 在data目录下生成test_yesno目录和train_yesno目录,将train_yesno_wav.scp和train_yesno_wav.txt拷贝到train_yesno目录下,修改文件名为wav.scp和text;将test_yesno_wav.scp和test_yesno_wav.txt拷贝到test_yesno目录下,修改文件名为wav.scp和text;同时在这两个目录下均生成spk2utt和 utt2spk两个文件。
  • data目录下的数据准备完毕。data下有三个目录,local、test_yesno和train_yesno。local下存放语言模型文件lm_tg.arpa;test_yesno和train_yesno下均有四个文件,text、wav.scp、utt2spk和spk2utt,而且都是根据相应的训练数据和测试数据生成。
  1. text文件内容格式为<utteranceID  wav_file_path>,即<语音标号  对应的语音文件路径>
  2. wav.scp内容格式为<utteranceID  scripts>, 即<语音标号   语音对应的文本脚本>
  3. utt2spk内容格式为<utteranceID  speakerID>, 即<语音标号  讲话人ID>
  4. spk2utt内容格式为<speakerID  utteranceID ......>,  即<讲话人ID  属于这个讲话人的语音标号串,以空格分隔>

prepare_dict.sh脚本不带参数。分别做了以下几件事情:

  • 在data/local目录下创建dict子目录。
  • 在dict子目录下生成四个文件lexicon_words.txt、lexicon.txt、nonsilence_phones.txt、silence_phones.txt和optional_silence.txt。
  1. lexicon_words.txt内容格式为<单词 音素组合,音素之间用空格分隔>
  2. lexicon.txt内容格式为<单词 音素组合,音素之间用空格分隔>,与lexicon_words.txt的区别是lexicon.txt包含了静音SIL。
  3. nonsilence_phones.txt内容格式为<非静音音素>
  4. silence_phones.txt内容格式为<静音音素>
  5. optional_silence.txt内容格式为<其他特殊音素>

prepare_lang.sh脚本的功能主要是上面的字典dict数据来生成L.fst和HMM拓扑网络,也就是音素的状态机。

  • utils/prepare_lang.sh --position-dependent-phones false data/local/dict "<SIL>" data/local/lang data/lang
  • 命令行参数 --position-dependent-phones false, 这里设置为false (默认状态是true)。--option-dependent-phones与词位信息有关,如果设置为true,则用_B _E _I _S 表示开始、结束、词中、单个词。因为yesno这个例子都是孤立词,所以设置为false。
  • 四个数据参数 data/local/dict "<SIL>" data/local/lang data/lang,分别代表 <input_dir> <oov> <temperary_dir> <output_dir>。
  1. <input_dir>表示输入数据所在的目录,这里就是上一个脚本生成的字典dict目录;
  2. <oov>表示数据集之外的单词,会映射到这里,这个例子没有数据集之外的单词,只把"<SIL>"放进去;
  3. <temperary_dir>是临时目录,存放中间生成的临时文件;
  4. <output_dir>是数据输出的存放目录,这里的文件才是最要关心的。
  • 在<output_dir>下会生成以下文件:
  1. L.fst文件:这是一个 fst 格式的发音词典,输入是音素,而输出是词。
  2. L_disambig.fst文件:这也是一个 fst 格式的发音词典,只是在这个文件当中,添加了消除歧义的符号,比如#1,#2,以及自循环符号 #0。
  3. oov.int文件:这个文件里面存储了一些符号,所有ooV词(词表之外的词,out of  vocabulary)都会被映射为这个符号。
  4. phones.txt文件:音素和整数标号的映射
  5. topo文件: 这个文件里面存储了我们后将要用到的 HMM 模型的拓扑模型,可以看到有两部分,第一部分 <ForPhones></ForPhones> 之间的数字是 2,3 ,是发音的编号指的是 YES, NO ,这个可以从 phones.txt 找到,自然 下面一部分 的数字 1 指的是SIL。 YES,NO 公有3种状态,SIL有五种状态,它们各自状态之间的转移以及转移概率则是每一行<Transition> state probability 指定。
  6. words.txt文件:单词和整数标号的映射
  7. phones目录:phones 目录下的文件很明显有一个特点,每一个文件都有3个,但是他们的后缀名都不一样,同名不同后缀的文件,其实有着完全一样的内容,只是他们各自存储信息的格式是不同的(下列文件不一定全部有)。

context_indep.* 存储了上下文无关的信息。

align_lexicon  表示对齐文件,是由lexiconp.txt的第一列第三列提取出来生成

nonsilence.* silence.* optional_silence.*存储了音素信息。

extra_question.*存储的是一些额外的信息,主要是和音调和重音之类的信息有关。

disambig.* 则存储了一些消除歧义使用的符号,在前面已经见过,比如#0, #1等等。

set.* 包含了一系列的因素集,一般是将同一个音素(有时候会包含词位信息 _B _E)存储在同一行,聚类使用。

align_lexicon  表示对齐文件,是由lexiconp.txt的第一列第三列提取出来生成

context_indep  里面包含的是那些非正常音素,包含(静音(SIL),口语噪声(SPN),非口语噪声(NSN)和笑声(LAU)

word_boundary 里面是音素和词位的关联信息,建立这种对应关系是需要这些信息在音素网络中恢复词的边界

roots  里面包含的是建立音素上下文决策树信息,里面的shared 表示共享根,一般语气和语调会在同一行,认为共享
 

prepare_lm.sh脚本的功能是利用task.arpabo(lm_tg.arpa)来生成语言模型的状态机G.fst。

在基于 wfst 的语音识别中,需要将 HCLG 四个不同层次的模型复合(composition)在一起构成一个超大的解码网络,其中的 G 即是语言模型的 WFST表示。但是我们常见的语言模型并不是以 WFST 形式存在的,而是基于 ngram 实现的,通常以 arpa 文件形式存在。所以要将 arpa 文件转为 wfst,在 kaldi 中以 arpa2fst 脚本来转换。

  • 在data目录下创建一个lang_test_tg目录,将data/lang下的文件拷贝到lang_test_tg目录下
  • arpa2fst  --disambig-symbol=#0  --read-symbol-table=data/lang_test_tg/words.txt  input/task.arpabo  data/lang_test_tg/G.fst
  • arpa2fst [opts]  <input-arpa>  <output-fst>

到这里,所有数据准备完毕。在data目录下生成了后续需要的所有数据。除了训练和测试用的音频文件目录外,在data/lang_test_tg下包含了L.fst, G.fst,topo(HMM拓扑网络)和其他相关文件。

 

#========第二步:语音信号特征抽取 ====================
for x in train_yesno test_yesno; do
  steps/make_mfcc.sh --nj 1 data/$x exp/make_mfcc/$x mfcc  // 提取语音信号特征码
  steps/compute_cmvn_stats.sh data/$x exp/make_mfcc/$x mfcc // 计算标准特征码
  utils/fix_data_dir.sh data/$x
done

  • make_mfcc.sh [opt] <data-dir> <log-dir> <mfcc-dir> :--nj 1表示只需要一个处理进程,因为数据量很少。data/train_yesno为训练数据目录;data/test_yesno为测试数据目录。exp/make_mfcc/train_yesno为存放训练日志;exp/make_mfcc/test_yesno为存放测试日志。mfcc为存放输出特征值的目录。
  • compute_cmvn_stats.sh [opt] <data-dir> [<log-dir> [<cmvn dir>] ]: data/train_yesno为训练数据目录;data/test_yesno为测试数据目录;exp/make_mfcc/train_yesno为存放训练日志;exp/make_mfcc_test_yesno为存放测试日志;mfcc为存放输出结果目录。
  • fix_data_dir.sh <data-dir>:检查确保data-dir目录下的数据文件被正确的排序和过滤,比如删除掉没有特征码的语音utterance。

#========第三步:单音模型训练 =======================
steps/train_mono.sh --nj 1 --cmd "$train_cmd" --totgauss 400 data/train_yesno data/lang exp/mono0a

因为这个例子的语音都是非常简单的单个单词,单词和单词之间的影响很小,所以只需要做单音模型训练即可。训练的目标就是获得语音特征值和音素的映射网络GMM-HMM,也就是声学模型。

  • steps/train_mono.sh [options] <data-dir> <lang-dir> <exp-dir>
  • [options] / --config <config-file> 包含配置参数的文件
  • [options] / --nj <nj>  并行工作进程的个数
  • [options] / --cmd (utils/run.pl | queue.pl <queue opts> ) 工作进程的运行方式
  • [options] / --totgauss 400 单高斯函数的个数为400

训练的过程:

1.首先是初始化GMM,使用的脚本是/kaldi-trunk/src/gmmbin/gmm-init-mono,输出是0.mdl和tree文件;

2.compile training graphs,使用的脚本是/kaldi-trunk/source/bin/compile-training-graphs,输入是tree,0.mdl和L.fst

   输出是fits.JOB.gz,其是在训练过程中构建graph的过程;

3.接下来是一个对齐的操作,kaldi-trunk/source/bin/align-equal-compiled;

4.然后是基于GMM的声学模型进行最大似然估计得过程,脚本为/kaldi-trunk/src/gmmbin/gmm-est;

5.然后进行迭代循环中进行操作,如果本步骤到了对齐的步骤,则调用脚本kaldi-kaldi/src/gmmbin/gmm-align-compiled;

6.重新估计GMM,累计状态,用脚本/kaldi-trunk/src/gmmbin/gmm-acc-states-ali;调用新生成的参数(高斯数)重新估计GMM,调用脚本/kaldi-trunk/src/gmmbin/gmm-est;

7.对分散在不同处理器上的结果进行合并,生成.mdl结果,调用脚本gmm-acc-sum;

8.增加高斯数,如果没有超过设定的迭代次数,则跳转到步骤5重新进行训练

最后生成的.mdl即为声学模型文件
 

#========第四步:结合声学模型和语言模型(L.fst + G.fst)生成解码矩阵 =======================
utils/mkgraph.sh data/lang_test_tg exp/mono0a exp/mono0a/graph_tgpr

  • data/lang_test_tg目录下存放着语言模型
  • exp/mono0a目录下存放着声学模型
  • exp/mono0a/graph_tgpr为输出目录,存放最后生成的HCLG解码网络HCLG.fst

 

#========第五步:解码测试 =============================
steps/decode.sh --nj 1 --cmd "$decode_cmd" exp/mono0a/graph_tgpr data/test_yesno exp/mono0a/decode_test_yesno
for x in exp/*/decode*; do
  [ -d $x ] && grep WER $x/wer_* | utils/best_wer.sh;
done

  • steps/decode.sh [opt] <graph-dir> <data-dir> <decode-dir>
  • [opt] / --config <config-file> :存放配置的文件
  • [opt] / --nj <nj> : 并行工作进程的个数
  • [opt] / --iter <iter> 迭代模型测试的次数
  • [opt] / --model 选用哪个模型
  • [opt] / --cmd (utils/run.pl | utils/queue.pl <queue opts>) 工作进程的运行方式
  • [opt] / --transform-dir <trans-dir>  fMLLR转换的目录
  • [opt] / -- acwt <float> 用于lattice 产生的声学模型刻度
  • [opt] / --scoring-opts <string> 用于local/score.sh
  • [opt] / --num-threads <n> 线程的个数
  • graph-dir为解码网络文件存放的目录
  • data-dir为测试音频文件的目录
  • decode-dir为解码结果存放的目录

 

总结一下:

  • data/train_yesno子目录下存放训练用的音频文件
  • data/test_yesno子目录下存放测试用的音频文件
  • data/lang下存放生成语言模型的文件
  • 利用data/lang下的文件在data/lang_test_tg目录下生成的语言模型
  • 利用data/train_yesno下的文件,训练出exp/mono0a目录下生成的声学模型
  • 然后结合语言模型和声学模型生成HCLG解码网络存放在exp/mono0a/graph_tgpr下
  • 最后,利用这个HCLG解码网络来测试data/test_yesno下的音频文件,得到测试结果。
  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值