本文首先是將Speech Processing for Machine Learning: Filter banks, Mel-Frequency Cepstral Coefficients (MFCCs) and What's In-Between這篇2016年4月21號的文章翻譯了一下,而後講出一點本身的理解和註釋。前端
機器學習的語音處理:濾波器組,梅爾頻率倒譜系數(MFCC)以及二者之間的區別python
語音處理在任何語音系統中都扮演着重要的角色,不管它是自動語音識別(ASR)仍是說話者識別等等。 長期以來,梅爾頻率倒譜系數(MFCC)是很是受歡迎的功能。 可是最近,濾波器組正變得愈來愈流行。 在這篇文章中,我將討論濾波器組和MFCC,以及爲何濾波器組變得愈來愈流行。算法
計算濾波器組和MFCC涉及相同的過程,在這兩種狀況下,都將計算濾波器組,並經過一些額外的步驟能夠得到MFCC。 簡而言之,信號會經過預加劇濾波器。 而後將其切成(重疊的)幀,並將窗口函數應用於每一個幀; 以後,咱們在每一個幀上進行傅立葉變換(或更具體地說是短時傅立葉變換),並計算功率譜; 而後計算濾波器組。 爲了得到MFCC,可將離散餘弦變換(DCT)應用於濾波器組,以保留多個所得係數,而其他係數則被丟棄。 兩種狀況的最後一步是平均歸一化。安全
創建
在本文中,我今後處使用了一個16位PCM wav文件,稱爲「 OSR_us_000_0010_8k.wav」,其採樣頻率爲8000 Hz。 wav文件是乾淨的語音信號,包括單個語音,在其中說出一些句子,中間有一些停頓。 爲簡單起見,我使用了信號的前3.5秒,大體至關於wav文件中的第一句話。
我將使用Python 2.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 ''' 顯示這個圖片 plt.figure(figsize=(20,5)) # 修改座標顯示 plt.xlabel("Time(s)") plt.ylabel("Amplitude") plt.plot(x , signal) plt.show() '''原始信號在時域中具備如下形式:app
時域信號
強化前
第一步是對信號應用預加劇濾波器,以放大高頻。 預加劇濾波器在幾種方面頗有用:(1)平衡頻譜,由於與低頻相比,高頻一般具備較小的幅度;(2)避免在傅立葉變換操做期間出現數值問題;(3)還可改善信號 噪聲比(SNR)。機器學習
可使用如下等式中的一階濾波器將預加劇濾波器應用於信號xx:ide
y(t)= x(t)-αx(t-1)y(t)= x(t)-αx(t-1)函數
可使用如下代碼輕鬆實現,其中濾波器係數(αα)的典型值爲0.95或0.97,pre_emphasis = 0.97:性能
emphasized_signal = numpy.append(signal[0], signal[1:] - pre_emphasis * signal[:-1])預加劇在現代系統中的影響不大,主要是由於除避免了不該該成爲問題的傅立葉變換數值問題外,大多數預加劇濾波器的動機均可以使用均值歸一化(在本文後面討論)來實現。 在現代FFT實現中。
預加劇後的信號在時域中具備如下形式:預加劇後的時域信號
構圖
通過預加劇後,咱們須要將信號分紅短時幀。 此步驟的基本原理是信號中的頻率會隨時間變化,所以在大多數狀況下,對整個信號進行傅立葉變換是沒有意義的,由於咱們會隨時間丟失信號的頻率輪廓。 爲避免這種狀況,咱們能夠安全地假設信號中的頻率在很短的時間內是固定的。 所以,經過在此短時幀上進行傅立葉變換,咱們能夠經過串聯相鄰幀來得到信號頻率輪廓的良好近似值。
語音處理中的典型幀大小爲20毫秒至40毫秒,連續幀之間有50%(+/- 10%)重疊。 流行的設置是幀大小爲25毫秒,frame_size = 0.025和10毫秒跨度(重疊15毫秒),frame_stride = 0.01。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)]
窗口
將信號切成幀後,咱們對每一個幀應用諸如漢明窗之類的窗口函數。 Hamming窗口具備如下形式:
w [n] = 0.54-0.46cos(2πnN-1)w [n] = 0.54-0.46cos(2πnN-1)
其中,0≤n≤N-10≤n≤N-1,NN爲窗口長度。 繪製先前的方程式將得出如下曲線:
Hamming Window
咱們有幾個緣由須要將窗函數應用於這些幀,特別是要抵消FFT所作的數據無限的假設並減小頻譜泄漏。
frames *= numpy.hamming(frame_length) # frames *= 0.54 - 0.46 * numpy.cos((2 * numpy.pi * n) / (frame_length - 1)) # Explicit Implementation **傅立葉變換和功率譜
如今,咱們能夠在每一個幀上執行NN點FFT來計算頻譜,這也稱爲短時傅立葉變換(STFT),其中NN一般爲256或512,NFFT = 512; 而後使用如下公式計算功率譜(週期圖):
P = | FFT(xi)| 2NP = | FFT(xi)| 2N
其中,xixi是信號xx的第i個幀。 這能夠用如下幾行實現:
mag_frames = numpy.absolute(numpy.fft.rfft(frames, NFFT)) # Magnitude of the FFT pow_frames = ((1.0 / NFFT) * ((mag_frames) ** 2)) # Power Spectrum濾波器
計算濾波器組的最後一步是將三角濾波器(一般爲40個濾波器,在Mel等級上爲nfilt = 40)應用於功率譜以提取頻帶。 梅爾音階的目的是模仿低頻的人耳對聲音的感知,方法是在較低頻率下更具判別力,而在較高頻率下則具備較少判別力。 咱們可使用如下公式在赫茲(ff)和梅爾(mm)之間轉換:
m = 2595log10(1 + f700)m =2595log10(1 + f700)
f = 700(10m / 2595-1)f = 700(10m / 2595-1)濾波器組中的每一個濾波器都是三角形的,在中心頻率處的響應爲1,並朝着0線性減少,直到它到達兩個相鄰濾波器的中心頻率處,響應爲0,以下圖所示:
梅爾秤上的濾波器組
能夠經過如下方程式建模(今後處獲取):
Hm(k)=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪0k-f( m-1)f(m)-f(m-1)1f(m + 1)-kf(m + 1)-f(m)0k <f(m-1)f(m-1)≤k< f(m)k = f(m)f(m)<k≤f(m + 1)k> f(m + 1)Hm(k)= {0k <f(m-1)k-f(m -1)f(m)-f(m-1)f(m-1)≤k<f(m)1k = f(m)f(m + 1)-kf(m + 1)-f(m )f(m)<k≤f(m + 1)0k> f(m + 1)
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將濾波器組應用於信號的功率譜(週期圖)後,咱們獲得如下頻譜圖:
信號的頻譜圖
若是梅爾級濾波器組是所需的功能,那麼咱們能夠跳過以歸一化。
梅爾頻率倒譜系數(MFCC)
事實證實,在上一步中計算出的濾波器組係數是高度相關的,這在某些機器學習算法中可能會出現問題。 所以,咱們能夠應用離散餘弦變換(DCT)去相關濾波器組係數,並生成濾波器組的壓縮表示。 一般,對於自動語音識別(ASR),結果倒譜系數2-13將保留,其他的將被丟棄; num_ceps =12。丟棄其餘係數的緣由是,它們表明濾波器組係數的快速變化,而這些細微的細節對自動語音識別(ASR)毫無幫助。
mfcc = dct(filter_banks, type=2, axis=1, norm='ortho')[:, 1 : (num_ceps + 1)] # Keep 2-13能夠將正弦提高器1應用於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
平均歸一化
如前所述,爲了平衡頻譜並改善信噪比(SNR),咱們能夠簡單地從全部幀中減去每一個係數的平均值。
filter_banks -= (numpy.mean(filter_banks, axis=0) + 1e-8)均值歸一化濾波器組:
歸一化濾波器組
對於MFCC一樣如此:
mfcc -= (numpy.mean(mfcc, axis=0) + 1e-8)均值歸一化MFCC:
標準化MFCC
濾波器組與MFCC
到目前爲止,已根據其動機和實現方式討論了計算濾波器組和MFCC的步驟。有趣的是,計算濾波器組所需的全部步驟都是由語音信號的性質和人類對此類信號的感知所激發的。相反,某些機器學習算法的侷限性促使了計算MFCC所需的額外步驟。須要離散餘弦變換(DCT)來使濾波器組係數去相關,該過程也稱爲白化。特別是,當高斯混合模型-隱馬爾可夫模型(GMMs-HMM)很是流行而且MFCC和GMMs-HMM一塊兒發展成爲自動語音識別(ASR)2的標準方式時,MFCC便很受歡迎。隨着語音系統中深度學習的出現,人們可能會質疑MFCC是否仍然是正確的選擇,由於深度神經網絡不太容易受到高度相關的輸入的影響,所以離散餘弦變換(DCT)再也不是必要的步驟。值得注意的是,離散餘弦變換(DCT)是線性變換,所以是不但願的,由於它會丟棄語音信號中的某些信息,這些信息是高度非線性的。
懷疑是否須要進行傅立葉變換是明智的。鑑於傅立葉變換自己也是線性運算,所以忽略它並嘗試在時域中直接從信號中學習可能會有所幫助。確實,最近的一些工做已經嘗試了這一點,並報告了積極的結果。可是,傅立葉變換操做是一個很難學習的操做,而且能夠說會增長得到相同性能所需的數據量和模型複雜性。此外,在進行短時傅立葉變換(STFT)時,咱們假設信號在此短期內保持穩定,所以傅立葉變換的線性不會形成嚴重問題。結論
在本文中,咱們探討了計算梅爾級濾波器組和梅爾頻率倒譜系數(MFCC)的過程。 討論了該過程當中每一個步驟的動機和實現。 咱們也提出了與MFCC相比,濾波器組愈來愈受歡迎的緣由。
tl; dr:若是機器學習算法不易受到高度相關的輸入的影響,請使用梅爾級濾波器組。 若是機器學習算法易受相關輸入的影響,請使用MFCC。1.提高在倒頻譜域中進行過濾。 請注意,頻譜和倒譜中的符號分別使用了濾波和提高。 ↩
2.本文對此主題進行了精彩的討論。 ↩
重要名詞理解:
濾波器組:
人耳對聲音頻譜的響應是非線性的,經驗代表:若是咱們可以設計一種前端處理算法,以相似於人耳的方式對音頻進行處理,能夠提升語音識別的性能。FilterBank就是這樣的一種算法。FBank特徵提取要在預處理以後進行,這時語音已經分幀,咱們須要逐幀提取FBank特徵。
傅里葉變換:
人類聽覺系統其實就像是在作傅里葉變換,經過耳蝸(能夠視爲一組濾波器)將時域聲壓轉換爲獨立的不一樣頻率的份量,可是,準確地說,更接近於短時傅里葉變換(STFT)。
傅里葉變換處理非平穩信號(頻率隨時間變化的信號)有天生缺陷。它只能獲取一段信號整體上包含哪些頻率的成分,可是對各成分出現的時刻並沒有所知。所以時域相差很大的兩個信號,可能頻譜圖同樣。
對於這樣的非平穩信號,只知道包含哪些頻率成分是不夠的,咱們還想知道各個成分出現的時間。知道信號頻率隨時間變化的狀況,各個時刻的瞬時頻率及其幅值——這也就是時頻分析。
一個簡單可行的方法就是——加窗。我又要套用方沁園同窗的描述了,「把整個時域過程分解成無數個等長的小過程,每一個小過程近似平穩,再傅里葉變換,就知道在哪一個時間點上出現了什麼頻率了。」這就是短時傅里葉變換。
梅爾頻率倒譜系數(MFCC):
參考:https://zhuanlan.zhihu.com/p/27416870
均值歸一化:
參考:https://www.jianshu.com/p/ef3534ddda15
第一部分構圖用的完整代碼:
import numpy import scipy.io.wavfile from scipy.fftpack import dct import matplotlib as mpl import matplotlib.pyplot as plt 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 plt.figure(figsize=(20,5)) # 修改座標顯示 plt.xlabel("Time(s)") plt.ylabel("Amplitude") plt.plot(signal) plt.show()