語音識別之——音訊特徵fbank與mfcc,程式碼實現與分析
語音識別中常用的音訊特徵包括fbank與mfcc。
獲得語音訊號的fbank特徵的一般步驟是:預加重、分幀、加窗、短時傅立葉變換(STFT)、mel濾波、去均值等。對fbank做離散餘弦變換(DCT)即可獲得mfcc特徵。
下面透過程式碼進行分析說明。
1、導包
# 導包
import numpy as np
from scipy。io import wavfile
from scipy。fftpack import dct
import matplotlib。pyplot as plt
2、繪圖函式
繪製時域圖
def plot_time(sig, fs):
time = np。arange(0, len(sig)) * (1。0 / fs)
plt。figure(figsize=(20, 5))
plt。plot(time, sig)
plt。xlabel(‘Time(s)’)
plt。ylabel(‘Amplitude’)
plt。grid()
繪製頻域圖
def plot_freq(sig, sample_rate, nfft=512):
xf = np。fft。rfft(sig, nfft) / nfft
freqs = np。linspace(0, sample_rate/2, nfft/2 + 1)
xfp = 20 * np。log10(np。clip(np。abs(xf), 1e-20, 1e100))
plt。figure(figsize=(20, 5))
plt。plot(freqs, xfp)
plt。xlabel(‘Freq(hz)’)
plt。ylabel(‘dB’)
plt。grid()
繪製二維陣列
def plot_spectrogram(spec, notylabele):
fig = plt。figure(figsize=(20, 5))
heatmap = plt。pcolor(spec)
fig。colorbar(mappable=heatmap)
plt。xlabel(‘Time(s)’)
plt。ylabel(ylabel)
plt。tight_layout()
plt。show()
3、資料讀取
音訊訊號連結:OSR_us_000_0010_8k。wav
wav_file = ‘OSR_us_000_0010_8k。wav’
fs, sig = wavfile。read(wav_file)
# 保留前3。5s資料
sig = sig[0: int(3。5 * fs)]
plot_time(sig, fs)
plot_freq(sig, fs)
4、預加重
pre_emphasis = 0。97
sig = np。append(sig[0], sig[1:] - pre_emphasis * sig[:-1])
plot_time(sig, fs)
plot_freq(sig, fs)
從上面這個圖來看,確實起到了平衡頻譜的作用。
5、分幀
def framing(frame_len_s, frame_shift_s, fs, sig):
“”“
分幀,主要是計算對應下標
:param frame_len_s: 幀長,s
:param frame_shift_s: 幀移,s
:param fs: 取樣率,hz
:param sig: 訊號
:return: 二維list,一個元素為一幀訊號
”“”
sig_n = len(sig)
frame_len_n, frame_shift_n = int(round(fs * frame_len_s)), int(round(fs * frame_shift_s))
num_frame = int(np。ceil(float(sig_n - frame_len_n) / frame_shift_n) + 1)
pad_num = frame_shift_n * (num_frame - 1) + frame_len_n - sig_n # 待補0的個數
pad_zero = np。zeros(int(pad_num)) # 補0
pad_sig = np。append(sig, pad_zero)
# 計算下標
# 每個幀的內部下標
frame_inner_index = np。arange(0, frame_len_n)
# 分幀後的訊號每個幀的起始下標
frame_index = np。arange(0, num_frame) * frame_shift_n
# 複製每個幀的內部下標,訊號有多少幀,就複製多少個,在行方向上進行復制
frame_inner_index_extend = np。tile(frame_inner_index, (num_frame, 1))
# 各幀起始下標擴充套件維度,便於後續相加
frame_index_extend = np。expand_dims(frame_index, 1)
# 分幀後各幀的下標,二維陣列,一個元素為一幀的下標
each_frame_index = frame_inner_index_extend + frame_index_extend
each_frame_index = each_frame_index。astype(np。int, copy=False)
frame_sig = pad_sig[each_frame_index]
return frame_sig
frame_len_s = 0。025
frame_shift_s = 0。01
frame_sig = framing(frame_len_s, frame_shift_s, fs, sig)
6、加窗
在分幀之後,通常需要對每幀的訊號進行加窗處理。目的是讓幀兩端平滑地衰減,這樣可以降低後續傅立葉變換後旁瓣的強度,取得更高質量的頻譜。常用的窗有:矩形窗、漢明(Hamming)窗、漢寧窗(Hanning),以漢明窗為例,其窗函式為:
這裡
,
是窗的寬度。
# 加窗
window = np。hamming(int(round(frame_len_s * fs)))
plt。figure(figsize=(20, 5))
plt。plot(window)
plt。grid()
plt。xlim(0, 200)
plt。ylim(0, 1)
plt。xlabel(‘Samples’)
plt。ylabel(‘Amplitude’)
frame_sig *= window
plot_time(frame_sig[1], fs)
plot_freq(frame_sig[1], fs)
7、短時傅立葉變換(STFT)
對於每一幀的加窗訊號,進行N點FFT變換,也稱短時傅立葉變換(STFT),N通常取256或512。
def stft(frame_sig, nfft=512):
“”“
:param frame_sig: 分幀後的訊號
:param nfft: fft點數
:return: 返回分幀訊號的功率譜
np。fft。fft vs np。fft。rfft
fft 返回 nfft
rfft 返回 nfft // 2 + 1,即rfft僅返回有效部分
”“”
frame_spec = np。fft。rfft(frame_sig, nfft)
# 幅度譜
frame_mag = np。abs(frame_spec)
# 功率譜
frame_pow = (frame_mag ** 2) * 1。0 / nfft
return frame_pow
nfft = 512
frame_pow = stft(frame_sig, nfft)
plt。figure(figsize=(20, 5))
plt。plot(frame_pow[1])
plt。grid()
8、mel濾波
經過上面的步驟之後,在能量譜上應用Mel濾波器組,就能提取到FBank特徵。
在介紹Mel濾波器組之前,先介紹一下Mel刻度,這是一個能模擬人耳接收聲音規律的刻度,人耳在接收聲音時呈現非線性狀態,對高頻的更不敏感,因此Mel刻度在低頻區分辨度較高,在高頻區分辨度較低,與頻率之間的換算關係為:
Mel濾波器組就是一系列的三角形濾波器,通常有40個或80個,在中心頻率點響應值為1,在兩邊的濾波器中心點衰減到0,如下圖:
具體公式可以寫為:
def mel_filter(frame_pow, fs, n_filter, nfft):
“”“
mel 濾波器係數計算
:param frame_pow: 分幀訊號功率譜
:param fs: 取樣率 hz
:param n_filter: 濾波器個數
:param nfft: fft點數
:return: 分幀訊號功率譜mel濾波後的值的對數值
mel = 2595 * log10(1 + f/700) # 頻率到mel值對映
f = 700 * (10^(m/2595) - 1 # mel值到頻率對映
上述過程本質上是對頻率f對數化
”“”
mel_min = 0 # 最低mel值
mel_max = 2595 * np。log10(1 + fs / 2。0 / 700) # 最高mel值,最大訊號頻率為 fs/2
mel_points = np。linspace(mel_min, mel_max, n_filter + 2) # n_filter個mel值均勻分佈與最低與最高mel值之間
hz_points = 700 * (10 ** (mel_points / 2595。0) - 1) # mel值對應回頻率點,頻率間隔指數化
filter_edge = np。floor(hz_points * (nfft + 1) / fs) # 對應到fft的點數比例上
# 求mel濾波器係數
fbank = np。zeros((n_filter, int(nfft / 2 + 1)))
for m in range(1, 1 + n_filter):
f_left = int(filter_edge[m - 1]) # 左邊界點
f_center = int(filter_edge[m]) # 中心點
f_right = int(filter_edge[m + 1]) # 右邊界點
for k in range(f_left, f_center):
fbank[m - 1, k] = (k - f_left) / (f_center - f_left)
for k in range(f_center, f_right):
fbank[m - 1, k] = (f_right - k) / (f_right - f_center)
# mel 濾波
# [num_frame, nfft/2 + 1] * [nfft/2 + 1, n_filter] = [num_frame, n_filter]
filter_banks = np。dot(frame_pow, fbank。T)
filter_banks = np。where(filter_banks == 0, np。finfo(float)。eps, filter_banks)
# 取對數
filter_banks = 20 * np。log10(filter_banks) # dB
return filter_banks
# mel 濾波
n_filter = 40 # mel濾波器個數
filter_banks = mel_filter(frame_pow, fs, n_filter, nfft)
plot_spectrogram(filter_banks。T, ylabel=‘Filter Banks’)
9、去均值
去均值的目的是希望減少訓練集與測試集之間的不匹配,均衡頻譜,提升信噪比。
# 去均值
filter_banks -= (np。mean(filter_banks, axis=0) + 1e-8)
plot_spectrogram(filter_banks。T, ylabel=‘Filter Banks’)
去均值之後的fbank
10、離散餘弦變換(DCT)
前面提取到的FBank特徵,往往是高度相關的。因此可以繼續用DCT變換,將這些相關的濾波器組係數進行壓縮。對於ASR來說,通常取2~13維,扔掉的資訊裡面包含濾波器組係數快速變化部分。
num_ceps = 12
mfcc = dct(filter_banks, type=2, axis=1, norm=‘ortho’)[:, 1:(num_ceps+1)]
plot_spectrogram(mfcc。T, ‘MFCC Coefficients’)
從圖中可以看出,DCT使得fbank的很多高頻部分被濾除掉了。
同樣可以對mfcc進行去均值操作。
mfcc -= (np。mean(mfcc, axis=0) + 1e-8)
plot_spectrogram(mfcc。T, ‘MFCC Coefficients’)
去均值之後的mfcc
11、fbank與mfcc的比較
fbank特徵更多是希望符合聲音訊號的本質,擬合人耳的接收特性。
DCT是線性變換,會丟失語音訊號中原本的一些高度非線性成分。在深度學習之前,受限於演算法,mfcc配GMMs-HMMs是ASR的主流做法。當深度學習方法出來之後,由於神經網路對高度相關的資訊不敏感,mfcc不是最優選擇,經過實際驗證,其在神經網路中的表現也明顯不如fbank。
有人質疑是否需要傅立葉變換,其根據是傅立葉變換也是一種線性變換,也會使得語音訊號中的非線性成分丟失,因此是不是有可能對時域訊號進行直接處理的效果會更好。經實驗證明,訊號經過傅立葉變換後比直接處理時域訊號的效果更好,這是因為傅立葉變換本身不容易被擬合,不可避免地會增加模型的複雜度。此外,傅立葉變換是在短時上應用的,可以假設訊號在短時內是線性的,因此,傅立葉變換的線性不會造成很嚴重的問題。
總結
本文分析了音訊訊號特徵fbank與mfcc,進行了程式碼實現與視覺化。
DCT造成音訊訊號的高度非線性成分丟失嚴重,實驗已經證明,mfcc在對高度相關資訊不敏感的神經網路中的效果不如fbank。
傅立葉變換應用在短時上,其本身的線性不會造成嚴重問題,實驗證明,傅立葉變換的使用有利於模型效果的提升。
參考
Speech Processing for Machine Learning: Filter banks, Mel-Frequency Cepstral Coefficients (MFCCs) and What‘s In-Between
ASR中常用的語音特徵之FBank和MFCC(原理 + Python實現)
英國愛丁堡大學ASR課程講義
一個成熟的python提取這些特徵的包