Kaldi官方github地址:https://github.com/kaldi-asr/kaldi
Kaldi目录结构
./tools, ./src, ./egs 这三个目录是比较重要的。
./tools目录下面全部都是Kaldi依赖的包。其中主要有:
- OpenFST:Weighted Finite State Transducer library,是一个用来构造有限状态自动机的库。我们知道隐马尔科夫模型就可以看成是一个有限状态自动机的。这是最终要的一个包,Kaldi的文档里面说:If you ever want to understand Kaldi deeply you will need to understand OpenFst.诶,要学的好多。
- ATLAS:这是一个C++下的线性代数库。做机器学习自然是需要很多矩阵运算的。
- IRSTLM:这是一个统计语言模型的工具包。
- sph2pipe:这是宾夕法尼亚大学linguistic data consortium(LDC)开发的一款处理SPHERE_formatted数字音频文件的软件,它可以将LDC的sph格式的文件转换成其它格式。
./src目录存放的是Kaldi的源代码。
./egs存放的是Kaldi提供的一些例子。
Kaldi的编译安装:
按照README.md中说明进行操作即可
运行yesno实例
该实例是一个非常小的数据集,每一条记录都是一系列yes或者no的语音,标注是由文件名来标注的。先运行一下。
切换到./egs/yesno/s5目录下,运行sudo./run.sh命令。
经过一段时间的训练和测试,可以看到运行结果。
WER为0.00。看来这个例子识别的还是挺准的。
PS:WER(WordError Rate)是字错误率,是一个衡量语音识别系统的准确程度的度量。其计算公式是WER=(I+D+S)/N,其中I代表被插入的单词个数,D代表被删除的单词个数,S代表被替换的单词个数。也就是说把识别出来的结果中,多认的,少认的,认错的全都加起来,除以总单词数。这个数字当然是越低越好。
下面进入./yesno/s5/waves_yesno目录瞧一瞧。
全部都是.wav格式的音频文件。可以打开一个文件听一听,发现是一个老男人连续不停地说yes或者no,每个文件说8次。文件名中,0代表那个位置说的是no,1代表说的是yes。这个实验没有单独的标注文件,直接采用的是文件名来标注的。
那个waves_yesno.zip.gz数据可以在http://www.openslr.org/resources/1/waves_yesno.tar.gz上下载,解压到s5下面。也可以不下载,run.sh里就帮你做好了。你在实验前可以看下里面的说明文档。
运行thchs30(清华大学中文语料库)
https://blog.csdn.net/snowdroptulip/article/details/78943748
HCLG
L.fst: The Phonetic Dictionary FST
L maps monophone sequences to words.
The file L.fst is the Finite State Transducer form of the lexicon with phone symbols on the input and word symbols on the output.
L_disambig.fst:The Phonetic Dictionary with Disambiguation Symbols FST
A lexicon with disambiguation symbols
G.fst:The Language Model FST
FSA grammar (can be built from an n-gram grammar).
C.fst:The Context FST
C maps triphone sequences to monophones.
Expands the phones into context-dependent phones.
H.fst:The HMM FST
H maps multiple HMM states (a.k.a. transition-ids in Kaldi-speak) to context-dependent triphones.
Expands out the HMMs. On the right are the context-dependent phones and on the left are the pdf-ids.
HCLG.fst: final graph
总结一下:
构图过程 G -> L -> C -> H
G: 作为 acceptor (输入 symbol 与输出相同),用于对grammar 或者 language model 进行编码
L: Lexicon, 其输出 symbol 是 words, 输入 symbol 是 phones
C: context-dependency 其输出 symbol 是 phones, 其输入 symbol 为表示context-dependency phones
如: vector<int32> ctx_window = { 12, 15, 21 };
含义:id = 15 的 phone 为 中心 phone, left phone id = 12, right phone id = 21
H: 包括HMM definitions,其输出 symbol 为context-dependency phones, 其输入 symbol 为transitions-ids(即 对 pdf-id 和 其它信息编码后的 id)
asl=="add-self-loops”
rds=="remove-disambiguation-symbols”,
and H' is H without the self-loops:
HCLG = asl(min(rds(det(H' o min(det(C o min(det(L o G))))))))
ark文件与txt文件互相转换
这个很简单,也只需要用到copy-feats命令
copy-feats ark:train.ark ark,t:/train.txt ark转化为txt
copy-feats ark,t:train.txt ark:train.ark txt转化为ark
总结:主要的文件转化是通过copy-feats这个命令,它的主要功能是将文件变成数据流,这样方便对数据进行处理。
比如你想查看kaldi中提取的mfcc特征到底是什么样子,同样可以用copy-feats,如:
copy-feats ark:train.ark ark,t:- | less
通过一个管道用less来查看ark里面的内容。
https://blog.csdn.net/by21010/article/details/51776447
命令行级 I/O 机制
命令行 I/O 是指在 shell 上调用编译好的 Kaldi 工具的方法。比如,如果想查看音频文件的时长,就可以使用这样的命令
wav-to-duration scp:wav.scp ark,t:-
其中,wav-to-duration 是 Kaldi 中的查看时长的工具,后面两个是参数。下面具体介绍命令行级 Kaldi 的 I/O。
非表格型I/O
非表格型的 I/O 一般比较简单,一般是输入输出只包含一个或两个对象的文件或流(比如,声学模型文件,变换矩阵)。使用方式就是 shell 命令的使用方式。其中值得注意的有:
- Kaldi 采用一种扩展的文件名进行输入输出。读取文件的文件名称为 rxfilename , 写入的文件名称为 wxfilename。
- 在 rxfilename/wxfilename 处采用 “-” 来表示标准输入输出。
- rxfilename/wxfilename 处可以使用管道命令,比如先用gunzip将压缩文件解压,再输入到 Kaldi 程序中,即可在文件输入路径处填入 “gunzip -c foo.gz|”。
- rxfilename/wxfilename 后可以通过 “:” 来描述偏移量,如 “foo:1045” 表示从 foo 文件偏移 1045 个字节开始读取。
- 使用 –binary=true/false 来控制是否使用二进制输出。默认是true。
- 有一组 copy* 命令,可以用来查看文件。如,copy-matrix –binary=false foo.mat -。
- Log 信息和输出会混在一起显示,但是 Log 信息是 stderr 上,所以不会传到管道中。可以通过添加 2>/dev/null 命令,将log信息重定向到 /dev/null。
下面列举一些例子:
echo ‘[ 0 1 ]’ | copy-matrix –binary=false - -
将矩阵 [ 0 1 ] 输入到 copy-matrix 中,再显示出来。亦可以将其表示为
copy-matrix –binary=false ‘echo [ 0 1 ]|’ -
表格型I/O
对于一系列数据的集合,比如对应于每一句话的特征矩阵,或者每一条音频文件对应的路径,Kaldi 采用表格形式的数据表示。表格中,以没有空格的字符串为索引。Kaldi 中,称从表格文件中读取的一个字符串为 rspecifier, 写入表格文件的一个字符串为wspecifier。有两种表格文件格式:archive 和 script 。其读取方式和非表格型数据类似,也是使用 rxfilename/wxfilename的格式,其格式为[options]:path:[offset]。需要在 options 中注明是archive文件还是script文件。例如
wav-to-duration scp:wav.scp ark,t:-
rspecifiers 和 wspecifier
rspecifiers 和 wspecifier 是 Kaldi 中定义的两种术语,是存在于表格文件中的文件名,格式类似于 rxfilename/wxfilename, 亦可以采用管道命令呢,对于ark文件,可以添加选项,如ark,s,cs:-等。
两种存储格式:script-file 和 archive-file
script-file 是一种文件指针,里面包含的是具体文件所在路径,其格式为
key rspecifier|wspecifier
key 是字段名字,rspecifier/wspecifier则是文件路径,rspecifier/wspecifier 可以有空格。例如 “utt1 gunzip -c /foo/bar/utt1.mat.gz”。
archive-file 里面装的是实际的数据。其文件格式是
key1 object1 key2 object2 key3 object3 …
ark文件可以连接在一起,依然是一个有效的ark文件。但是如果需要的文件时有序的话,连接的时候应该要注意。
ark文件和scp文件有可能同时写,这时可以这么写:ark,scp:foo.ark,foo.scp。
特征提取
对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-feats
和copy-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.ark
和mfcc/cmvn_train.scp
,然后将后者复制到data/train/cmvn.scp
https://blog.csdn.net/Xwei1226/article/details/80491538
https://blog.csdn.net/u010731824/article/details/69668765
ASR指标:
正确率:只要和原来的标签相同就算正确。
准确率:除了要正确,还需要加上因为插入其它词造成的错误。
Correct(cor) = ( N − D − S )× 100% / N = H× 100% / N
Accurac(acc) = ( N− D− S− I )× 100% / N = (H - I)× 100% / N
N- total number of labels (总标签数)
D- deletion errors (删除错误)
S- substitution errors (替换错误)
I- insertion errors (插入错误)
ASR指标统计工具:HTK HResults