获得语音信号的fbank特征和MFCC特征的一般步骤是:预加重、分帧、加窗、短时傅里叶变换(STFT)、mel滤波、去均值等。对fbank做离散余弦变换(DCT)即可获得mfcc特征。
PS:“log mel-filter bank outputs”和“FBANK features”说的是同一个东西。
Step0 MFCC
倒谱参数:
MFCCs(Mel Frequency Cepstral Coefficents):是在Mel标度频率域提取出来的倒谱参数,是一种在自动语音和说话人识别中广泛使用的特征。
Mel标度描述了人耳频率的非线性特性,它与频率的关系可用下式近似表示:
M
e
l
(
f
)
=
2595
×
l
g
(
1
+
f
700
)
Mel(f)=2595×lg(1+\frac{f}{700})
Mel(f)=2595×lg(1+700f)
在这个频域内,人对音调的感知度为线性关系。
Step1 预加重
针对录音时高频信号丢失:
在音频录制过程中,高频信号更容易衰减,而像元音等一些因素的发言包含了较多的高频信号的成分,高频信号的丢失,可能会导致音素的共振峰并不明显,使得声学模型对这些音素的建模能力不强。
预加重处理其实是将语音信号通过一个高通滤波器,可以提高信号高频部分的能量,给定时域输入信号x[n],预加重之后信号为:
y
(
t
)
=
x
(
t
)
−
α
x
(
t
−
1
)
,
0.9
≤
α
≤
1.0
y(t)=x(t)-\alpha x(t-1), 0.9\le \alpha \le 1.0
y(t)=x(t)−αx(t−1),0.9≤α≤1.0
公式理解:
信号频率的高低主要是由信号电平变化的速度所决定,对信号做一阶差分时,高频部分(变化快的地方)差分值大,低频部分(变化慢的地方)差分值小,达到平衡频谱的作用。
pre_emphasis = 0.97
sig = np.append(sig[0], sig[1:] - pre_emphasis * sig[:-1])
plot_time(sig, fs)
Step2 分针加窗
分帧:帧长0.025s、帧移0.01s
- 为了减少语音信号整体的非稳态、时变的影响,从而对语音信号进行分段处理,其中每一段称为一帧,一般取25ms。
- 为了使帧与帧之间平滑过渡,保持其连续性,分帧一般采用交叠分段的方法,保证相邻两帧相互重叠一部分。相邻两帧的起始位置的时间差称为帧移,一般取10ms.
frame_size, frame_stride = 0.025, 0.01
frame_length, frame_step = int(round(frame_size * sample_rate)), int(round(frame_stride * sample_rate))
signal_length = len(emphasized_signal)
num_frames = int(np.ceil(np.abs(signal_length - frame_length) / frame_step)) + 1
pad_signal_length = (num_frames - 1) * frame_step + frame_length
z = np.zeros((pad_signal_length - signal_length))
pad_signal = np.append(emphasized_signal, z)
indices = np.arange(0, frame_length).reshape(1, -1) + np.arange(0, num_frames * frame_step, frame_step).reshape(-1, 1)
frames = pad_signal[indices]
print(frames.shape)
加窗:
- 在分帧之后,为了让帧两端平滑地衰减,取得更高质量的频谱。
- 之后会对加窗的数据进行FFT,它假设一个窗内的信号是代表一个周期的信号(也就是说窗的左端和右端应该大致能连在一起)。而通常一小段音频数据没有明显的周期性,加后,数据形状就有点周期的感觉了。
- 常用的窗有:矩形窗、汉明(Hamming)窗、汉宁窗(Hanning)
以汉明窗为例,其窗函数为:
w
(
n
,
α
)
=
{
(
1
−
α
)
−
α
×
c
o
s
(
2
π
n
N
−
1
)
,
0
≤
n
≤
N
−
1
0
,
n
=
else
w(n,\alpha) = \begin{cases} (1-\alpha)-\alpha×cos(\frac{2\pi n}{N-1}),0\le n\le N-1\\ 0,n =\text{else}\\ \end{cases}
w(n,α)={(1−α)−α×cos(N−12πn),0≤n≤N−10,n=else
N是窗的宽度。一般
α
\alpha
α取0.46。
hamming = np.hamming(frame_length)
# hamming = 0.54 - 0.46 * np.cos(2 * np.pi * np.arange(0, frame_length) / (frame_length - 1))
frames *= hamming
Step3 STFT
了解FFT
对于每一帧的加窗信号,进行N点FFT变换,也成短时傅里叶变换STFT,N通常取256或512,然后用如下的公式计算能量谱:
P
=
∣
F
F
T
(
x
i
)
∣
2
N
P=\frac{|FFT(x_{i})|^{2}}{N}
P=N∣FFT(xi)∣2
NFFT = 512
mag_frames = np.absolute(np.fft.rfft(frames, NFFT))
pow_frames = ((1.0 / NFFT) * (mag_frames ** 2))
Step4 提取FBank特征
在能量谱上应用Mel滤波器组,就能提取到FBank特征。
Mel滤波器组就是一系列的三角形滤波器,通常有40个或80个,在中心频率点响应值为1,在两边的滤波器中心点衰减到0。
具体的公式可写为
H
m
(
k
)
=
{
0
k
<
f
(
m
−
1
)
k
−
f
(
m
−
1
)
f
(
m
)
−
f
(
m
−
1
)
f
(
m
−
1
)
≤
k
<
f
(
m
)
1
k
=
f
(
m
)
f
(
m
+
1
)
−
k
f
(
m
+
1
)
−
f
(
m
)
f
(
m
)
<
k
≤
f
(
m
+
1
)
0
k
>
f
(
m
+
1
)
H_{m}(k)= \begin{cases} 0&k< f(m-1)\\ \frac{k-f(m-1)}{f(m)-f(m-1)}&f(m-1)\le k <f(m)\\ 1&k=f(m)\\ \frac{f(m+1)-k}{f(m+1)-f(m)}&f(m)<k\le f(m+1)\\ 0&k>f(m+1) \end{cases}
Hm(k)=⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧0f(m)−f(m−1)k−f(m−1)1f(m+1)−f(m)f(m+1)−k0k<f(m−1)f(m−1)≤k<f(m)k=f(m)f(m)<k≤f(m+1)k>f(m+1)
最后在能量谱上应用Mel滤波器组,其公式为:
Y
t
(
m
)
=
∑
k
=
1
N
H
m
(
k
)
∣
X
t
(
k
)
∣
2
Y_{t}(m)=\sum_{k=1}^{N}H_{m}(k)|X_{t}(k)|^{2}
Yt(m)=k=1∑NHm(k)∣Xt(k)∣2
其中,k表示FFT变换后的编号,m表示mel滤波器的编号。
low_freq_mel = 0
high_freq_mel = 2595 * np.log10(1 + (sample_rate / 2) / 700)
nfilt = 40
mel_points = np.linspace(low_freq_mel, high_freq_mel, nfilt + 2) # 所有的mel中心点,为了方便后面计算mel滤波器组,左右两边各补一个中心点
hz_points = 700 * (10 ** (mel_points / 2595) - 1)
fbank = np.zeros((nfilt, int(NFFT / 2 + 1))) # 各个mel滤波器在能量谱对应点的取值
bin = (hz_points / (sample_rate / 2)) * (NFFT / 2) # 各个mel滤波器中心点对应FFT的区域编码,找到有值的位置
for i in range(1, nfilt + 1):
left = int(bin[i-1])
center = int(bin[i])
right = int(bin[i+1])
for j in range(left, center):
fbank[i-1, j+1] = (j + 1 - bin[i-1]) / (bin[i] - bin[i-1])
for j in range(center, right):
fbank[i-1, j+1] = (bin[i+1] - (j + 1)) / (bin[i+1] - bin[i])
Step5 MFCC特征
num_ceps = 12
mfcc = dct(filter_banks, type=2, axis=1, norm='ortho')[:, 1:(num_ceps+1)]
cep_lifter = 23
(nframes, ncoeff) = mfcc.shape
n = np.arange(ncoeff)
lift = 1 + (cep_lifter / 2) * np.sin(np.pi * n / cep_lifter)
mfcc *= lift
一般对于ASR来说,对MFCC进行一个正弦提升(sinusoidal liftering)操作,可以提升在噪声信号中最后的识别率: