Kaldi特征提取之-MFCC
背景
- 上一节介绍的FBank特征已经很贴近人耳的响应特性,但是仍有一些不足:FBank特征相邻的特征高度相关(相邻滤波器组有重叠),因此当我们用HMM对音素建模的时候,几乎总需要首先进行倒谱转换,通过这样得到MFCC特征。
提取MFCC特征
MFCC特征的提取是在FBank特征的基础上再进行离散余弦变换, 因此前面几步和FBank一样,具体请参考上一节.
- 傅里叶变换
- 计算能量谱
- Mel滤波
- 取Log
-
- 假设做完取Log之后,我们得到N维的特征向量
Mlog
。离散余弦变换公式如下:
Ci=2N−−−√∑j=1Nmj∗cos(pi∗iN∗(j−0.5)),∀i∈[1,M]N是取Log之后的特征维度,M 是 DCT(离散余弦变换)之后的特征维度。 - 关于离散余弦变换,可以用matlab脚本看一下效果。
- 关于离散余弦的实现。从计算公式可以看出DCT的时间复杂度为O(N*M),如果每次都重新计算DCT table无疑是得不偿失的,Kaldi中的做法是首先根据N和M的值计算出整个table,之后调用blas线性代数库,可以加快计算速度。相比之下,Htk中每次都要重新计算cos函数就显得性能比较低。以下是Htk和Kaldi的代码。
// Htk implement void FBank2MFCC(Vector fbank, Vector c, int n) { int j,k,numChan; float mfnorm,pi_factor,x; numChan = VectorSize(fbank); mfnorm = sqrt(2.0/(float)numChan); pi_factor = PI/(float)numChan; for (j=1; j<=n; j++) { c[j] = 0.0; x = (float)j * pi_factor; for (k=1; k<=numChan; k++) c[j] += fbank[k] * cos(x*(k-0.5)); c[j] *= mfnorm; } } // Kaldi implement template<typename Real> void ComputeDctMatrix(Matrix<Real> *M) { //KALDI_ASSERT(M->NumRows() == M->NumCols()); MatrixIndexT K = M->NumRows(); MatrixIndexT N = M->NumCols(); KALDI_ASSERT(K > 0); KALDI_ASSERT(N > 0); Real normalizer = std::sqrt(1.0 / static_cast<Real>(N)); // normalizer for // X_0. for (MatrixIndexT j = 0; j < N; j++) (*M)(0, j) = normalizer; normalizer = std::sqrt(2.0 / static_cast<Real>(N)); // normalizer for other // elements. for (MatrixIndexT k = 1; k < K; k++) for (MatrixIndexT n = 0; n < N; n++) (*M)(k, n) = normalizer * std::cos( static_cast<double>(M_PI)/N * (n + 0.5) * k ); } // 底层调用的blas库函数 void VectorBase<Real>::AddMatVec(const Real alpha, const MatrixBase<Real> &M, MatrixTransposeType trans, const VectorBase<Real> &v, const Real beta) { KALDI_ASSERT((trans == kNoTrans && M.NumCols() == v.dim_ && M.NumRows() == dim_) || (trans == kTrans && M.NumRows() == v.dim_ && M.NumCols() == dim_)); KALDI_ASSERT(&v != this); cblas_Xgemv(trans, M.NumRows(), M.NumCols(), alpha, M.Data(), M.Stride(), v.Data(), 1, beta, data_, 1); }
- 假设做完取Log之后,我们得到N维的特征向量
Mlog
。离散余弦变换公式如下:
均值方差归一化(CMVN)
- 实际情况下,受不同麦克风及音频通道的影响,会导致相同音素的特征差别比较大,通过CMVN可以得到均值为0,方差为1的标准特征。均值方差可以以一段语音为单位计算,但更好的是在一个较大的数据及上进行计算,这样识别效果会更加robustness。Kaldi中计算均值和方差的代码在compute-cmvn-stats.cc, 归一化在apply-cmvn.cc。
FBank与MFCC对比
- 计算量,因为MFCC是在FBank的基础上进行的,所以MFCC的计算量更大
- 特征区分度,FBank特征相关性较高,MFCC具有更好的判别度,这也是在大多数语音识别论文中用的是MFCC,而不是FBank