背景:
報了深藍學院的語音識別課程,這里做學習記錄
第二課:語音信號處理-->特征提取
1.數(shù)字信號處理
- 1)傅立葉變換
2.常用特征提取
- 1)Fbank特征一般用于DNN訓練
- 2)MFCC特征一般用于對角GMM訓練,各維度之間相關性小
Step1.預加重(pre-emphasis)
? 為什么需要預加重?
提高信號高頻部分的能量,高頻信號在傳遞過程中,衰減較快,
但是高頻部分又蘊含很多對語音識別有利的特征,
因此,在特征提取部分,需要提高高頻部分能量
預加重濾波器是一個一階高通濾波器,給定時域輸入信號??[??],預加重之后的信號為:
?? [??] = ??[??] ? ????[?? ? 1]
import librosa
import numpy as np
from scipy.fftpack import dct
#preemphasis config
alpha = 0.97
# Enframe config
frame_len = 400 # 25ms, fs=16kHz
frame_shift = 160 # 10ms, fs=15kHz
fft_len = 512
# Mel filter config
num_filter = 23
num_mfcc = 12
# Read wav file
wav, fs = librosa.load('./test.wav', sr=None)
def preemphasis(signal, coeff=alpha):
"""perform preemphasis on the input signal.
:param signal: The signal to filter.
:param coeff: The preemphasis coefficient. 0 is no filter, default is 0.97.
:returns: the filtered signal.
"""
return np.append(signal[0], signal[1:] - coeff * signal[:-1])
Step2.加窗(windowing)分幀
- 為什么需要分幀?
語音信號為非平穩(wěn)信號,其統(tǒng)計屬性是隨著時間變化的,以漢語為例,一句話中包含很多聲母和韻母,不同的拼音,發(fā)音的特點很明顯是不一樣的;
- 但是!語音信號又具有短時平穩(wěn)的屬性,比如漢語里一個聲母或者韻母,往往只會持續(xù)幾十到幾百毫秒,在這一個發(fā)音單元里,語音信號表現(xiàn)出明顯的穩(wěn)定性,規(guī)律性(可以自己使用Audition觀察一段語音)
?在進行語音識別的時候,對于一句話,識別的過程也是以較小的發(fā)音單元(音素、字、字節(jié))為單位進行識別,因此用滑動窗來提取短時片段,
? 幀長、幀移、窗函數(shù)的概念,對于采樣率為16kHz的信號,幀長、幀移一般為25ms、10ms,即400和160個采樣點
def enframe(signal, frame_len=frame_len, frame_shift=frame_shift, win=np.hamming(frame_len)):
"""Enframe with Hamming widow function.
:param signal: The signal be enframed
:param win: window function, default Hamming
:returns: the enframed signal, num_frames by frame_len array
"""
num_samples = signal.size
num_frames = np.floor((num_samples - frame_len) / frame_shift)+1
frames = np.zeros((int(num_frames),frame_len))
for i in range(int(num_frames)):
frames[i,:] = signal[i*frame_shift:i*frame_shift + frame_len]
frames[i,:] = frames[i,:] * win
return frames
Step3.傅里葉變換
- 將上一步分幀之后的語音幀,由時域變換到頻域,取DFT系數(shù)的模,得到譜特征
def get_spectrum(frames, fft_len=fft_len):
"""Get spectrum using fft
:param frames: the enframed signal, num_frames by frame_len array
:param fft_len: FFT length, default 512
:returns: spectrum, a num_frames by fft_len/2+1 array (real)
"""
cFFT = np.fft.fft(frames, n=fft_len)
valid_len = int(fft_len / 2 ) + 1
spectrum = np.abs(cFFT[:,0:valid_len])
return spectrum
Step4.梅爾濾波器組和對數(shù)操作
? DFT得到了每個頻帶上信號的能量,但是人耳對頻率的感知不是等間隔的,近似于對數(shù)函數(shù)
? 將線性頻率轉換為梅爾頻率,梅爾頻率和線性頻率轉換關系
mel(f) = 2595 * log(1 + f /700)
? 梅爾三角濾波器組:根據(jù)起始頻率、中間頻率和截止頻率,確定各濾波器系數(shù)

filter_bank_m(k) = [ k - f(m-1) ] / [ f(m) - f(m-1)] , f(m-1) < k < f(m)
filter_bank_m(k) = [ f(m+1) - k] / [ f(m) - f(m-1)] , f(m) < k < f(m+1)
def fbank(spectrum, num_filter = num_filter):
"""Get mel filter bank feature from spectrum
:param spectrum: a num_frames by fft_len/2+1 array(real)
:param num_filter: mel filters number, default 23
:returns: fbank feature, a num_frames by num_filter array
DON'T FORGET LOG OPRETION AFTER MEL FILTER!
"""
feats=np.zeros((spectrum.shape[0], num_filter))
freq_h = fs // 2 # 最大截止頻率
mel_h = f_mel(freq_h) # 最大梅爾刻度
mel_lst = np.linspace(0, mel_h, num_filter+2) # mel刻度等間隔
freq_lst = mel_f(mel_lst) # mel對應的頻率
fft_mel = np.floor((fft_len + 1) * freq_lst / fs) # mel對應fft的位置
bank = np.zeros((num_filter, int(fft_len//2)+1))
for i in range(1, num_filter+1):
left = int(fft_mel[i-1]) # 三角波左側
center = int(fft_mel[i]) # 三角波頂點
right = int(fft_mel[i+1]) # 三角波右側
for j in range(left, center):
bank[i-1, j] = (j - left) / (center -left)
for j in range(center, right):
bank[i-1, j] = (right - j) / (right - center)
feats = np.dot(spectrum, bank.T) # 頻譜與濾波器點乘
feats = np.where(feats == 0, np.finfo(float).eps, feats) # 避免log(0)
return np.log(feats)
Step5.動態(tài)特征計算
- 一階差分(Delta,Δ),類比速度,最簡單的一階差分計算方法
Δ t = [c(t + 1) ? c(t ? 1)]/2
def mfcc(fbank, num_mfcc = num_mfcc):
"""Get mfcc feature from fbank feature
:param fbank: a num_frames by num_filter array(real)
:param num_mfcc: mfcc number, default 12
:returns: mfcc feature, a num_frames by num_mfcc array
"""
feats = dct(fbank, type=2, axis=1, norm="ortho")[:, 1 : (num_mfcc + 1)]
return feats
- 二階差分(Delta delta, ΔΔ),類比加速度,簡單計算方法
ΔΔ t = [Δ(t + 1) ? Δ(t ? 1)]/2
3. 代碼實踐
主要代碼在上面有詳細實現(xiàn),以下是一些實現(xiàn)的感想:
1)主要是寫fbank的濾波器,mfcc就是在fbank的基礎上做了一個離散余弦變換,可以直接調(diào)用scipy;
2)對于fbank,核心是三角濾波器, 直接按照公式,循環(huán)計算就可以了