Kaldi-Timit脚本

数据准备

local/timit_data_prep.sh

生成的内容放在data/local/data中。
对于train、test和dev,分别生成相应的_sph.flist_sph.scp.uttids.trans.text_wav.scp.utt2spk.spk2utt.spk2gender.stm.glm文件。
NOTE:
.wav files are not really .wav, they are .sph. Use tools/sph2pipe_v2.5/sph2pipe to convert.

  • 一大堆检查

    • 检查参数数目是否正确
    • 检查sph2pipe程序是否可用
    • 检查conf/test_spk.list和conf/dev_spk.list是否存在
    • 检查train和test目录是否存在
    • 检查目录名是大写的还是小写的(TRAIN或者train)
  • 创建临时文件

  • 用trap命令设置收到EXIT信号时删除临时文件

  • 转换大小写,将dev_spk.list和test_spk.list分别写到$tmpdir/dev_spk和$tmpdir/test_spk中,用ls和sed生成$tmpdir/train_spk,也就是训练集中的说话人名字列表

在目录data/local/data下,未注明路径的文件都是生成在该路径下

  • for x in train dev test; do
    • 生成与x相关的.WAV路径列表,放在${x}_sph.flist
    • 根据${x}_sph.flist中的人名和句子名,生成utt-id(人名_句子名),放在$tmpdir/${x}_sph.uttids中
    • 将.uttids和_sph.flist中的同一行粘在一起,根据第一列的uttids进行排序后放在${x}_sph.scp中,每一行的格式为:utt-id 对应的.WAV路径
    • 根据${x}_sph.scp的第一列生成${x}.uttids
    • 生成与x相关的.PHN路径列表,放在$tmpdir/${x}_phn.flist中(.PHN文件中存放的是音素抄本)
    • 根据_phn.flist中的人名和句子名,生成utt-id(人名_句子名),放在$tmpdir/${x}_phn.uttids中
    • 将存放在.PHN文件中的抄本转换为kaldi使用的格式保存在$tmpdir/${x}_phn.trans中(.PHN中,一个音素一行,将这些音素放在一行)
    • 将.uttids和.trans中的同一行粘在一起,根据第一列的uttids进行排序后放在${x}.trans中,每一行的格式为:utt-id 对应的抄本
    • 调用local/timit_norm_trans.pl对${x}.trans进行normalization,将h#替换成sil,生成$x.text
    • 创建${x}_wav.scp,每一行的格式为:utt-id extendedfilename
    • 生成$x.utt2spk,调用utils/utt2spk_to_spk2utt.pl生成$x.spk2utt
    • 生成性别映射$x.spk2gender
    • 为sclite准备stm文件:调用程序wav-to-duration生成${x}_dur.ark,用awk生成stm文件
    • 创建假glm文件

local/timit_prepare_dict.sh

生成的文件放在data/local/dictdata/local/lm_tmpdata/local/nist_lm中。

(1)准备字典
以下数据的默认路径是data/local/dict

  • 将sil写到silence_phones.txtoptional_silence.txt
  • 根据train.text生成phones.txt
  • 根据phones.txt生成lexicon.txt(将phones.txt的一列复制成两列)
  • 根据silence_phones.txt和phones.txt生成nonsilence_phones.txt
  • 根据silence_phones.txt和nonsilence_phones.txt生成extra_questions.txt

(2)创建音素bigram LM

  • 给data/train.text的每一句抄本句首加<s>,句尾加</s>,放在data/lm_train.text
  • 调用irstlm/bin/build-lm.sh生成音素语言模型,即lm_tmp/lm_phone_bg.ilm.gz
  • 调用irstlm/bin/compile-lm对上一步生成的LM进行处理,输出为nist_lm/lm_phone_bg.arpa.gz

utils/prepare_lang.sh

生成的文件放在data/langdata/local/lang_tmp中,需要用到data/local/dict中的文件。
针对timit run.sh中的参数而言,部分if分支的解释略去。

tmpdir=data/local/lang_tmp

  • 调用utils/validate_dict_dir.pl检验data/local/dict下的除phones.txt外的所有文件
  • 如果没有dict/lexiconp.txt,根据dict/lexicon.txt生成之,其格式为:音素 1.0 音素。
  • 经过几个if,将dict/lexiconp.txt复制到$tmpdir/lexiconp.txt
  • 根据dict下的silence_phones.txt和nonsilence_phones.txt生成$tmpdir/phones,将此phones的一列复制成两列生成$tmpdir/phone_map.txt
  • 调用utils/apply_map.pl生成lang/phones/sets.txt
  • 根据sets.txt生成lang/phones/roots.txt,其中每一行的开头为shared split
  • 根据dict中的silence_phones.txt、nonsilence_phones.txt、optional_silence.txt生成lang/phones下的silence.txtnonsilence.txtoptional_silence.txt
  • 根据lang/phones下的silence.txt生成lang/phones/context_indep.txt
  • 根据dict中的extra_questions.txt生成lang/phones/extra_questions.txt
  • 生成$tmpdir/lexiconp_disambig.txt
  • 生成$tmpdir/lex_ndisambig
  • 根据整数变量ndisambig(此处为1)的大小,生成phones/disambig.txt
  • 生成音素符号表lang/phones.txt。将<eps>和phones/{silence,non_silence,disambig}.txt合在一起生成phones.txt,每一行的格式为:音素 编号。
  • 生成phones/word_boundary.txt,本参数下不生成该文件
  • 创建单词符号表lang/words.txt,根据$tmpdir/lexiconp.txt生成之,每一行的格式为:音素 编号。附加有<eps>、#0、<s>和</s>
  • 将$tmpdir/lexiconp.txt的第一列第三列提取出来生成$tmpdir/align_lexicon.txt;并将<eps> sil补加到$tmpdir/align_lexicon.txt
  • 根据$tmpdir/align_lexicon.txt生成phones/align_lexicon.txt
  • 根据align_lexicon.txt、phones.txt和words.txt生成phones/lexicon.int
  • 调用utils/make_lexicon_fst.pl生成lang/L.fst
  • 生成lang/oov.txtlang/oov.int,int映射是根据words.txt生成的
  • 将#0写到phones/wdisambig.txt中,根据phones.txt将wdisambig.txt映射为phones/wdisambig_phone.int,根据words.txt将wdisambig.txt映射为phones/wdisambig_words.int
  • 根据phones.txt的映射,生成silence、nonsilence、optional_silence、disambig和context_indep的int和csl版本,生成sets和extra_question的int版本,生成roots的int版本
  • 调用utils/gen_topo.pl,根据silence.csl和nonsilence.csl生成lang/topo
  • 调用utils/make_lexicon_fst.pl生成lang/L_disambig.fst
  • 验证lang整个目录下的文件

local/timit_format_data.sh

  • 准备train、dev和test数据。创建data下创建train、dev和test目录,将data/local/data中的相应数据分别复制到data目录下相应的目录中
  • 为测试准备语言模型。创建data/lang_test_bg目录,将data/lang下的所有文件复制到该目录。根据data/local/nist_lm下的音素二元语言模型lm_phone_bg.arpa.gz生成G.fst
  • 验证lang_test_bg下文件,删除data/local/lm_tmp目录

特征提取

对data下的train、dev和test数据分别调用下面两个脚本

steps/make_mfcc.sh

特征存在feats.scp中,存储的特征是每一帧13维,当用到mfcc特征的时候才计算deltas和deltas-deltas,转为39维。
脚本主要为下面几行

  $cmd JOB=1:$nj $logdir/make_mfcc_${name}.JOB.log \
    compute-mfcc-feats  $vtln_opts --verbose=2 --config=$mfcc_config \
     scp,p:$logdir/wav_${name}.JOB.scp ark:- \| \
      copy-feats --compress=$compress ark:- \
      ark,scp:$mfccdir/raw_mfcc_$name.JOB.ark,$mfccdir/raw_mfcc_$name.JOB.scp \
      || exit 1;

其中$cmd为run.pl

以data/train为例

  • 检查。检查一些文件是否存在等
  • 根据变量$nj的大小,将data/train/wav.scp平分为$nj份
  • 调用run.pl,提取特征。一个JOB处理一份wav.scp,共$nj个JOB。用到程序compute-mfcc-featscopy-feats,生成mfcc/raw_mfcc_train.JOB.ark和对应的mfcc/raw_mfcc_train.JOB.scp文件,JOB为1到$nj的数字。
  • 将$nj个mfcc/raw_mfcc_train.JOB.scp文件合成一个data/train/feats.scp文件
  • 检查。检查是否正确提取所有文件

steps/compute_cmvn_stats.sh

  • 对每个说话人计算cmvn(cepstral mean and variance normalization)。
  • 对每个说话人,对每一维(共13维)分别计算count/sum/sum-squared三组statistics,分别为1维、13维、13维。count代表该说话人所有音频文件的总帧数,sum代表所有帧里每一维的和,sum-square代表所有帧里每一维的平方的和。然后根据这三组statistics,计算均值和方差。
  • 可用copy-matrixs程序查看cmvn.scp或cmvn.ark文件,对每一个说话人,有一个28维的矩阵,前十三维是sum,然后是count,接着十三维是sum-squared,最后一个0。
  • 该脚本有三个选项:--fake--two-channel--fake-dims
    调用compute-cmvn-stats生成mfcc/cmvn_train.arkmfcc/cmvn_train.scp,然后将后者复制到data/train/cmvn.scp

单因素训练和解码

steps/train_mono.sh

主要输出为:final.mdltree

  • 创建sdatadata/train/split$nj($nj为作业数,此处为30),将data/train分为$nj
  • 57 调用程序apply-cmvnadd-deltas生成39维特征(原始的mfcc只有13维)
  • 65-75 run.pl调用程序gmm-init-mono初始化单音素模型,在exp/mono/下生成0.mdltree
  • 80-86 编译训练图(compiling traning graphs),调用程序compile-train-graphs,生成exp/mono/fsts.JOB.gz(共有$nj个JOB);对每句话,编译FST;
  • 88-94 等价对齐数据(Aligning data equally),调用程序align-equal-compiled(Write an equally spaced alignment (for getting training started))和gmm-acc-stats-ali(Accumulate stats for GMM training.),生成exp/mono/0.JOB.acc
  • 99-103 调用gmm-est对0.mdl进行重新估计,该程序对基于GMM的声学模型进行最大似然重新估计。删除exp/mono/0.*.acc,生成exp/mono/1.mdl
  • 109-134 开始迭代更新参数。用到程序gmm-align-compiled(Align features given [GMM-based] models. Viterbi alignment)、gmm-acc-stats-aligmm-est

变量$stage的妙用:设置这个变量值,可以从中断的地方重新开始训练,不至于从头开始。比如设置该值为10,则接着第10次迭代,直接开始第11次迭代,而不用从头开始。(个人想法)
对于每个程序的用法,可以直接在命令行输入程序名查看其使用信息。

utils/mkgraph.sh

该脚本创建一个完全扩展的解码图(HCLG),该解码图表示语言模型、发音字典、上下文相关性和HMM结构。输出是一个FST。

$lang=data/lang_test_bg

required="$lang/L.fst $lang/G.fst $lang/phones.txt $lang/words.txt $lang/phones/silence.csl $lang/phones/disambig.int $model $tree"
  • L_disambig.fst(lexicon,发音字典)和G.fst(语言模型)生成最新的$lang/tmp/LG.fst
  • 生成最新的$lang/tmp/CLG_1_0.fst 和 ilabels_1_0 和 disambig_ilabels_1_0.int,需要LG.fstdisambig.int
  • 生成最新的exp/mono/graph/Ha.fst,需要文件$tree$model
  • 生成最新的exp/mono/graph/HCLGa.fst
  • 生成最新的exp/mono/graph/HCLG.fst,调用程序add-self-loops
  • 检查HCLG.fst是否为空
  • $lang下的一些文件复制到exp/mono/graph

steps/decode.sh

Usage: steps/decode.sh [options] <graph-dir> <data-dir> <decode-dir>
  • 调用gmm-latgen-fastergmm-latgen-faster-parallel进行解码,生成lat.JOB.gz
  • 如果设置变量skip_scoring为false,则调用local/score.sh进行打分。

三音素:deltas + delta-deltas 训练和解码

steps/align_si.sh

usage: steps/align_si.sh <data-dir> <lang-dir> <src-dir> <align-dir>
作用:使用src-dir中的模型对data-dir中的数据进行对齐,将结果放在align-dir中

用到的文件:
data/train/text data/lang/oov.in exp/mono/tree exp/mono/final.mdl exp/mono/final.occs
输出:
exp/mono_ali/ali.JOB.gz

  • 先调用compile-train-graphs对每句话生成FST
  • 再调用gmm-align-compiled对数据进行对齐,结果放在exp/mono_ali/ali.JOB.gz
  • 调用steps/diagnostic/analyze_alignments.sh对对齐结果进行分析

steps/train_deltas.sh

Usage: steps/train_deltas.sh <num-leaves> <tot-gauss> <data-dir> <lang-dir> <alignment-dir> <exp-dir>

一段理论:

  • 训练单音素系统(或者之前建立的三音素系统)是为了得到数据的时间对齐结果;对每一个在数据里看到的三音素,accumulate sufficent statistics to train a single Gaussian per HMM state.
  • statsstatistics的缩写,sufficient statistics for Gaussian are (count, sum, sum-squared),每个状态所拥有的个数分别为(1,39,39)。
  • “pdf-class” which is
    normally synonymous with HMM-state.
  • decision tree leaves are integers, call these pdf-ids(zero-based, but transition-ids are one-based)
  • gmm-init-model把两个对象写到模型文件中(x.mdl):TransitionModelAmDiagGmmTransitionModel存储HMM transition probabilities,追踪HMM topologies(包含HmmTopology对象)。
  • traisiton-ids:对应转移概率的一个索引

流程:

  • accumulating tree statistics,用到程序acc-tree-statssum-tree-stats
  • 避免使用手工制作的语音问题集,对音素进行聚类得到问题集;一个问题只是一个音素集;用到程序cluster-phones
  • compiling the questions
  • build-tree
  • 初始化模型gmm-init-model

推荐阅读更多精彩内容

  • 刚刚拿到一个简单语料库练手,发现只有语音和对应文字, 这篇文章记录了从数据预处理到kaldi对数据进行训练和测试的...
    zqh_zy阅读 8,446评论 14 19
  • 承接前面的《浅谈机器学习基础》、《浅谈深度学习基础》和《浅谈自然语言处理基础》,主要参考了《解析深度学习:语音识别...
    我偏笑_NSNirvana阅读 18,365评论 6 65
  • 对于文字的把握,每个人都有自身的见解,独于自我而言,阅读量到底该怎么解读。文字是个很神奇的事物,因为排列方式的不同...
    雨微水阅读 61评论 0 1
  • (一) 2006年,我转学来到了这个初中,初识时,只是通过同桌见过她。印象极...
    baby小姐阅读 70评论 1 2
  • 我是从福州路走到外滩的,那里离外滩很近。期间经过一家全家便利店和中山东一路。 走到外白渡桥的时候,正是黄昏,看到景...
    黑麦的光影部落阅读 137评论 0 2