简介
Kaldi 目前支持多种特征和模型空间的变换与映射。特征空间变换和映射通过工具来处理(本质上就是矩阵),以下章节将介绍:
- 全局线性或仿射变换
- 说话人无关或单一说话人(per-speaker)或者(per-utterance)单一句子自适应
- 句子-说话人(Utterance-to-speaker)和说话人-句子(speaker-to-utterance)映射
- 组合变换
- 估计变换时的静音权重
变换,投影或者其他没有特定说话人的特征操作包括:
- 线性判别性分析(LDA)
- 帧拼接和差分特征计算
- 异方差的线性判别性分析(HLDA)
- 全局半协方差/最大似然线性变换
全局变换主要通过说话人自适应的方式应用:
- 全局GMLLR/fMLLR变换
- 线性声道长度归一化
- 指数变换
- 谱均值和方差归一化
下面我们将介绍回归树以及用到回归树的变换:
- 为自适应构建回归类树
全局线性或仿射特征变换
Matrix<BaseFloat>The Tableconcept)中。
变换可以通过程序transform-feats应用于特征上,语法如下
Specifying Tableformats: wspecifiers and rspecifiersExtendedfilenames: rxfilenames and wxfilenames).这个程序通常用于管道的一部分。 这个程序将根据矩阵的列数与特征维数相等还是比维数多1来确定变换是线性或者仿射变换,一个典型的例子是:
这里文件0.mat包括一个矩阵。一个说话人相关的变换的例子是:
说话人无关vs 说话人 vs 句子
估计变换的程序通常指定来做某种指定的自适应。例如说话人无关或者(指定说话人或者句子)。例如LDA和MLLT/STC变换是说话人无关的,fMLLR变换是指定说话人或者句子的。估计指定说话人或者句子的变换的程序默认情况下将以每句每句的模式工作,但是如果提供了-spk2utt选项的话,将按照每个无关说话人的模式。
Extended filenames: rxfilenames and wxfilenamesSpecifying Table formats: wspecifiers and rspecifiers
句子到说话人 和 说话人到句子 映射
...
这些字符串只是例子,代表通常的说话人和句子的id.同样将会有一个名为spk2utt的文件存在:
spk1 spk1utt1 spk1utt2spk2 spk2utt1 spk2utt2
...
接收spk2utt 选项的程序通常会遍历spk2utt文件中的每个说话人id,并为每个speaker-id遍历所有句子,累积每个句子的统计信息。对特征的访问将采用随机访问模式而不是通常的序列模式,这里需要小心设置,因为特征文件非常大,通常完整处理的特征需要从文件读取,这并不允许最节省内存的随机访问,除非仔细设置。Avoiding memory bloat when reading archives in random-access mode为了避免在这种情况下访问特征文件引起内存膨胀,建议为了确保所有文件按utterance-id排序,提供给-spk2utt选项的文件里的句子应该顺序出现,而且对于指定程序的特征输入的rspecifiers需要给出适当的选项(如"ark,s,cs:-",如果这是标准输入)。
组合变换
另一个接收通用变换的程序是compose-transforms。通常的语法是“compose-transformsa b c”,它将执行乘法操作c= a*b,(当然如果a是一个仿射矩阵,将不止是乘法操作)。该脚本的另一个变形是下面的形式:
some-program some-args "$feats" ...
这个例子也显示了从一个程序调用两层命令。这里0.mat是一个全局变换(例如LDA),而1.tans是以句子id为索引的fMLLR/CMLLR矩阵集合。compose-transforms程序将把多个变换组合起来。通过下列方式这些特征可以更容易计算,但是效率较低:
估计变换时的静音加权
Building regression trees for adaptationstate-levelposteriorsGlobal CMLLR/fMLLR transforms):
ali-to-post ark:$srcdir/test.ali ark:- | \
在这里,shell变量“silphones”将被设置为以冒号分隔的、静音音子的整数id列表。
线性判别分析变换
Kaldi 通过类LdaEstimate支持线性判别分析变换(LDA)估计。这个类不需要与任意特殊种类的模型交互,它需要用类的数目进行初始化,累积函数声明如下
class LdaEstimate {};
帧拼接
LDA之前通常会对原始MFCC特征先做帧拼接(例如将连续9帧拼接起来),splice-feats程序用来做这个工作。脚本里面典型的一行如下:
feats="ark:splice-feats scp:data/train.scp ark:- |
SpecifyingTable formats: wspecifiers and rspecifiers
差分特征计算
feats="ark:add-deltas --print-args=false scp:data/train.scp ark:- |"
异方差线性区分性分析(HLDA)
HLDA是一种降低维度的线性特征映射技术,它利用最大似然准则,其中被拒绝的维度利用一个全局的均值和方差来建模,接受的维度利用一个特殊模型来建模,该模型的均值和方差是通过最大似然准则估计得到。目前已经集成到工具里的HLDA的形式是在HldaAccsDiagGmm
同样,对于程序来说累积K维模型的统计信息也很方便。所以在HLDA累积过程中,我们累积足够估计K维均值的统计信息,对于接受的维度,我们累积下列信息而不是G
整个HLDA的估计过程如下 (seerm_recipe_2/scripts/train_tri2j.sh):
- 首先利用LDA进行初始化 (我们将同时存储全矩阵和维度下降的矩阵).
- 开始模型创建和训练过程。在某些我们已经决定做HLDA更新的迭代上(非连续)进行如下操作:
- 累积HLDA信息 (S, 以及全维度的均值统计信息). 做这些统计的程序(gmm-acc-hlda) 需要利用模型,未转化的特征,当前转换(用来对特征进行变换,为了计算高斯后验概率)
- 更新HLDA变换。执行该操作的程序 (gmm-est-hlda) 需要模型,统计量,和之前的全转换矩阵,来启动优化和正确地报告辅助函数的变化。它输出新变换(全维度和降低后的维度),以及新估计和转换均值后的模型。
全局半绑定协方差(STC)/最大似然线性变换估计(MLLT)
GlobalSTC/MLLT is a feature-transformation matrix. 全局STC/MLLT是平方特征变换矩阵。详情见"Semi-tiedCovariance Matrices for Hidden Markov Models", by Mark Gales, IEEETransactions on Speech and Audio Processing, vol. 7, 1999, pages 272-281.将它看成特征空间转化,目标函数是给定模型后转换后特征的平均帧似然度加上变换的对数行列式。模型的均值也在更新阶段被变换旋转了。对于维度充分统计量如下,其中D 是特征维度:
见参考文献, Equations (22) and (23) 更新的公式。这基本是一个简单形式的对角线逐行的受限MLLR/fMLLR更新方程,其中二次方程的一阶项消失了。注意我们的实现与参考文献不同在于我们用了矩阵逆的一列而不是辅助因子,因为乘以行列式并不会对结果造成不同而且可能会引起浮点下溢出或者上溢出的潜在问题。
我们描述整个过程类似我们在LDA特征上做MLLT。但是它同样在传统的差分特征上适用。参考脚本里的例子rm_recipe_2/steps/train_tri2f:
- 开始一个正常的模型训练过程,始终利用被转换过的特征. 在某些指定的迭代(更新MLLT矩阵)做如下操作:
- 在当前全变换的空间累积MLLT统计信息(例如在转化后的特征上).为了效率,我们利用训练数据的子集来做这件事。
- 变换模型均值通过设置.
MLLT估计相关的程序是gmm-acc-mllt和est-mllt。同样也需要程序gmm-transform-means利用转化高斯均值,以及组合变换做乘法
全局CMLLR/fMLLR变换
充分统计量如下存储:
我们的估计方法是标准方法,见附录B (in particular sectionB.1, "Direct method over rows")。不同的是我们用矩阵逆的一列而不是辅助因子,也就是忽略行列式因为它并不影响结果而且可能引起上下溢出的危险。
全局约束MLLR(CMLLR) 的估计是通过类FmllrDiagGmmAccs, 和程序gmm-est-fmllr (同样可参考gmm-est-fmllr-gpost). gmm-est-fmllr的语法如下:
gmm-est-fmllr [options] <model-in> <feature-rspecifier> \
下面是脚本的节选(rm_recipe_2/steps/decode_tri_fmllr.sh),它会利用前一轮没有自适应的解码产生的对齐来估计和应用CMLLR变换。上一轮解码假设是用的相同的模型,否则我们需要利用程序"convert-ali"来转化对齐。
...silphones=48 # colon-separated list with one phone-id in it.mincount=500 # min-count to estimate an fMLLR transformsifeats="ark:add-deltas --print-args=false scp:data/test.scp ark:- |"# The next comand computes the fMLLR transforms.ali-to-post ark:$srcdir/test.ali ark:- | \feats="ark:add-deltas --print-args=false scp:data/test.scp ark:- |# The next command decodes the data.gmm-decode-faster --beam=30.0 --acoustic-scale=0.08333 \"$feats" ark,t:$dir/test.tra ark,t:$dir/test.ali 2>$dir/decode.log
线性VTLN (LVTLN)
最近几年,有大量的论文描述VTLN的实现,事实证明一个线性特征变换与每个VTLN扭曲因子对应。例如, ``UsingVTLN for broadcast news transcription'', by D. Y. Kim, S. Umesh, M. J. F.Gales, T. Hain and P. C. Woodland, ICSLP 2004.
LinearVtln实现了一种方法,程序有gmm-init-lvtln,gmm-train-lvtln-special, 和gmm-est-lvtln-trans。LinearVtln
其中例如=31, 对应31个不同的扭曲因子. 下面将描述我们是怎么获得这些矩阵的。指定说话人的变换是通过下列方法估计的。首先,我们需要某种模型以及其对应的对齐,在示例脚本中,我们通常用单音子模型或者全三音子模型。利用这个模型及其对齐,以及原始的弯转特征,我们计算出估计传统CMLLR的统计信息。计算VTLN变换时,我们利用矩阵,计算偏移向量,来为变换最大化CMLLR辅助函数。最大化辅助函数值(i.e. 在不同 i上)的就成为该说话人的变换. 由于我们在估计一个均值偏移,我们基本上是通过将一种基于模型的谱均值归一化(或者一种只有偏移的CMLLR形式)和以线性变换来实现的VTLN结合起来,这样就避免需要去单独实现均值归一化。
参考文献:https://blog.csdn.net/shichaog/article/details/78441304