语音特征提取

原英文博客地址:https://haythamfayek.com/2016/04/21/speech-processing-for-machine-learning.html

目录

 

 Setup

预加重(Pre-Emphasis)

分帧(Framing)

加窗(Window)

傅里叶变换和功率谱(Fourier-Transform and Power Spectrum)

Filter Banks

梅尔倒谱系数 Mel-frequency Cepstral Coefficients (MFCCs)

均值归一化 Mean Normalization

Filter Banks vs MFCCs

Conclusion


 语音处理在自动语音识别(Automatic Speech Recognition ,简称ASR)或者说话人识别(speaker recognition)等系统中扮演了重要的角色。一直以来梅尔频率倒谱系数( Mel-Frequency Cepstral Coefficients ,简称MFCCs)在语音特征处理中很流行,最近则filter banks越来越流行了。在这边博客中讨论filter banks 和MFCCs,以及为什么filter bank会越来越受欢迎。

Filter banks和MFCCs的计算包含了一些相同的步骤,filter banks计算完成后,MFCCs只需在filter banks的基础上再操作几个步骤就可以得到。简而言之,语音信号先经过预加重(pre-emphasis),分帧,为每一帧加窗;然后为每一帧做傅里叶变化,计算功率谱;然后计算 filter banks。在 filter banks上做离散余弦变换(Discrete Cosine Transform ,DCT)保留部分获得系数。最后一步是做均值归一化(mean normalization)。

 Setup

本文使用的wav语音文件“OSR_us_000_0010_8k.wav”,可以从地址下载。wav文件的采样率是8000Hz,是一个干净的语音信号,由一个单独的声音发出一些句子,中间有一些停顿。为了简化,使用这个wav文件的头3.5秒的信号,大致约是这个wav文件的第一句话的长度。

代码使用python2.7.x, Numpy 和Scipy,完整的代码在这地址

import numpy
import scipy.io.wavfile
from scipy.fftpack import dct

sample_rate, signal = scipy.io.wavfile.read('OSR_us_000_0010_8k.wav')  # File assumed to be in the same directory
signal = signal[0:int(3.5 * sample_rate)]  # Keep the first 3.5 seconds

原始的语音信号在时域上形式,如下图:

                                                                                         Signal in the Time Domain

预加重(Pre-Emphasis)

第一步是在信号上应用一个预加重滤波器来放大高频。预加重有几种用途:

  1. 为了平衡频谱,因为高频比低频有更小。(high frequencies usually have smaller magnitudes compared to lower frequencies)
  2. 避免傅里叶变换运算中的数值问题。
  3. 可以提高信噪比(Signal-to-Noise Ratio ,SNR))

预加重公式: 

使用以下代码执行预加重,系数 (α) 一般为 0.95 或者 0.97, 在此设置为pre_emphasis = 0.97:

emphasized_signal = numpy.append(signal[0], signal[1:] - pre_emphasis * signal[:-1])

在现在的系统中预加重的作用不是很大,主要是因为预加重作用大部分可以通过均值归一化来实现(后面会介绍),而避免傅里叶变换运算中的数值问题,在现在FFT实现中也已经不再是个问题了。

预加重之后信号在时域上的形式,如下图:

                                                                         Signal in the Time Domain after Pre-Emphasis

分帧(Framing)

预加重之后,需要将信号切割为较短固定长度的时间片段。原因是信号中的频率是随着时间而改变,因此在大多数情况下对整个信号进行傅里叶变换是没有意义的,就会失去频率曲线。为了避免这个问题,我们可以假定在短的时间内频率是固定的。因此在短时帧上做傅里叶变换,可以通过连接相邻帧获得近似的频率曲线 。

语音帧的范围是20毫秒~40毫秒,且相邻的帧直接有50%(+/-10%)的重叠。一般设置帧大小为25毫秒(frame_size = 0.025),步长是10毫秒( frame_stride = 0.01.),有25毫秒的重叠。

frame_length, frame_step = frame_size * sample_rate, frame_stride * sample_rate  # Convert from seconds to samples
signal_length = len(emphasized_signal)
frame_length = int(round(frame_length))
frame_step = int(round(frame_step))
num_frames = int(numpy.ceil(float(numpy.abs(signal_length - frame_length)) / frame_step))  # Make sure that we have at least 1 frame

pad_signal_length = num_frames * frame_step + frame_length
z = numpy.zeros((pad_signal_length - signal_length))
pad_signal = numpy.append(emphasized_signal, z) # Pad Signal to make sure that all frames have equal number of samples without truncating any samples from the original signal

indices = numpy.tile(numpy.arange(0, frame_length), (num_frames, 1)) + numpy.tile(numpy.arange(0, num_frames * frame_step, frame_step), (frame_length, 1)).T
frames = pad_signal[indices.astype(numpy.int32, copy=False)]

加窗(Window)

信号切割为帧后,然后为每一帧应用加窗函数,例如 汉明窗,汉明窗公式:

 0≤n≤N−1, N是窗长。汉明窗图,如下:

应用加窗的主要原因是为了抵消FFT的假设(即数据是无限的),并减少频谱泄漏。

frames *= numpy.hamming(frame_length)
# frames *= 0.54 - 0.46 * numpy.cos((2 * numpy.pi * n) / (frame_length - 1))  # Explicit Implementation **

傅里叶变换和功率谱(Fourier-Transform and Power Spectrum)

在每帧上做FFT计算频谱,称为短时傅里叶变换(Short-Time Fourier-Transform ,STFT),N一般设置为256或者512(NFFT = 512);然后用下面的公式计算功率谱:

是信号的第i帧 ,公式的代码如下:

mag_frames = numpy.absolute(numpy.fft.rfft(frames, NFFT))  # Magnitude of the FFT
pow_frames = ((1.0 / NFFT) * ((mag_frames) ** 2))  # Power Spectrum

Filter Banks

最后一步计算filter banks,使用用三角滤波器在功率谱的Mel尺度上提取频带 (一般滤波器是40 nfilt = 40 ). Mel尺度旨在模拟人耳对声音的非线性感知,在较低的频率下更具辨别力,在较高的频率下则更辨别力较低。Hz和Mel尺度的转换使用如下公式: 

滤波器组中的每个滤波器都是三角形的,在中心频率处的对应1并向0线性减小,直到达到两个相邻滤波器的中心频率对应为0,如图所示:

                                                                                      Filter bank on a Mel-Scale

可以用下面的方程来模拟:

low_freq_mel = 0
high_freq_mel = (2595 * numpy.log10(1 + (sample_rate / 2) / 700))  # Convert Hz to Mel
mel_points = numpy.linspace(low_freq_mel, high_freq_mel, nfilt + 2)  # Equally spaced in Mel scale
hz_points = (700 * (10**(mel_points / 2595) - 1))  # Convert Mel to Hz
bin = numpy.floor((NFFT + 1) * hz_points / sample_rate)

fbank = numpy.zeros((nfilt, int(numpy.floor(NFFT / 2 + 1))))
for m in range(1, nfilt + 1):
    f_m_minus = int(bin[m - 1])   # left
    f_m = int(bin[m])             # center
    f_m_plus = int(bin[m + 1])    # right

    for k in range(f_m_minus, f_m):
        fbank[m - 1, k] = (k - bin[m - 1]) / (bin[m] - bin[m - 1])
    for k in range(f_m, f_m_plus):
        fbank[m - 1, k] = (bin[m + 1] - k) / (bin[m + 1] - bin[m])
filter_banks = numpy.dot(pow_frames, fbank.T)
filter_banks = numpy.where(filter_banks == 0, numpy.finfo(float).eps, filter_banks)  # Numerical Stability
filter_banks = 20 * numpy.log10(filter_banks)  # dB

对功率谱应用滤波器组后,得到如下的谱图:

如果想得到的特征是filter banks,那么可以跳到均值归一化(mean normalization)。

梅尔倒谱系数 Mel-frequency Cepstral Coefficients (MFCCs)

前面计算得到的filter bank系数是高度相关的,在一些机器学习的算法中这会引起一些问题。因此,应用离散余弦变换(Discrete Cosine Transform ,DCT)去掉系统的filter bank相关性,生成一个filter banks的压缩表示(compressed representation)。通常,对于自动语音识别(Automatic Speech Recognition,ASR)会保留倒谱系数2-13,其余的被丢弃num_ceps = 12。丢弃一些表示的原因是,这些表示反应的是filter bank系数的快速变化,而且这些细节对于自动语音识别(ASR)没有贡献。

mfcc = dct(filter_banks, type=2, axis=1, norm='ortho')[:, 1 : (num_ceps + 1)] # Keep 2-13

可以将正弦提升器应用于mfcc,以消除高mfcc的影响,后者被认为可以提高语音识别在噪音信号下。

(nframes, ncoeff) = mfcc.shape
n = numpy.arange(ncoeff)
lift = 1 + (cep_lifter / 2) * numpy.sin(numpy.pi * n / cep_lifter)
mfcc *= lift  #*

结果就是MFCCs,如下:

                                                                                                MFCCs

均值归一化 Mean Normalization

如之前所说,为了平衡频谱和提高信噪比(SNR), 为所有帧减去每个系数的均值。

filter_banks -= (numpy.mean(filter_banks, axis=0) + 1e-8)

均值归一化后的 filter banks:

                                                                                        Normalized Filter Banks

MFCCs也做同样的操作:

mfcc -= (numpy.mean(mfcc, axis=0) + 1e-8)

均值归一化后的MFCCs,如下:

Filter Banks vs MFCCs

Filter banks和MFCCs都需要计算filter banks是由于语音信号的性质和人类对这些信号的感知。相反的,计算mfcc所需的额外步骤是由一些机器学习算法的局限性所导致的。离散余弦变换(DCT)是用来去掉 filter bank 系数的相关性,也被称为美白的过程。尤其是在使用Gaussian Mixture Models - Hidden Markov Models (GMMs-HMMs)模型的时候MFCCs非常受欢迎,MFCCs和GMMs-HMMs共同发展成为自动语音识别(ASR)的标准方法。随着深度学习在语音系统中的应用,由于深度神经网络对高相关性输入数据的敏感性较低,那么MFCCs是否仍然是正确的选择?因此,离散余弦变换(DCT)不再是必要的了。值得注意的是,离散余弦变换(DCT)是一种线性变换,改变换丢弃了语音信号中一些高度非线性的信息,因此,DCT不再受欢迎。

那么傅里叶变换是否是必要的操作?傅里叶变换本身也是一个线性运算,不使用傅里叶变换而试图直接从时域信号中学习也会有益。事实上,最近的一些工作已经尝试了这一点,并有了积极的结果。然而,傅里叶变换操作是一个很难学习的操作,从时域直接学习,可能会增加实现相同性能所需的数据量和模型复杂性。此外,在进行短时傅里叶变换(STFT)时,我们假设信号在这一短时间内是平稳的,因此,傅里叶变换的线性不会构成一个关键问题。   

总结 Conclusion

本文,探讨了计算Mel尺度filter banks 和Mel频率倒谱系数(MFCCs)的过程,讨论了每个步骤的动机和实现。我们还讨论了与MFCC相比,filter banks越来越受欢迎的原因。

如果机器学习算法不易受高度相关输入的影响,则使用filter banks。

如果机器学习算法易受相关输入的影响,则使用MFCCs。

(完)

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值