本篇接着kaldi中ivector的提取【一】继续分析ivector的在线提取方法。
特征设计
在feed模型之前,前端的特征处理操作包括如下:
- 基本的特征提取(PLP,MFCC,FBANK)
- 拼帧
- Delta
- 线性变换(LDA,PCA)
- 特征融合(MFCC + Pitch)
- 归一化(CMVN)
在线状态下,由于上述操作必须online的进行,kaldi将上述操作封装成下面几个类,定义在online-feature.h
中
1 | // 模板类,提取PLP,MFCC,FBANK特征 |
以上特征类继承OnlineFeatureInterface
通过GetFrame(int32, VectorBase<BaseFloat>*)
获取特征输出,在构造函数中指定输入源。
比如,在ivector提取中,UBM模型的输入特征需要经过online-CMVN+Splice+LDA,那么最终的特征构造如下:
1 | // base表示基本声学特征PLP/MFCC/FBANK |
可以这么理解这种设计,将OnlineFeatureInterface
的基类理解为一个节点,那么制定每个节点之间的输入输出关系,输入节点为原始音频采样数据,调用输出节点的GetFrame()
函数即可获得最终的特征,计算逻辑在节点的内部实现。
在ivector-extract-online2.cc
中,实现ivector提取的类OnlineIvectorFeature
也是OnlineFeatureInterface
的基类。调用GetFrame(int32, VectorBase<BaseFloat>*)
即可获得截止当前帧的估计ivector。
在ivector提取的过程中,需要用到两种特征
- Splice + LDA:作为ivector的零阶统计量
- CMVN + Splice + LDA:作为UBM的输入,获取后验概率
在OnlineIvectorFeature
中,用成员lda_
和lda_normalized_
表示。
在线估计
OnlineIvectorFeature
类内部使用num_frames_states_
来追踪上次估计的时间戳,每一次估计首先遍历新增的帧,更新统计量,并以一定的周期提取ivector。这个周期是可以设置的,代码逻辑如下:
1 | for (; num_frames_stats_ <= frame; num_frames_stats_++) { |
更新的统计量包括通过UBM获取的后验和Splice+LDA的特征。GetIvector
函数内部使用共轭梯度法计算ivector,(没有直接计算ivector)主要考虑这么做可以减少计算耗时。
OnlineIvectorEstimationStats
类中具体实现ivector的在线估计算法,核心函数是累积统计量的AccStats(IvectorExtractor, VectorBase<BaseFloat>, std::vector<std::pair<int32, BaseFloat> >)
和ivector估计函数GetIvector(int32, VectorBase<double>*)
。
首先看一下ivector的计算:
1 | void OnlineIvectorEstimationStats::GetIvector( |
根据注释部分,ivector提取方式为:
$\mathbf{Q}$(quadratic_term_
),$\mathbf{L}$(linear_term_
)在AccStats
中完成估计。
1 | void OnlineIvectorEstimationStats::AccStats( |
离线方法中,$\mathbf{L},\mathbf{Q}$的计算如下:
在线方法中,一帧一帧的以累加形式不算修正:
$\mathbf{Q}$被初始化为$\mathbf{I}$。$\mathbf{x}$表示当前输入特征(feature_dbl
),$p_c$表示以$\mathbf{x}$为输入的情况下,UBM第$c$个component的后验。$\mathbf{B}_c$和$\mathbf{U}_c$为ivector提取器中的Sigma_inv_M_[g]
和U_g
。
结合对OnlineIvectorEstimationStats::GetIvector
的分析,式子$(1)$可以写成:
综上所述,考虑到在线方法中的ivector输出周期,因此,一个句子($T$帧)作为输入往往可以得到$N$个ivector($N < T$)。
共轭梯度法
考虑到效率问题,每次计算$\mathbf{w}$的时候都需要对矩阵求逆,共轭梯度法在这里起的作用是在不进行矩阵求逆的操作下求出$\mathbf{Q}\mathbf{w} = \mathbf{L}$的解$\mathbf{w}$,即线性方程组求解问题。
使用共轭梯度法求解线性方程组的思想是将$\mathbf{w}$看成方程:
的驻点。此时$\nabla_w f(\mathbf{w}) = \mathbf{Q}\mathbf{w} - \mathbf{L} = 0$。
定义$\mathbf{d}_1, \mathbf{d}_2$,若满足$\mathbf{d}_1^\top \mathbf{Q} \mathbf{d}_2 = 0$,那么称$\mathbf{d}_1, \mathbf{d}_2$关于$\mathbf{Q}$共轭。令$\mathbf{d}_k$表示迭代第$k$次的搜索方向,那么第$k + 1$次的搜索方向$\mathbf{d}_{k + 1}$为:
$\beta_{k}$使得$\mathbf{d}_{k + 1}$和$\mathbf{d}_k$关于$\mathbf{Q}$共轭。它的计算可以通过FR和PR算法得到,kaldi中使用的FR算法如下:
在第$k$步的搜索步长$\alpha_k$使得解从$\mathbf{w}_k$迁移到$\mathbf{w}_{k + 1}$:
其中$\alpha_k$计算如下:
共轭梯度法初始化$\mathbf{w}_0$为之前估计的ivector:
之后反复的计算$\mathbf{w}_k,\mathbf{g}_k,\mathbf{d}_k$,直到$\mathbf{g}_k = \mathbf{0}$的时候,$\mathbf{w}_k$即为所求的ivector。