Kaldi中MFCC计算源码剖析(二)
在上一篇文章中提到以下四块内容将在本文进行进一步剖析:1、计算mel滤波器组:MelBanks &mel_banks = *(GetMelBanks(vtln_warp)); 2、fft后的信号计算功率谱:ComputePowerSpectrum;3、功率谱过mel滤波器组:mel_banks.Compute;4、dct_matrix_的计算。
一、GetMelBanks
该函数在src/feat/feature-mfcc.cc中定义。对于给定low_freq和high_freq确定的频率区间后,mel滤波器组系数是固定的。该函数主要就是在计算该系数。
mel滤波器组是由一些列三角滤波器构成的,用于对功率谱做滤波。以40个三角滤波器构成mel滤波器组为例,若fft点数为512点,则滤波器系数矩阵的维度为40*257。其中每行绝大部分的值均为0,只有在该三角滤波器对应的范围内其值不为0。对于257个点的输入信号,分别过40个三角滤波器并计算该257点信号经过每个滤波器后所得的能量,最后我们会得到40个能量值(对应到本文第三部分)。
mel滤波器组滤波过程示意图(引用自http://fancyerii.github.io/books/mfcc/)图(a)是26个滤波器;图(b)是滤波后的信号;图(c)是其中的第8个滤波器,它只让某一频率范围的信号通过;图(d)通过它的信号的能量;图(e)是第20个滤波器;图(f)是通过它的信号的能量(该段文字与上图引自相同出处)。
mel_banks的计算过程如下:
1、对于给定频率范围[low_freq, high_freq],计算两者对应的mel尺度频率。
low_freq和high_freq一般是给定超参,通常low_freq会根据实际问题领域被人为赋值,而high_freq则通常取默认的。这是因为,根据采样定理,信号的最大有效频率就是采样率的一半。而mel尺度频率和线性尺度频率的转换关系如图(定义在src/feat/mel-computations.h中)
mel频率与线性频率转换公式2、等距划分mel尺度空间。
对于40个三角滤波器而言,需要把[mel(low_freq), mel(high_freq)]范围等分地取42个点(可自行画图理解,例如三个三角滤波器会占有5个点)。
3、将该42个mel频点值换算回线性频率值。
此时相邻点的差值将不相等。
4、将这些频率对应到最接近的FFT的频率bin里。
首先理解FFT的频率bin,详细内容可翻阅信号处理书籍,简单来说就是频率轴的分辨率,数值上等于采样率/fft点数。例如对采样率为16kHz的语音每帧做512点的FFT,那么一个频率bin=16000/512=31.25Hz。实际上我们得到的频谱是取一半,相当于用257个点表征8kHz的频率范围(在这个问题中频率范围是由第1步中的low_freq和high_freq定义的,假设low和high分别为0Hz和8kHz):即bin[0]=0Hz, bin[1]=31.25Hz, bin[2]=62.5Hz,...., bin[256]=8kHz。
有了上述先验知识后,将第3步的42个mel频值换算来的线性频值映射到最接近的FFT频率bin中,其实就是除以31.25并向下取整的过程:。
5、计算mel滤波器组的系数矩阵。
对于40个三角滤波器组、fft点数为512点的例子而言,因为该矩阵维度是40*257的,所以就是循环地计算40个三角滤波器的系数,且每个三角滤波器的系数对应257维。在第一节开头已经提到,每个三角滤波器的系数虽然是257维,但大部分值都是0,只有“三角形”光顾的那些点有对应的值。因此我们就是在做这么一件简单的数学运算:在x-y平面上已知了一个底边在x轴上的三角形的左右点x坐标,以及峰点对应的x坐标,且高恒定为1,求三角形落在x轴上的边所包含的各个点对应的y值。下面给出伪代码:
三角滤波器系数计算伪代码二、ComputePowerSpectrum
该函数在src/feat/feature-functions.cc中定义。实际上就是在求fft结果的模方。根据功率谱的定义:短时功率谱与短时傅里叶变换的关系为:
关于最后结果只取了N/2+1点的信号学基础可参考该文 。
ComputePowerSpectrum函数定义三、mel_banks.Compute
该函数在src/feat/mel-computations.cc中定义。该函数核心是在计算向量相乘(即下图中BaseFloat energy的计算)以得到每个bin的能量。
mel_banks.Compute函数定义四、dct_matrix_
计算DCT变换系数。这是MfccComputer类的一个私有成员变量,该类的构造函数如下:
MfccComputer构造函数(所在文件为src/feat/feature-mfcc.cc)由上图代码段可知,计算dct系数矩阵主要依靠函数ComputeDctMatrix,该函数声明如下图:
计算DCT矩阵函数定义(所在文件为src/matrix/matrix-function.h)注释中给出了计算公式:;。关于DCT的讲解可参考维基百科-DCT 。该函数的实现是在src/matrix/matrix-function.cc中,如下图:
ComputeDctMatrix函数实现(所在文件为 src/matrix/matrix-function.cc )