Feature Extraction for ASR: Pitch

Pitch即音调、音高,是人类对声音的一种感知,想必大家都还记得中学物理中讲过的声音的三个特性:响度、音调、音色。所以Pitch在语音识别中是个很重要的特征。Pitch可以量化为频率,称为基本频率(F0)。字词中音调及音调的变化构成了像中文这样音调语言的语调,所以Pitch特征在音调类语言识别中更为重要。

Pitch特征提取就是计算声音基频(F0)的过程,有很多的方法可以获取声音的基频,本文将要介绍的是Kaldi语音识别工具包中的Pitch特征提取,Kaldi中提取Pitch的方法是一种基于时域的方法,论文(Ghahremani et al., 2014)发表于2014年的ICASSP会议,该方法源自发表于1995年的RAPT(David Talkin, 1995)。其他一些提取Pitch的方法可以在Kaldi的论文中找到,比如Yin、SAcC、SWIPE、YAAPT等,作者开门见山说我们找了一下现在有哪些方法可以提取Pitch,一一做了实验后发现Getf0这个方法最靠谱,于是打算改一下,不得不感叹大佬们写文章就是这么任性。

本文将详细介绍RAPT算法的主要思想,以及Kaldi对RAPT的一些修改,最后尝试对Kaldi的源代码做个简单解读。

Mathematics

介绍RAPT算法之前先复习一下相关的数学基础,自相关和互相关函数。

Cross-correlation and Autocorrelation

在信号处理中,互相关一般是用来度量两个向量之间的相似度,它是一个向量相对与另一个向量的偏移的函数,所以也叫做滑动内积,常用来在一个长的信号中搜索一些短片段特征(可以简单的理解为搜索substr的操作),关于互相关的详细内容可以参考wikipedia
对于连续函数f和g,互相关定义如下:

而对于离散函数则有:

这里m表示滞后(lag)即偏移,看到这两个公式是不是觉得跟计算卷积的公式很像,他们的确本质上差不多,只是差了个符号而已,卷积公式的定义如下:

下面这张图展示了卷积,自相关和互相关之间的异同,自相关定义为函数与自身的互相关。

可以看到自相关函数在波形重合的地方会出现峰值,这个特性使得他可以用来发现序列中的重复模式(即周期),例如在lag为0是(完全重合)时有一个峰值。对于周期信号而言,自相关函数的峰值会出现在函数周期或者函数周期整数倍的地方,下面这个gif可以直观体现这一特性。所以利用自相关函数可以用来在时域上获取基频(f0)的值,这就算RAPT 算法的数学基础。

Normalized Cross-Correlation Function

在实际提取特征是会对语音信号进行分帧,在帧这样一个级别的粒度计算,我们假设一帧有N个采样点,那么自相关(ACF)可以这样计算:

互相关(CCF)这样计算:

其中 表示这一帧起始的帧索引,这两个计算公式看起来非常相像,只是使用的数据略有不同,自相关函数计算时将超出本帧的部分都做了补零,而互相关函数计算时则使用了下一帧的数据,所以实际上计算自相关和互相关用了不同的数据,自相关时只用到了 范围的数据,而互相关则用得多一些,为

从计算中可以看出,ACF的问题是随着偏移的增大,计算的序列就越短,显然不同的偏移的数据是不可比较的,而CCF的计算同样存在问题,由于使用到了下一帧的数据,那么偏移后的数据就有可能变得很大或者很小(比如当前帧在静音与话音的交接处),得出的数据依然不可比较。为此必须使用归一划使得不同的偏移统一成可比较的数值,这就是 Normalized Cross-Correlation Function(NCCF)。

其中 是信号在 范围的能量,定义为:

这样NCCF的值域在 之间, 而且当偏移为周期或者周期的整数倍时, 值越接近1.0。

RAPT

Main idea

我们现在可以使用NCCF来计算F0了,然而计算NCCF是很耗时的操作,为了减少计算量,RAPT运用了两阶段来计算NCCF,主要的步骤如下:

  • 对音频进行下采样;
  • 对下采样后的音频计算NCCF, 并记录下局部最大值的位置;
  • 对原音频数据在上述局部最大值位置的附近计算NCCF,一般来说没一帧都会有好几个候选点;
  • 使用动态规划算法来获取每一帧的最佳候选点。

前三点和上面介绍的数学基础并无不同,之是使用了些技巧来减小计算量,而后面的动态规划算法是RAPT的核心部分,下面将着重讲解。

DP recursion

由于做了分帧,每一个片段都比较短,每一帧不能单纯以NCCF最大值的位置作为F0,而且RAPT是个tracking的算法,目的是要计算F0及跟踪F0的变化,计算中使用了类似维特比算法的动态规划来搜索最佳候选点,下面一边讲解细节,一边来理解为什么要这么做。
动态规划算法主要有三个重要的值:全局cost表示到当前步骤为止的代价;局部cost表示当前节点本身的代价;转移cost表示从上一个步骤转到当前步骤的代价。现在将这三个值套到求F0这个算法里面去,定义第i帧第j个候选点的全局cost为 , 局部cost为 , 从第i-1帧的第k个候选点转移到第i帧的第j个候选点的cost为 。他们之间的转移关系如下所示:

初始条件为:

其中,表示每一帧候选点的个数,亦即使得NCCF达到局部最大值的偏移(lag)的个数。
确定了转移关系之后,那么 这两个参数怎么来选择呢。显然我们希望NCCF 越大越好, 所以 应该与其成反比,即 。另外,由于是求基频F0,当然希望偏移越小越好,假设与 对应的偏移量为 , 那希望 。最终我们得到下式:

其中是用来惩罚长偏移的系数。另外这里也需要说明 和式(5)中的 不是一个意思,式(5)中m是偏移量,这里j是候选点的索引, 才是偏移,所以对应到式(5)中的NCCF的话,应该是

对于转移代价 的考量则主要是希望F0的变化能够平滑一些,因为帧间的F0变化不应该太大,所以这里要惩罚的是偏移量的变化,一种可行的代价是:

同样, 是一个惩罚系数。转移代价的定义在RAPT的论文里要复杂一些,上式只是简化的版本,不过原理都是对偏移变化的惩罚,Kaldi Pitch的论文里定义的是二次方惩罚:

而代码实现中又是这样写的:

也是常数, 由此可见惩罚的形式不太重要。

Kaldi Pitch

Kaldi Pitch算法由RAPT修改而来,论里讲到与RAPT最大的不同是没有对每一帧是否是话音做硬性判断,而是对每一帧都计算Pitch使得Pitch是个连续的值。另外,Kaldi Pitch给出了每一帧是否是话音的概率以及对原始Pitch特征的一些处理,使其更加符合语音识别的任务。具体有哪些不同,我们按照上述RAPT的步骤,一步一步对着Kaldi 的源码来看就一目了然了。

Source Code of Kaldi Pitch

Some issues about online pitch

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

小小鼓励一下~

支付宝
微信