Kaldi中MFCC计算源码剖析(一)

Summary

在剖析源码前,首先借助kaldi官方手册中对mfcc计算步骤的总结来梳理下整个计算流程框架(该部分对应手册地址:http://www.kaldi-asr.org/doc/feat.html)。之后所有脚本代码都是围绕这个流程展开。

kaldi官方手册中对计算mfcc步骤的总结

本文侧重分析kaldi计算mfcc的总体流程,详细说明了计算的步骤、每步所用函数的名字及其所在文件的路径、每个涉及到的函数功能和源码分析。但有些值得重点分析和学习的函数源码没有在本文中做详细介绍,该部分内容会在《kaldi中MFCC计算源码剖析(二)》中体现。

提醒:下文中为简便说明,使用到了一些文件的行数做指引,但kaldi历经了多年的变迁,有些文件会有变动,本文所参考的版本是https://github.com/kaldi-asr/kaldi.git的master分支,拉取的时间是2020年3月。


        一般地,在kaldi样例工程(egs)中涉及计算mfcc的脚本代码通常包含以下两个步骤:steps/make_mfcc_pitch.sh; steps/compute_cmvn_stats.sh。其中第二步主要是对第一步计算得到的mfcc特征做均值方差归一化,所以重点是make_mfcc_pitch.sh脚本。而分析这个脚本会发现,mfcc计算的核心是使用了compute-mfcc-feats指令,因此,接下来跟进到src/featbin/compute-mfcc-feats.cc文件看看流程。

        这个c文件里只有一个main函数,所以结构很清晰。开始是对一些mfcc计算使用到的参数进行赋值以及用从用户端传入的值去覆盖相应参数(感觉使用方法与python的parse.add_argument类似);然后定义了Mfcc类的对象mfcc用于后续计算(79行);接下来是一些读写类对象的定义,用于读音频数据和写计算得到的特征数据;之后进入最主要的大循环(106行),截止条件是音频数据全部读完。对于每条音频,经过:根据时长决定取舍、校验通道数、VTLN归一化判断(关于声道长度归一化的细节和原理请参考:http://fancyerii.github.io/kaldidoc/feature/ 讲解的很清楚)一系列操作后,开始计算mfcc(148行)。那么接下来我们重点关注mfcc.ComputeFeatures这个函数。在compute-mfcc-feats.cc这个文件中,mfcc.ComputeFeatures后面基本没有实质性的算法内容了,都是不难理解的东西。

        mfcc.ComputeFeatures从调用形式上看,ComputeFeatures函数是mfcc的一个成员函数,所以找到Mfcc类的定义处,在文件src/feat/feature-mfcc.h中第147行:typedef OfflineFeatureTpl<MfccComputer> Mfcc;。可见,Mfcc只是个宏定义,其真正的类是OfflineFeatureTpl,其定义在文件src/feat/feature-common.h中第111行,其成员函数ComputeFeatures和Compute的实现是在文件src/feat/feature-common-inl.h中。首先看ComputeFeatures这个函数:核心是最后的调用Compute,前面主要是判断采样率的匹配情况,如果采样率不等于16KHz则先进行重采样再调用Compute(重采样是调用了如下图中的ResampleWaveform,其定义在src/feat/resample.h中。此处需注意,旧版本的kaldi中是只做下采样的,不做上采样,即采样率低于预设值时直接报错)。

OfflineFeatureTpl::ComputeFeature函数部分内容

而Compute函数的实现中,除了前部分的获取数据行列信息,定义输出变量维度,定义是否使用对数能量的标识符,定义窗变量等,核心有两个:其一、ExtractWindow,其作用是取出窗长大小的数据(该数据已经做了一定的处理,详见其定义处src/feat/feature-window.h,其实现处src/feat/feature-window.cc),具体内容可参考该函数的注释:ExtractWindow extracts a windowed frame of waveform with a power-of-two, padded size.  It does mean subtraction, pre-emphasis and dithering as requested;其二、computer_.Compute函数。computer_是OfflineFeatureTpl类中用模版定义的一个量(src/feat/feature-common.h中),调用的Compute是在src/feat/feature-mfcc.cc中实现的,这个函数也是mfcc计算全流程的最后一个正经的函数了。

        下面重点跟进下MfccComputer::Compute这个函数的实现细节。首先定义一个mel滤波器组const MelBanks &mel_banks = *(GetMelBanks(vtln_warp)); 相关函数的声明和实现分别在src/feat/mel-computations.h和src/feat/mel-computations.cc中,并使用srfft_->Compute或RealFft对信号做傅立叶变换;然后使用ComputePowerSpectrum函数求得功率谱,并从计算结果中取出0~signal_frame->Dim() / 2+1的数据(这是因为傅氏变换的结果是对称的)存在power_spectrum变量中;接下来将power_spectrum过mel滤波器组得到mel_energies_,调用的是函数mel_banks.Compute,函数ApplyFloor是将mel_energies_若有0值加上一个epsilon小常值,这样施加取对数运算ApplyLog就不会有定义域错误的问题了;最后对盛放最后返回结果的变量feature做初始化feature->SetZero()并开始对其赋值feature->AddMatVec(1.0, dct_matrix_, kNoTrans, mel_energies_, 0.0)。实际上这步中最后返回的结果是mel_energies_与dct系数的乘积。
        此时对比文章开头Summary中提供的官方手册总结步骤,我们对每步对应的函数做个映射。

1、Extract the data, do optional dithering, preemphasis and dc offset removal, and multiply it by a windowing function (various options are supported here, e.g. Hamming) 对应ExtractWindow;

2、Work out the energy at this point (if using log-energy not C0).也是对应ExtractWindow;

3、Do FFT and compute the power spectrum 对应的srfft->Compute/RealFft和ComputePowerSpectrum;

4、Compute the energy in each mel bin; these are e.g. 23 triangular overlapping bins whose centers are equally spaced in the mel-frequency domain.对应mel_banks.Compute;

5、Compute the log of the energies and take the cosine transform, keeping as many coefficients as specified (e.g. 13)对应的ApplyLog和feature->AddMatVec(***);

6、Optionally do cepstral liftering; this is just a scaling of the coefficients, which ensures they have a reasonable range.

        上述第6步对应的是脚本src/feat/feature-mfcc.cc中第61行if (opts_.cepstral_lifter != 0.0) 和第64行if(opts_.use_energy) 。

        针对MfccComputer::Compute函数主体计算流程介绍完毕了,但其中的几个关键被调函数还是很值得进一步详细阅读的,包括:1、计算mel滤波器组:MelBanks &mel_banks = *(GetMelBanks(vtln_warp)); 2、fft后的信号计算功率谱:ComputePowerSpectrum;3、功率谱过mel滤波器组:mel_banks.Compute;4、dct_matrix_的计算。这四部分函数源码将在下一篇中分析。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容