梅尔倒谱系数(Mel-scale FrequencyCepstral Coefficients,简称MFCC)。依据人的听觉实验结果来分析语音的频谱,

MFCC分析依据的听觉机理有两个

第一梅尔刻度(Mel scale):人耳感知的声音频率和声音的实际频率并不是线性的,有下面公式

  • 从频率转换为梅尔刻度的公式为:$f_{mel}=2595*\log _{10}(1+\frac{f}{700})$
  • 从梅尔回到频率:$f = 700 (10^{f_{mel}/2595} - 1)$

  式中$f_{mel}$是以梅尔(Mel)为单位的感知频域(简称梅尔频域),$f$是以$Hz$为单位的实际语音频率。$f_{mel}$与$f$的关系曲线如下图所示,若能将语音信号的频域变换为感知频域中,能更好的模拟听觉过程的处理。

第二临界带(Critical Band):把进入人耳的声音频率用临界带进行划分,将语音在频域上就被划分成一系列的频率群,组成了滤波器组,即Mel滤波器组。

  研究表明,人耳对不同频率的声波有不同的听觉敏感度。从200Hz到5000Hz的语音信号对语音的清晰度影响较大。两个响度不等的声音作用于人耳时,则响度较高的频率成分的存在会影响到对响度较低的频率成分的感受,使其变得不易察觉,这种现象称为掩蔽效应

  由于频率较低的声音(低音)在内耳蜗基底膜上行波传递距离大于频率较高的声音(高音),因此低音容易掩蔽高音。低音掩蔽的临界带宽较高频要小。所以,人们从低频到高频这一段频带内按临界带宽的大小由密到疏安排一组带通滤波器,对输入信号进行滤波。将每个带通滤波器输出的信号能量作为信号的基本特征,对此特征经过进一步处理后就可以作为语音的输入特征。由于这种特征不依赖于信号的性质,对输入信号不做任何的假设和限制,又利用了听觉模型的研究成果。因此,这种参数比基于声道模型的LPCC相比具有更好的鲁棒性,更符合人耳的听觉特性,而且当信噪比降低时仍然具有较好的识别性能。

求MFCC的步骤

  1. 将信号帧化为短帧
  2. 对于每一帧,计算功率谱的周期图估计
  3. 将mel滤波器组应用于功率谱,求滤波器组的能量,将每个滤波器中的能量相加
  4. 取所有滤波器组能量的对数
  5. 取对数滤波器组能量的离散余弦变换(DCT)。
  6. 保持DCT系数2-13,其余部分丢弃

  通常还有其他事情要做,有时会将帧能量附加到每个特征向量上。通常还会附加Delta和Delta-Delta 特征。提升也通常应用于最终特征。

MFCC的提取过程

一、预处理

  预处理包括预加重、分帧、加窗函数。假设我们的语音信号采样频率为8000Hz,语音数据在这里获取

import numpy
import scipy.io.wavfile
from scipy.fftpack import dct sample_rate, signal = scipy.io.wavfile.read('OSR_us_000_0010_8k.wav')
signal = signal[0:int(3.5 * sample_rate)] # 我们只取前3.5s

时域中的语音信号

(1)、预加重(Pre-Emphasis)

  对信号应用预加重滤波器以放大高频,预加重滤波器在以下几个方面很有用:

  1. 平衡频谱,因为高频通常与较低频率相比具有较小的幅度,
  2. 避免在傅里叶变换操作操作过程中出现数值问题
  3. 改善信号 - 噪声比(SNR)
  4. 消除发声过程中声带和嘴唇的效应,来补偿语音信号受到发音系统所抑制的高频部分,也为了突出高频的共振峰。

预加重处理其实是将语音信号通过一个高通滤波器:

$$y(t) = x(t) - \alpha x(t-1)$$

其中滤波器系数($\alpha$)的通常为0.95或0.97,这里取pre_emphasis =0.97:

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

预加重后的时域信号

题外话:预加重在现代系统中的影响不大,主要是因为除避免在现代FFT实现中不应成为问题的FFT数值问题,大多数预加重滤波器的动机都可以通过均值归一化来实现(在本文后面讨论)。 在现代FFT实现中。

(2)、分帧(Framing)

  在预加重之后,我们需要将信号分成短时帧。因此在大多数情况下,语音信号是非平稳的,对整个信号进行傅里叶变换是没有意义的,因为我们会随着时间的推移丢失信号的频率轮廓。语音信号是短时平稳信号。因此我们在短时帧上进行傅里叶变换,通过连接相邻帧来获得信号频率轮廓的良好近似。

  将信号帧化为20-40 ms帧。标准是25毫秒 frame_size = 0.025。这意味着8kHz信号的帧长度为0.025 * 8000 = 200个采样点。帧移通常为10毫秒 frame_stride = 0.01(80个采样点),为了避免相邻两帧的变化过大,因此会让两相邻帧之间有一段重叠区域,通常约为每帧语音的1/2或1/3或50%(+/-10%),我们设置为15毫秒 overlap=0.015,因此此重叠区域包含了0.015*8000=120个取样点。第一个语音帧0开始,下一个语音帧从80开始,直到到达语音文件的末尾。如果语音文件没有划分为偶数个帧,则用零填充它以使其完成。

frame_length, frame_step = frame_size * sample_rate, frame_stride * sample_rate  # 从秒转换为采样点
signal_length = len(emphasized_signal)
frame_length = int(round(frame_length))
frame_step = int(round(frame_step))
# 确保我们至少有1帧
num_frames = int(numpy.ceil(float(numpy.abs(signal_length - frame_length)) / frame_step)) pad_signal_length = num_frames * frame_step + frame_length
z = numpy.zeros((pad_signal_length - signal_length))
# 填充信号,确保所有帧的采样数相等,而不从原始信号中截断任何采样
pad_signal = numpy.append(emphasized_signal, z) 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)]

(3)、加窗(Window)

  将信号分割成帧后,我们再对每个帧乘以一个窗函数,如Hamming窗口。以增加帧左端和右端的连续性。抵消FFT假设(数据是无限的),并减少频谱泄漏。汉明窗的形式如下:

$$W\left( n,a \right)=\left( 1-a \right)-a\times \cos \left( \frac{2\pi n}{N-1} \right)$$

  式$0\le n\le N-1$$,N是窗口长度,我们这里假设$a=0.46$

frames *= numpy.hamming(frame_length)
# frames *= 0.54 - 0.46 * numpy.cos((2 * numpy.pi * n) / (frame_length - 1)) # 内部实现

二、FFT(Fourier-Transform)

  由于信号在时域上的变换通常很难看出信号的特性,通常对它做FFT变换转换为频域上的能量分布来观察,不同的能量分布,就能代表不同语音的特性。

  接下来我们对分帧加窗后的各帧信号进行做一个N点FFT来计算频谱,也称为短时傅立叶变换(STFT),其中N通常为256或512,NFFT=512;

$$S_i(k)=\sum_{n=1}^{N}s_i(n)e^{-j2\pi kn/N} 1\le k \le K$$

mag_frames = numpy.absolute(numpy.fft.rfft(frames, NFFT))   # fft的幅度(magnitude)

三、功率谱(Power Spectrum)

  然后我们使用以下公式计算功率谱(周期图periodogram),对语音信号的频谱取模平方(取对数或者去平方,因为频率不可能为负,负值要舍去)得到语音信号的谱线能量。

$$P = \frac{|FFT(x_i)|^2}{N}$$

其中,$X_i$是信号X的第$i$帧,这可以用以下几行来实现:

pow_frames = ((1.0 / NFFT) * ((mag_frames) ** 2))  # 功率谱

四、滤波器组(Filter Banks)

  计算Mel滤波器组,将功率谱通过一组Mel刻度(通常取40个滤波器,nfilt=40)的三角滤波器(triangular filters)来提取频带(frequency bands)。

  这个Mel滤波器组就像人类的听觉感知系统(耳朵),人耳只关注某些特定的频率分量(人的听觉对频率是有选择性的)。它对不同频率信号的灵敏度是不同的,换言之,它只让某些频率的信号通过,而压根就直接无视它不想感知的某些频率信号。但是这些滤波器在频率坐标轴上却不是统一分布的,在低频区域有很多的滤波器,他们分布比较密集,但在高频区域,滤波器的数目就变得比较少,分布很稀疏。因此Mel刻度的目的是模拟人耳对声音的非线性感知,在较低的频率下更具辨别力,在较高的频率下则不具辨别力。我们可以使用以下公式在赫兹(f)和梅尔(m)之间进行转换:

  我们可以用下面的公式,在语音频率和Mel频率间转换

  • 从频率转换为梅尔刻度的公式为:$f_{mel}=2595*\log _{10}(1+\frac{f}{700})$
  • 从梅尔回到频率:$f = 700 (10^{f_{mel}/2595} - 1)$

  定义一个有M个三角滤波器的滤波器组(滤波器的个数和临界带的个数相近),M通常取22-40,26是标准,本文取nfilt = 40。滤波器组中的每个滤波器都是三角形的,中心频率为f(m) ,中心频率处的响应为1,并向0线性减小,直到达到两个相邻滤波器的中心频率,其中响应为0,各f(m)之间的间隔随着m值的增大而增宽,如图所示:

Mel-Scale上的Filter Bank

这可以通过以下等式建模,三角滤波器的频率响应定义为:

$$H_m(k) =
\begin{cases}
\hfill 0 \hfill & k < f(m - 1) \\
\\
\hfill \dfrac{k - f(m - 1)}{f(m) - f(m - 1)} \hfill & f(m - 1) \leq k < f(m) \\
\\
\hfill 1 \hfill & k = f(m) \\
\\
\hfill \dfrac{f(m + 1) - k}{f(m + 1) - f(m)} \hfill & f(m) < k \leq f(m + 1) \\
\\
\hfill 0 \hfill & k > f(m + 1) \\
\end{cases}$$

  对于FFT得到的幅度谱,分别跟每一个滤波器进行频率相乘累加,得到的值即为该帧数据在该滤波器对应频段的能量值。如果滤波器的个数为22,那么此时应该得到22个能量值

nfilt = 40
low_freq_mel = 0
high_freq_mel = (2595 * np.log10(1 + (sample_rate / 2) / 700)) # 将Hz转换为Mel
# 我们要做40个滤波器组,为此需要42个点,这意味着在们需要low_freq_mel和high_freq_mel之间线性间隔40个点
mel_points = np.linspace(low_freq_mel, high_freq_mel, nfilt + 2) # 使得Mel scale间距相等
hz_points = (700 * (10 ** (mel_points / 2595) - 1)) # 将Mel转换回-Hz
# bin = sample_rate/NFFT # frequency bin的计算公式
# bins = hz_points/bin=hz_points*NFFT/ sample_rate # 得出每个hz_point中有多少frequency bin
bins = np.floor((NFFT + 1) * hz_points / sample_rate) fbank = np.zeros((nfilt, int(np.floor(NFFT / 2 + 1))))
for m in range(1, nfilt + 1):
f_m_minus = int(bins[m - 1]) # 左
f_m = int(bins[m]) # 中
f_m_plus = int(bins[m + 1]) # 右 for k in range(f_m_minus, f_m):
fbank[m - 1, k] = (k - bins[m - 1]) / (bins[m] - bins[m - 1])
for k in range(f_m, f_m_plus):
fbank[m - 1, k] = (bins[m + 1] - k) / (bins[m + 1] - bins[m])
filter_banks = np.dot(pow_frames, fbank.T)
filter_banks = np.where(filter_banks == 0, np.finfo(float).eps, filter_banks) # 数值稳定性
filter_banks = 20 * np.log10(filter_banks) # dB

信号的功率谱经过 Filter Bank 后,得到的谱图为:

信号的频谱图

如果经过Mel scale滤波器组是所需的特征,那么我们可以跳过下一步。

五、梅尔频率倒谱系数(MFCCs)

  上一步骤中计算的滤波器组系数是高度相关的,这在某些机器学习算法中可能是有问题的。因此,我们可以应用离散余弦变换(DCT)对滤波器组系数去相关处理,并产生滤波器组的压缩表示。通常,对于自动语音识别(ASR),保留所得到的个倒频谱系数2-13,其余部分被丢弃; 我们这里取 num_ceps = 12丢弃其他系数原因是它们代表了滤波器组系数的快速变化,并且这些精细的细节对自动语音识别(ASR)没有贡献。

$$C(n)=\sum\limits_{m=0}^{N-1}{s( m)\cos( \frac{\pi n( m-0.5)}{M} )},n=1,2,...,L$$

  L阶指MFCC系数阶数,通常取2-13。这里M是三角滤波器个数。

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

可以将正弦提升器(Liftering在倒谱域中进行过滤。 注意在谱图和倒谱图中分别使用filtering和liftering)应用于MFCC以去强调更高的MFCC,其已被证明可以改善噪声信号中的语音识别。

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

生成的MFCC:

MFCCs

六、均值归一化(Mean Normalization)

  如前所述,为了平衡频谱并改善信噪比(SNR),我们可以简单地从所有帧中减去每个系数的平均值。

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

均值归一化滤波器组:

归一化滤波器数组

同样对于MFCC:

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

均值归一化MFCC:

标准的MFCC

总结

  在这篇文章中,我们探讨了计算Mel标度滤波器组和Mel频率倒谱系数(MFCC)的过程。

  我们从动机和实现的角度讨论了计算Filter BanksMFCCs的步骤。值得注意的是,计算滤波器组所需的所有步骤都是由语音信号的性质和人类对这些信号的感知所驱动的。相反,计算MFCC所需的额外步骤是由一些机器学习算法的限制所驱动的。需要离散余弦变换(DCT)来对滤波器组系数去相关化,该过程也称为白化。特别是,当高斯混合模型 - 隐马尔可夫模型(GMMs-HMMs)非常受欢迎时,MFCC非常受欢迎,MFCC和GMM-HMM共同演化为自动语音识别(ASR)的标准方式2随着深度学习在语音系统中的出现,人们可能会质疑MFCC是否仍然是正确的选择,因为深度神经网络不太容易受到高度相关输入的影响,因此离散余弦变换(DCT)不再是必要的步骤。值得注意的是,离散余弦变换(DCT)是一种线性变换,因此在语音信号中丢弃一些高度非线性的信息是不可取的

  质疑傅里叶变换是否是必要的操作是明智的。鉴于傅立叶变换本身也是线性运算,忽略它并尝试直接从时域中的信号中学习可能是有益的。实际上,最近的一些工作已经尝试过,并且报告了积极的结果。然而,傅立叶变换操作是很难学习的操作,可能会增加实现相同性能所需的数据量和模型复杂性。此外,在进行短时傅里叶变换(stft)时,我们假设信号在这一短时间内是平稳的,因此傅里叶变换的线性不会构成一个关键问题。

  如果机器学习算法不易受高度相关输入的影响,则使用Mel刻度滤波器组。如果机器学习算法易受相关输入的影响,则使用MFCCs。

用librosa提取MFCC特征

  MFCC特征是一种在自动语音识别和说话人识别中广泛使用的特征。在librosa中,提取MFCC特征只需要一个函数:

librosa.feature.mfcc(y=None, sr=22050, S=None, n_mfcc=20, dct_type=2, norm='ortho', **kwargs)

参数:

  • y:音频时间序列
  • sr:音频的采样率
  • S:np.ndarray,对数功率梅尔频谱,这个函数既可以支持时间序列输入也可以支持频谱输入,都是返回MFCC系数。
  • n_mfcc:int>0,要返回的MFCC数,scale滤波器组的个数,通常取22~40,本文取40
  • dct_type:None, or {1, 2, 3}  离散余弦变换(DCT)类型。默认情况下,使用DCT类型2。
  • norm: None or ‘ortho’ 规范。如果dct_type为2或3,则设置norm =’ortho’使用正交DCT基础。norm不支持dct_type = 1。

返回:

M:np.ndarray MFCC序列

import librosa

y, sr = librosa.load('./train_nb.wav', sr=16000)
# 提取 MFCC feature
mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=40) print(mfccs.shape) # (40, 65)

绘制频谱图

Librosa有显示频谱图波形函数specshow( ):

import matplotlib.pyplot as plt
import librosa
import librosa.display y, sr = librosa.load('./train_wb.wav', sr=16000)
# 提取 mel spectrogram feature
melspec = librosa.feature.melspectrogram(y, sr, n_fft=1024, hop_length=512, n_mels=128)
logmelspec = librosa.power_to_db(melspec) # 转换为对数刻度
# 绘制 mel 频谱图
plt.figure()
librosa.display.specshow(logmelspec, sr=sr, x_axis='time', y_axis='mel', cmp="jet")
plt.colorbar(format='%+2.0f dB') # 右边的色度条
plt.title('Beat wavform')
plt.show()

参考文献

MFCC特征参数提取(一)(基于MATLAB和Python实现)

Speech Processing for Machine Learning: Filter banks, Mel-Frequency Cepstral Coefficients (MFCCs) and What's In-Between

Mel Frequency Cepstral Coefficient (MFCC) tutorial

音频处理库—librosa的安装与使用

语音信号的梅尔频率倒谱系数(MFCC)的原理讲解及python实现的更多相关文章

  1. 语音频谱语音信号处理之(四)梅尔频率倒谱系数(MFCC)

    今天一直在查找语音频谱之类的问题,今天正好有机会和大家共享一下. 语音信号处置之(四)梅尔频率倒谱系数(MFCC) zouxy09@qq.com http://blog.csdn.net/zouxy0 ...

  2. 【VS开发】【智能语音处理】语音信号处理之(四)梅尔频率倒谱系数(MFCC)

    语音信号处理之(四)梅尔频率倒谱系数(MFCC) zouxy09@qq.com http://blog.csdn.net/zouxy09 这学期有<语音信号处理>这门课,快考试了,所以也要 ...

  3. 梅尔频率倒谱系数(MFCC) 学习笔记

    最近学习音乐自动标注的过程中,看到了有关使用MFCC提取音频特征的内容,特地在网上找到资料,学习了一下相关内容.此笔记大部分内容摘自博文 http://blog.csdn.net/zouxy09/ar ...

  4. Mel倒谱系数

    Mel倒谱系数:MFCC Mel频率倒谱系数(Mel Frequency Cepstrum Coefficient)的缩写是MFCC,Mel频率是基于人耳听觉特性提出来的,它与Hz频率成非线性对应关系 ...

  5. 梅尔倒谱系数特征(Mel-frequency cepstral coefficients,MFCC)

    引言 感知实验表明,人耳对于声音信号的感知聚焦于某一特定频率区域内,而非在整个频谱包络中. MFCC特征是应用非常广泛的语音特征. 语音的MFCC特征是基于人耳感知实验得到,将人耳当成特定的滤波器,只 ...

  6. 倒谱(Cepstrum)和线性预测倒谱系数(LPCCs)

    倒谱是表示一帧语音数据特征的一个序列.从periodogram estimate of the power spectrum计算得到的倒谱系数,可以用于基音追踪(pitch tracking),然而, ...

  7. 如何将声学的spectrogram(声谱图)重新反变换成时域语音信号

    最近在研究一些信号分析的事情,感兴趣如何将频谱信号反变换成时域信号.fft 与ifft可以顺畅的转变,但是这个是一帧信号,当时间较长的信号再一起是,通过反变换变成一帧一帧的时域信号,如何把他们拼接起来 ...

  8. 形象地展示信号与系统中的一些细节和原理——卷积、复数、傅里叶变换、拉普拉斯变换、零极图唯一确定因果LTI系统

    看懂本文需要读者具备一定的微积分基础.至少开始学信号与系统了本文主要讲解欧拉公式.傅里叶变换的频率轴的负半轴的意义.傅里叶变换的缺陷.为什么因果LTI系统可以被零极图几乎唯一确定等等容易被初学者忽略但 ...

  9. 以语音评测的PC端demo代码为例,讲解口语评测如何实现

    本文由云+社区发表 作者:腾讯智慧教育 概述 腾讯云智聆口语评测(英文版)(Smart Oral Evaluation-English,SOE-E)是腾讯云推出的语音评测产品,是基于英语口语类教育培训 ...

随机推荐

  1. R语言中如何找出在两个数据框中完全相同的行(How to find common rows between two dataframe in R?)

    I would like to make a new data frame which only includes common rows of two separate data.frame. ex ...

  2. ubuntu16.04双系统创建分区

    ubuntu安装分区 安装ubuntu 图1:Ubuntu Linux分区向导 如果希望对分区过程进行完全控制,可以使用"其它"选项.单击"继续"按钮,安装向导 ...

  3. pip安装第三方库

    不是所有的第三方Python包都能通过pip来安装,只能是发布在pypi.org上面的才能通过pip安装. pypi是什么? pypi是一个仓库,上面存放了大量的Python第三方软件包,是由Pyth ...

  4. 【记录】SpringBoot 2.X整合Log4j没有输出INFO、DEBUG等日志信息解决方案

    由于批量更新的时候一直无法定位问题出处,就去服务器定位日志,奈何日志一直无法输出,为了能够更好的定位问题,痛定思痛后逐步排查最终解决问题.如有客官看到此处,请不要盲目对号入座,我的项目环境或许与你有区 ...

  5. Linux权限管理(7)

    权限的基本介绍: rwx权限详解: rwx作用到文件: [r]:代表可读,可以读取.查看 [w]:代表可写,可以修改,但不代表可以删除该文件,删除一个文件的前提条件是对该文件所在的目录有写权限才能删除 ...

  6. Asp.NetCore源码学习[1-2]:配置[Option]

    Asp.NetCore源码学习[1-2]:配置[Option] 在上一篇文章中,我们知道了可以通过IConfiguration访问到注入的ConfigurationRoot,但是这样只能通过索引器IC ...

  7. python 35 多线程

    目录 多线程 1. 线程 2. 线程vs进程 3. 开启线程的两种方法. 4. 线程的特性 5. 线程的相关方法 6. join 阻塞 7. 守护线程 daemon 8. 互斥锁 多线程 1. 线程 ...

  8. JS-对象的深浅拷贝及其新增方法测试

    我们在了解数据类型的时候,都知道一般的字符,数值,undefined和布尔值等都为一般数据类型,它们在进行数据传输的时候,进行的是值传递,当我们修改新数据,是不影响老数据的.但是我们今天要讲的是数据类 ...

  9. 云原生生态周报 Vol. 17 | Helm 3 发布首个 beta 版本

    本周作者 | 墨封.衷源.元毅.有济.心水 业界要闻 1. Helm 3 首个 beta 版本 v3.0.0-beta.1 发布 该版本的重点是完成最后的修改和重构,以及移植其他 Helm 2 特性. ...

  10. C#开发BIMFACE系列8 服务端API之获取文件上传状态信息

    系列目录     [已更新最新开发文章,点击查看详细] 在BIMFACE控制台上传文件,上传过程及结束后它会自动告诉你文件的上传状态,目前有三种状态:uploading,success,failure ...