ASR中常用的语音特征之FBank和MFCC(原理 + Python实现)
一步一步讲解和实现ASR中常用的语音特征——FBank和MFCC的提取,包括算法原理、代码和可视化等。
完整Jupyter Notebook链接:https://github.com/Magic-Bubble/SpeechProcessForMachineLearning/blob/master/speech_process.ipynb
文章目录
语音信号的产生
语音通常是指人说话的声音。从生物学的角度来看,是气流通过声带、咽喉、口腔、鼻腔等发出声音;从信号的角度来看,不同位置的震动频率不一样,最后的信号是由基频和一些谐波构成。
之后被设备接收后(比如麦克风),会通过A/D转换,将模拟信号转换为数字信号,一般会有采样、量化和编码三个步骤,采样率要遵循奈奎斯特采样定律:f s > = 2 f fs >= 2ffs>=2f,比如电话语音的频率一般在300Hz~3400Hz,所以采用8kHz的采样率足矣。
下面采用一个30s左右的16比特PCM编码后的语音wav为例。
预加重(Pre-Emphasis)
预加重一般是数字语音信号处理的第一步。语音信号往往会有频谱倾斜(Spectral Tilt)现象,即高频部分的幅度会比低频部分的小,预加重在这里就是起到一个平衡频谱的作用,增大高频部分的幅度。它使用如下的一阶滤波器来实现:
y ( t ) = x ( t ) − α x ( t − 1 ) , 0.95 < α < 0.99 y(t) = x(t) - \alpha x(t-1), \ \ \ \ 0.95 < \alpha < 0.99y(t)=x(t)−αx(t−1), 0.95<α<0.99
笔者对这个公式的理解是:信号频率的高低主要是由信号电平变化的速度所决定,对信号做一阶差分时,高频部分(变化快的地方)差分值大,低频部分(变化慢的地方)差分值小,达到平衡频谱的作用。
pre_emphasis = 0.97
emphasized_signal = np.append(signal[0], signal[1:] - pre_emphasis * signal[:-1])
- 1
- 2
plot_time(emphasized_signal, sample_rate)
- 1
plot_freq(emphasized_signal, sample_rate)
- 1
从下面这个图来看,确实起到了平衡频谱的作用。
分帧(Framing)
在预加重之后,需要将信号分成短时帧。做这一步的原因是:信号中的频率会随时间变化(不稳定的),一些信号处理算法(比如傅里叶变换)通常希望信号是稳定,也就是说对整个信号进行处理是没有意义的,因为信号的频率轮廓会随着时间的推移而丢失。为了避免这种情况,需要对信号进行分帧处理,认为每一帧之内的信号是短时不变的。一般设置帧长取20ms~40ms,相邻帧之间50%(+/-10%)的覆盖。对于ASR而言,通常取帧长为25ms,覆盖为10ms。
加窗(Window)
在分帧之后,通常需要对每帧的信号进行加窗处理。目的是让帧两端平滑地衰减,这样可以降低后续傅里叶变换后旁瓣的强度,取得更高质量的频谱。常用的窗有:矩形窗、汉明(Hamming)窗、汉宁窗(Hanning)
快速傅里叶变换(FFT)
对于每一帧的加窗信号,进行N点FFT变换,也称短时傅里叶变换(STFT),N通常取256或512,然后用如下的公式计算能量谱:
FBank特征(Filter Banks)
经过上面的步骤之后,在能量谱上应用Mel滤波器组,就能提取到FBank特征。
在介绍Mel滤波器组之前,先介绍一下Mel刻度,这是一个能模拟人耳接收声音规律的刻度,人耳在接收声音时呈现非线性状态,对高频的更不敏感,因此Mel刻度在低频区分辨度较高,在高频区分辨度较低,与频率之间的换算关系为:
m = 2595 l o g 10 ( 1 + f 700 ) m = 2595 log_{10} (1 + \frac{f}{700})m=2595log10(1+700f)
f = 700 ( 1 0 m / 2595 − 1 ) f = 700(10^{m/2595} - 1)f=700(10m/2595−1)
Mel滤波器组就是一系列的三角形滤波器,通常有40个或80个,在中心频率点响应值为1,在两边的滤波器中心点衰减到0,如下图:
具体公式可以写为:
PS:“log mel-filter bank outputs”和“FBANK features”说的是同一个东西。
MFCC特征(Mel-frequency Cepstral Coefficients)
前面提取到的FBank特征,往往是高度相关的。因此可以继续用DCT变换,将这些相关的滤波器组系数进行压缩。对于ASR来说,通常取2~13维,扔掉的信息里面包含滤波器组系数快速变化部分,这些细节信息在ASR任务上可能没有帮助。
DCT变换其实是逆傅里叶变换的等价替代:
所以MFCC名字里面有倒谱(Cepstral)。
一般对于ASR来说,对MFCC进行一个正弦提升(sinusoidal liftering)操作,可以提升在噪声信号中最后的识别率:
从公式看,猜测原因可能是对频谱做一个平滑,如果D DD取值较大时,会加重高频部分,使得噪声被弱化?
FBank与MFCC比较
FBank特征的提取更多的是希望符合声音信号的本质,拟合人耳接收的特性。而MFCC特征多的那一步则是受限于一些机器学习算法。很早之前MFCC特征和GMMs-HMMs方法结合是ASR的主流。而当一些深度学习方法出来之后,MFCC则不一定是最优选择,因为神经网络对高度相关的信息不敏感,而且DCT变换是线性的,会丢失语音信号中原本的一些非线性成分。
还有一些说法是在质疑傅里叶变换的使用,因为傅里叶变换也是线性的。因此也有很多方法,设计模型直接从原始的音频信号中提取特征,但这种方法会增加模型的复杂度,而且本身傅里叶变换不太容易拟合。同时傅里叶变换是在短时上应用的,可以建设信号在这个短的时间内是静止的,因此傅里叶变换的线性也不会造成很严重的问题。
结论就是:在模型对高相关的信号不敏感时(比如神经网络),可以用FBank特征;在模型对高相关的信号敏感时(比如GMMs-HMMs),需要用MFCC特征。从目前的趋势来看,因为神经网络的逐步发展,FBank特征越来越流行。
其他特征
- PLP(Perceptual Linear Prediction)
另外一种特征,与MFCC相比有一些优势,具体提取方式见下图:
- 动态特征
加入表现帧之间变化的特征,用如下公式:
d ( t ) = c ( t + 1 ) − c ( t − 1 ) 2 d(t) = \frac{c(t+1) - c(t-1)}{2}d(t)=2c(t+1)−c(t−1)
一般在ASR中使用的特征(用于GMM相关的系统),是39维的;包括(12维MFCC+1维能量) + delta + delta^2
具体提取过程见下图:
标准化
其目的是希望减少训练集与测试集之间的不匹配。有三种操作:
- 去均值 (CMN)
为了均衡频谱,提升信噪比,可以做一个去均值的操作
filter_banks -= (np.mean(filter_banks, axis=0) + 1e-8)
- 1
plot_spectrogram(filter_banks.T, 'Filter Banks')
- 1
mfcc -= (np.mean(mfcc, axis=0) + 1e-8)
- 1
plot_spectrogram(mfcc.T, 'MFCC Coefficients')
- 1
- 方差归一(CVN)
除以标准差,从而使得方差为1
- 标准化(CMVN)
y t ( j ) = y t ( j ) − μ ( y ( j ) ) σ ( y ( j ) ) y_t(j) = \frac{y_t(j) - \mu (y(j))}{\sigma (y(j))}yt(j)=σ(y(j))yt(j)−μ(y(j))
PS:这些操作,还可以针对speaker/channel做;在实时情景下,可以计算moving average。
总结
最后引用文末slide里面的一个总结:
传送门
Speech Processing for Machine Learning: Filter banks, Mel-Frequency Cepstral Coefficients (MFCCs) and What’s In-Between 一个很优质,讲的很清楚的英文博客
Speech Signal Analysis 英国爱丁堡大学一门ASR课程的讲义
python_speech_features 一个很成熟的python提取这些特征的包
ASR中常用的语音特征之FBank和MFCC(原理 + Python实现) 个人博客