語音識別就是把一段語音信號轉換成對應的文本信息,這一過程包括四個大的模塊,分別是:特征提取、聲學模型、語言模型、字典與解碼。
本篇就來梳理一下特征提取模塊的實現思路和方法。
常用的語音特征有:
- 梅爾頻率倒譜系數(Mel-Frequency Cepstral Coefficients, MFCC)
- 梅爾濾波器組系數(Mel Filter Bank, FBank,也叫log-Mel)
- 線性預測系數(Linear Prediction Coefficient, LPC)
基于深度學習網絡的語音識別,目前多采用FBank特征。?獲得FBank特征主要包括以下幾個步驟:
- 預加重;
- 分幀、加窗;
- 快速傅里葉變換,計算功率譜;
- Mel濾波器組;
- 取對數,得到FBank。
1. 預加重(Pre-Emphasis)
在音頻錄制過程中,高頻信號更容易衰減,高頻成分的丟失,可能導致音素的共振峰不明顯,使得聲學模型對這些音素的建模能力不強。預加重是個一階高通濾波器,可以提高信號高頻部分的能量。
預加重的實現方法:給定時域輸入信號x,預加重之后的信號y為:y(t)=x(t)?αx(t?1),其中0.9≤α≤1.0。經過預加重之后的頻譜圖和原始的頻譜圖的比較如下圖所示,其中,左側為原始頻譜圖,右側為預加重處理之后的頻譜圖。
2. 分幀、加窗
語音信號是一個時變的、非穩態的信號,但在短時間范圍內可以認為是時不變的、穩態的。這個短時間的長度一般取10~30ms,可在這個時間范圍內進行語音信號處理。這就是分幀。
分幀一般采用交疊分段的方法,這是為了使幀與幀之間平滑過渡,保持其連續性。前一幀和后一幀的交疊部分稱為幀移,幀移與幀長的比值一般為0~1/2。分幀如下圖所示:
分幀之后,需要使用有限長度窗口進行加權處理,也就是加窗,即sw(n) = s(n) * w(n)。那么,為什么要加窗呢?這是因為后面要對信號進行快速傅里葉變換(FFT)。FFT處理的要求是,信號要么從-∞到+∞,要么為周期信號。由于語音信號只能是有限長度信號,并且分幀后的信號是非周期的,進行FFT處理時會存在頻率泄露的問題。為了盡可能地減小頻率泄露,就需要對信號進行加窗處理。那么,窗函數的選擇就需要滿足:(1) 窗函數頻譜主瓣寬度盡量窄,以得到較高的頻率分辨能力;(2) 窗函數旁瓣衰減盡量大,以減少泄露。
下圖顯示了加窗和不加窗的FFT變換對比:
常用的加窗函數有漢明窗(Hamming)、漢寧窗(Hanning)等。其中,漢明窗的窗函數表達式為:
其中,0≤n≤N?1,N為窗口長度。窗口圖形繪制如下:
3. 短時快速傅里葉變換(STFT),計算功率譜
對于每一幀加窗信號,進行N點FFT變換,也稱為短時傅里葉變換(STFT),N通常取256或512。然后,計算能量譜:
4.? Mel濾波器組
人耳對不同頻率的聲音有不同的感知能力,通常情況下,人耳對低頻的感知辨識力比高頻更好,為了模擬人耳對不同頻率的非線性感知能力,引入了Mel頻率。赫茲頻率(f)與Mel頻率(m)之間的轉換關系如下:
該步是通過定義M個三角濾波器組,對上一步得到的功率譜進行濾波。M的取值范圍一般在22~40,標準值為26,這里取40。濾波器組中的每個濾波器都是三角形的,中心頻率為f(m),該處響應為1,中心頻率兩邊線性減小到0。各f(m)之間的間隔隨著m值的增大而變寬,如下圖所示:
三角濾波器的定義如下:
f(m)是在Mel尺度上轉換回赫茲頻率的位置,由于濾波器最終對第3步計算出來的功率譜進行濾波,因此,在實現中,可以將濾波器位置轉換成FFT bin所在的位置來計算。為了說明這一點,用一段代碼來描述這個過程:
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,在Mel頻率范圍內均勻創建nfilt+2也就是40個點
hz_points = (700 * (10**(mel_points / 2595) - 1)) # Convert Mel to Hz,將42個點的Mel points轉回赫茲頻率
bin = numpy.floor((NFFT + 1) * hz_points / sample_rate) # 將hz_points轉換到FFT binfbank = numpy.zeros((nfilt, int(numpy.floor(NFFT / 2 + 1))))for m in range(1, nfilt + 1):f_m_minus = int(bin[m - 1]) # leftf_m = int(bin[m]) # centerf_m_plus = int(bin[m + 1]) # rightfor 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) # pow_frames即當前幀的功率譜
filter_banks = numpy.where(filter_banks == 0, numpy.finfo(float).eps, filter_banks) # Numerical Stability
filter_banks = 20 * numpy.log10(filter_banks) # dB,進行對數計算,得到最終的FBank特征
5. 取對數,得到FBank
這一步較簡單,在上一步的Python代碼中,對三角濾波器組的輸出取對數,得到最終的FBank特征。不再贅述。
6. 均值歸一化(Mean Normalization)
為了平衡頻譜并提高信噪比,可以通過減去(所有幀的)系數平均值的方式來進行歸一化。
filter_banks -= (numpy.mean(filter_banks, axis=0) + 1e-8)
7. MFCC特征
由于許多ASR系統使用MFCC特征,這里做一個補充說明。
由于FBank系數存在高度相關性,在一些機器學習系統(如之前流行的GMMs-HMMs)中會存在問題,因此,如果對Fbank進行DCT變換來對FBank系數進行去相關,則可以得到MFCC(Mel-Frequency Cepstral Coefficients)。MFCC是FBank的一種壓縮表示。在ASR系統中,一般會保留前2~13個系數,其他的則被丟棄。被丟棄的這些系數表示filter bank的快速變化,這些精細的細節對某些ASR系統是沒有貢獻的。
mfcc = dct(filter_banks, type=2, axis=1, norm='ortho')[:, 1 : (num_ceps + 1)] # num_ceps = 2 - 13
?還可以對MFCC應用正弦提升來改善ASR在噪聲信號下的識別能力:
(nframes, ncoeff) = mfcc.shape
n = numpy.arange(ncoeff)
lift = 1 + (cep_lifter / 2) * numpy.sin(numpy.pi * n / cep_lifter)
mfcc *= lift
MFCC也可以應用均值歸一化。?
參考資料:
Practical Cryptography
Speech Processing for Machine Learning: Filter banks, Mel-Frequency Cepstral Coefficients (MFCCs) and What’s In-Between | Haytham Fayek
《人工智能技術》,鄭孝宗主編?