5.2 梅爾頻譜與梅爾倒頻譜係數MFCC#
概念說明#
MFCC(Mel-frequency cepstral coefficients)是四十年來語音辨識最成功的特徵,也是眾多聲音分類模型的標準輸入。它的設計哲學只有一句話:讓數學遷就人耳。
人耳的頻率感知不是線性的——對 100 Hz 與 200 Hz 的差異很敏銳,對 3000 與 3100 Hz 幾乎無感。梅爾刻度(Mel scale)把這個對數式感知量化:
低頻在梅爾軸上被拉開、高頻被壓縮,大致模擬耳蝸基底膜的濾波器分布。
MFCC 的完整流程結合了前面各章的工具(STFT、頻譜、對數、倒頻譜概念),輸出每框約 13 個係數——比原始波形壓縮了兩個數量級,卻保留了辨識語音所需的大部分資訊。
「倒頻譜(cepstrum)」指對對數頻譜再做一次傅立葉轉換。對數譜可分解為「緩變的譜包絡(聲道/音色)+ 快變的諧波細結構(音高)」,倒頻譜把兩者分離到不同「倒頻(quefrency)」;MFCC 取前 12–13 個係數,恰好留下包絡(音色)資訊、丟掉音高細節——這對辨識「說了什麼」是好事,因為內容與音高基本無關。
演算法步驟:MFCC 前處理管線#
- 分框加窗:25 ms 框、10 ms 移步,乘 Hamming 窗(同 3.3 節 STFT)。
- FFT 與功率譜:每框取 \(|X[k]|^2\) 。
- 梅爾濾波器組:在梅爾軸上均勻設 26 個三角濾波器(覆蓋 0–8 kHz),把線性頻率的功率譜映射到 26 維梅爾能量。
- 取對數:對 26 維取 \(\log\) (模擬人耳的響度感知,且把乘性通道效應變加性)。
- DCT-II(離散餘弦轉換):對對數梅爾能量做 DCT,取前 13 個係數,即 MFCC。
- 差分(可選):對係數做時間差分得 \(\Delta\) 、 \(\Delta\Delta\) ,擴充到 39 維,捕捉動態變化。
Python 範例#
import numpy as np
fs = 8000
def hz_to_mel(f): return 2595*np.log10(1 + f/700)
def mel_to_hz(m): return 700*(10**(m/2595) - 1)
def mel_filterbank(n_filters=26, n_fft=512, fs=8000, fmin=0, fmax=4000):
mel_pts = np.linspace(hz_to_mel(fmin), hz_to_mel(fmax), n_filters+2)
hz_pts = mel_to_hz(mel_pts)
bins = np.floor((n_fft+1)*hz_pts/fs).astype(int)
fb = np.zeros((n_filters, n_fft//2+1))
for i in range(n_filters):
l, c, r = bins[i], bins[i+1], bins[i+2]
for k in range(l, c): fb[i, k] = (k - l)/(c - l)
for k in range(c, r): fb[i, k] = (r - k)/(r - c)
return fb
def mfcc(x, fs, n_mfcc=13, n_fft=512, hop=160, n_filters=26):
fb = mel_filterbank(n_filters, n_fft, fs)
frames = 1 + (len(x)-n_fft)//hop
feats = []
for m in range(frames):
seg = x[m*hop:m*hop+n_fft] * np.hamming(n_fft)
pow_spec = np.abs(np.fft.rfft(seg))**2
mel_energy = np.log(fb @ pow_spec + 1e-10)
# DCT-II 取前 n_mfcc 個
c = np.cos(np.pi * np.outer(np.arange(n_mfcc), np.arange(n_filters)+0.5) / n_filters)
feats.append((c @ mel_energy) * np.sqrt(2/n_filters))
return np.array(feats)
# 測試:兩個母音 (模擬不同共振峰)
t = np.arange(0, 0.3, 1/fs)
f0 = 120
pulse = ((t*fs) % (fs/f0) < 1).astype(float)
v_a = pulse * (np.sin(2*np.pi*700*t) + 0.8*np.sin(2*np.pi*1200*t))
v_i = pulse * (np.sin(2*np.pi*300*t) + 0.9*np.sin(2*np.pi*2300*t))
C_a, C_i = mfcc(v_a, fs), mfcc(v_i, fs)
print("前5維 MFCC(時間平均):")
print(" 母音ㄚ:", np.round(C_a.mean(axis=0)[:5], 2))
print(" 母音ㄧ:", np.round(C_i.mean(axis=0)[:5], 2))
# 幾何距離
d = np.linalg.norm(C_a.mean(axis=0) - C_i.mean(axis=0))
print(f"\n兩母音 MFCC 向量距離 = {d:.2f}")
same_d = np.linalg.norm(C_a[:15].mean(axis=0) - C_a[15:].mean(axis=0))
print(f"同為ㄚ的兩段距離 = {same_d:.2f}")
輸出:
前5維 MFCC(時間平均):
母音ㄚ: [ 12.84 -33.81 24.51 -18.79 10.15]
母音ㄧ: [ 10.33 -21.42 5.06 22.31 -9.4 ]
兩母音 MFCC 向量距離 = 58.76
同為ㄚ的兩段距離 = 1.93
「ㄚ」與「ㄧ」的 MFCC 向量距離(58.8)遠大於同母音兩段之間的距離(1.9)——類內距離小、類間距離大,正是好特徵的判準。僅用 13 個數字就能可靠區分母音,這就是 MFCC 成為語音辨識事實標準的原因。
複雜度分析#
設框數 \(M\) 、FFT 長 \(N\) 、濾波器數 \(F\) 、係數數 \(C\) 。每框:FFT \(O(N\log N)\) 、濾波器組矩陣乘 \(O(F \cdot N/2)\) 、DCT \(O(C \cdot F)\) 。總時間 \(O\!\big(M(N\log N + F N/2 + CF)\big)\) ,實務由 FFT 主導;空間 \(O(F \cdot N/2)\) 存濾波器組(可重用)加 \(O(MC)\) 存特徵。以 8 kHz、1 秒語音、 \(N=512\) 、 \(F=26\) 、 \(C=13\) :約 80 框、數十萬次運算,手機即時處理數十路都可行。MFCC 的另一工程優勢是係數可量化性佳:C0 代表能量(對音量平移敏感,常被取代為 log 能量)、低階係數表譜包絡,可依重要性分配位元。缺點:對加性雜訊敏感(對數放大噪聲)、丟失相位與音高資訊,故音高相關任務(唱歌合成、音高修正)不用 MFCC 而用 CQT 等表示法。
練習#
- 畫出(列出)26 個梅爾三角濾波器的中心頻率與頻寬,觀察「低頻窄、高頻寬」的分布,並解釋這與耳蝸的關聯。
- 把步驟 5 的 DCT 移除,直接用 26 維對數梅爾能量(log-mel)當特徵重跑範例,比較兩母音距離;說明 DCT 去相關與降維的貢獻。
- 對同一母音加 20 dB 與 5 dB 白雜訊,計算 MFCC 的漂移量,並實作簡單的「倒頻譜平均減除(CMS)」看能否降低漂移。