Kaldi thchs30手札(一)

特征提取阶段(line 0-33)

Posted by Pelhans on January 17, 2018

Kaldi是一个语音识别工具包,基于C++并遵循Apache v2.0协议。它包含语音信号处理、声学模型训练、解码等一系列工具,同时内部还带有各种语言的源代码实例。非常强大。

thchs30 的运行

程序到手,当然是先跑起来看看长什么样子,功能是什么。所以本部分先介绍Kaldi的运行。

1) 从github上Clone 仓库: git clone https://github.com/kaldi-asr/kaldi

2) 进入Kaldi目录后查看INSTALL文件,它会告诉你怎么安装Kaldi,需要注意的是 extras/check_dependencies.sh这里检查依赖,一定要确保满足相关要求,否则后面会很麻烦。编译的话根据自己的CPU数目选择make -j n

3) 下载语音预料,thchs30的语料是公开的,可以在http://www.openslr.org/18/ 里下载并解压。

4) Kaldi安装好后就可以进入到egs/thchs30/s5/目录了。首先加入你是本地运行的话,需要讲cmd.sh文件内的queue.pl替换为run.pl。然后再修改第11行的thchs= 的路径为你自己存放语音语料的路径,到此Kaldi程序就可以运行了。后面DNN那里可能会出一点问题,这个后面再说。

生成text wav.scp utt2pk spk2utt

负责此处的是run.sh中的第22行,即:

local/thchs-30_data_prep.sh $H $thchs/data_thchs30 || exit 1;

其中$H 是当前目录名,$thchs指的是语音语料库位置。这俩是thchs-30_data_prep.sh 脚本的参数。(这个local我一开始以为是bash里面的local命令,想了半天也没有想懂,后来才发现这货就一文件夹。。。)

该程序的答题执行流程是读取语料库中的{train, dev, test}文件夹下的.wav文件和.trn文件。利用wav文件的名字和所在路径生成wav.scp文件,利用wav.trn文件中的第1行和第3行生成word.txt 和phone.txt。这里的word和phone都是直接从文件中读取的。同时由于此处没有说话人识别,因此对于utt2spk(语段到说话人)和spk2utt(说话人到语段)里的内容都是两列相同的wav文件名。

提取MFCC特征

对应代码为:

steps/make_mfcc.sh --nj $n --cmd "$train_cmd" data/mfcc/$x exp/make_mfcc/$x mfcc/$x || exit 1;

其中$n是make的cpu线程数目,$train_cmd 是cmd.sh里的train_cmd变量,$x就是循环里的{train,dev,test}了。

关于梅尔倒谱系数(MFCC)我们之前讲过,在Kaldi里它本身设置了合理的默认值,同事保留了一部分用户最有可能想调整的选项,如梅尔滤波器的个数,最大和最小截止频率等等.它通常需要读取wav文件或.pcm文件,假如数据源不是wav文件,我们就得使用工具来转化,Kaldi中有的sph2pipe工具能满足一般的情况.

命令工具 compute-mfcc-feats用来计算MFCC特征,若直接运行不带参数的话就会给出一个参数列表.改程序需要两个参数,rspecifier是用来度.wav数据, wspecifier是用来写特征的(就是r 和w啦).典型的用法是,将数据写入一个大的”archive”文件,也写到一个”scp”文件以便随机读取.

MFCC的计算由Mfcc类型的对象完成,它有Compute()函数可以根据波形计算特征.一个完整的MFCC计算如下:

1) 计算出一个文件中帧的数目(通常帧长25ms,帧移10ms)

2) 对每一帧提取数据,可选做Dithering,预加重和去除直流偏移,还可以和加窗函数想成(此处支持多种选项,如Hanmming 窗).

3)计算该点能量(假如用对数能量则没有).

4) 做快速傅里叶变换(FFT)并计算功率谱.

5)计算每个梅尔滤波器的能量,如23个部分重叠的三角滤波器,其中心在梅尔频域等间距.

6) 计算对数能量病作宇轩变换,根据要求保留系数(如13个).

7) 选做倒谱变;它仅仅是比例变换,确保系数在合理范围内.

上下截止频率根据三角滤波器界定,由选项–low-freq和–high-freq控制,通常分别设置为0Hz和奈奎斯特频率附近,如对16kHz采样的语音设置为–low-freq=20 和 –high-freq=7800。

Kaldi的特征和HTK的特征在很多方面不同,但是几乎所有这些不同归结于有不同的默认值。用选项–htk-compat=true并正确设置参数,能得到同HTK非常接近的特征。一个可能重要的选项是我们不支持能量最大归一化。这是因为Kaldi希望能把无状态方式应用到归一化方法,且希望从原理上计算一帧帧特征仍能给出相同结果。但是程序compute-mfcc-feats里有–subtract-mean选项来提取特征的均值。对每个语音做此操作;每个说话人可以有不同的方式来提取特征均值。(如compute_cmvn_stats.sh,表示倒谱均值和方差归一化)。

倒谱均值方差归一化(CMVN)

代码为:

steps/compute_cmvn_stats.sh data/mfcc/$x exp/mfcc_cmvn/$x mfcc/$x || exit 1;

在实际情况下,受不同麦克风及音频通道的影响,会导致相同音素的特征差别比较大,通过CMVN可以得到均值为0,方差为1的标准特征。均值方差可以以一段语音为单位计算,但更好的是在一个较大的数据及上进行计算,这样识别效果会更加robustness。Kaldi中计算均值和方差的代码在compute-cmvn-stats.cc, 归一化在apply-cmvn.cc。

滤波器组(FilterBank, FBank)

人耳对声音频谱的响应是非线性的,经验表明:如果我们能够设计一种前端处理算法,以类似于人耳的方式对音频进行处理,可以提高语音识别的性能。FilterBank分析就是这样的一种算法。FBank特征提取要在预处理之后进行,这时语音已经分帧,我们需要逐帧提取FBank特征。

语音感知线性预测(Perceptual Linear Prediction,PLP)

PLP和MFCC一样都是一种声学特征,且它们的前期计算工作也是一样的。PLP在Kaldi的手册中没有找到,此处再用HTK手册中的叙述。As implemented in HTK the PLP feature extraction is based on the standard mel-frequency filterbank (possibly warped). The mel filterbank coefficients are weighted by an equal-loudness curve and then compressed by taking the cubic root. 5 From the resulting auditory spectrum LP coefficients are estimated which are then converted to cepstral coefficients in the normal way (see above).

特征级声道长度归一化(VTLN)

声道长度是唇到声门之间的距离.成年女性在13cm左右,成年男性在18cm左右.其语音的共振峰频率的变化可高达20~25%.而声道长度归一化的目的就是通过对特征参数空间进行变换,使得源说话人源说话人与目标说话人之间的声道长度不匹配的程度最小化,尤其是异性之间的声道长度差异.声道长度归一化通过折叠或平移语音信号的短时谱来改变声道的长度,令说话人的共振峰频率相匹配.

声道长度归一化通过在频域上对MFCC参数进行线性折叠实现.该方法是基于声道传输模型,假设声道为单一均匀的声道管:

其中,VTL为声道长度,c为声速;f1为第i阶的共振峰频率.声道长度与共振峰频率成反比,用线性折叠的方法来近似声道特征的转换.而线性折叠可以表示为:

$ f^{`} = \alpha f $$

其中f 和$f^{`}$为折叠后和折叠前的频率.$\alpha$为频率弯折因子.但由于信号频率带宽改变的问题,实际使用中则采用分段线性折叠的方法,也就是在不同的频率区间内使用不同的折叠因子.

Kaldi官网对VTLN的解释

程序compute-mfcc-feats和compute-plp-feats接收一个VTLN弯折因子选项。在目前的脚本中,这仅用作线性版的VTLN的初始化线性转换的一种方法。VTLN通过移动三角频率箱的中心频率的位置来实现。移动频率箱的弯折函数是一个在频域空间分段线性的函数。为理解它,记住以下数量关系:

0 <= low-freq <=vtln-low < vtln-high < high-freq <= nyquist

此处,low-freq和high-freq分别是用于标准MFCC或PLP计算的最低和最高频率(忽略更低和更高的频率)。vtln-low和vtln-high是用于VTLN的截止频率,它们的功能是确保所有梅尔滤波器有合适的宽度

Kaldi实现的VTLN弯折函数是一个分段线性函数,三个部分映射区间[low-freq,high-freq]至[low-freq, high-freq]。记弯折函数为W(f),f是频率。中段映射f到f/scale,scale是VTLN弯折因子(通常范围为0.8到1.2)。x轴上低段和中段的连接点是满足min(f,W(f)) = vtln-low的f点。x轴上中段和高端的连接点是满足max(f, W(f)) = vtln-high的f点。要求低段和高段的斜率和偏移是连续的且W(low-freq)=low-freq, W(high-freq)=high-freq。这个弯折函数和HTK的不同;HTK的版本中,”vtln-low”和”vtln-high”的数量关系是x轴上可以不连续的点,这意味着变量”vtln-high”必须基于弯折因子的可能范围的先验知识谨慎选择(否则梅尔滤波器可能为空)。

一个合理的设置如下(以16kHz采样的语音为例);注意这反映的是我们理解的合理值,并非任何非常细致的调试实验的结果。

low-freq vtln-low vtln-high high-freq Nyquist
40 60 7200 7800 8000

Hanmming窗

前面说过,我们要处理的语音信号一般在10ms到30ms之间,我们可以把它看作是平稳的。为了处理语音信号,我们要对语音信号进行分段,即每次仅处理一段数据。

那怎么能够获取一段数据呢?一种方式就是构造一个函数。这个函数在某一区间有非零值,而在其余区间皆为0.汉明窗就是这样的一种函数。它主要部分的形状像sin(x)在0到pi区间的形状,而其余部分都是0.这样的函数乘上其他任何一个函数f,f只有一部分有非零值。

为什么汉明窗这样取呢?因为之后我们会对汉明窗中的数据进行FFT,它假设一个窗内的信号是代表一个周期的信号。(也就是说窗的左端和右端应该大致能连在一起)而通常一小段音频数据没有明显的周期性,加上汉明窗后,数据形状就有点周期的感觉了。

因为加上汉明窗,只有中间的数据体现出来了,两边的数据信息丢失了,所以等会移窗的时候,只会移1/3或1/2窗,这样被前一帧或二帧丢失的数据又重新得到了体现。

简单的说汉明窗就是个函数,它的形状像窗,所以类似的函数都叫做窗函数。

Ref

Kaldi Feature extraction 利用声道归一化提高语音转换效果的方法