コンテンツにスキップ

第5回 音響特徴量(MFCC)の原理とPython実装

この回の資料


本回の到達目標

  1. メル尺度が人間の聴覚特性を反映する理由を説明できる
  2. MFCCの計算ステップ(前処理→メルフィルタ→対数→DCT)を順に説明できる
  3. Pythonで MFCC を計算・可視化できる
  4. デルタ特徴量の意味を説明できる
  5. 発展:メル近似式の低域・高域での振る舞い、対数変換とDCTの役割、低次係数の選択による情報の変化を説明できる

進化の視点:聴覚に着想を得た特徴量は、どこまで耳に似ているか

メル尺度は、音の高さの知覚を表す心理尺度である。メル尺度上でフィルタを等間隔に配置すると、Hzで見た幅は一般に低域で狭く、高域で広くなる。ただし、音の高さの尺度・周波数差の弁別閾・蝸牛の聴覚フィルタの幅は異なる量であり、MFCCは内耳の構造や分解能をそのまま再現するモデルではない。1

聴覚に着想を得ることと、その仕様が自然選択によって音声認識のために生じたと示すことは別である。この授業では、特徴量の設計と認識課題での有効性を区別して考える。

技術への接続:MFCCはスペクトルの包絡を比較的少ない次元で表す特徴量として使われてきた。フィルタバンクでの集約や高次係数の切り捨てでは情報が失われるが、ヒトの耳が同じ情報を捨てていることを意味しない。対数メルスペクトログラムや波形を使うモデルとの比較では、特徴量だけでなく学習データ・モデル・計算量もそろえる必要がある(第7・8回)。

問い:ヒトの耳に合わせた特徴量は、機械にとっても最適か。


1. なぜ「メル尺度」が必要か—人間の聴覚の非線形性

Hzでの差と、知覚上の高さの差は同じ尺度ではない

私たちの耳は、すべての周波数を等しく聴いているわけではありません。

比較の例:

物理的差 比率・音程としての違い
100Hz → 200Hz(100Hzの差) 周波数が2倍、1オクターブ
5000Hz → 5100Hz(100Hzの差) 周波数が1.02倍、1オクターブより小さい変化

同じ100 Hzの差でも、相対的な変化は異なる。高域の100 Hz差が聞き取れないと断定することはできない。弁別できるかは音圧・長さ・周波数・聴取者などの条件で変わる。

音楽で体感してみよう

ピアノの鍵盤では、1オクターブ上がると周波数は2倍になります:

  • ラ(A4):440 Hz → ラ(A5):880 Hz
  • ラ(A5):880 Hz → ラ(A6):1760 Hz

音楽の音程は周波数比で表せるため、オクターブのHz差は一定ではない。ただし、オクターブを等間隔とする音高の尺度とメル尺度は同じではない。


2. メル変換の数式とグラフ

HTKで使われるメル近似式

\[ m = 2595 \log_{10}\left(1 + \frac{f}{700}\right) \]

逆変換(メル → Hz):

\[ f = 700\left(10^{m/2595} - 1\right) \]

直感的理解

  • \(f = 0\) Hz → \(m = 0\) メル
  • \(f = 700\) Hz → \(m \approx 781\) メル(700 は式の中の定数で、線形から対数へ移る目安)
  • \(f = 1000\) Hz → \(m \approx 1000\) メル(Hz とメルの目盛りが一致する点)
  • \(f = 7000\) Hz → \(m \approx 2702\) メル

この式は滑らかな曲線で、700 Hzに折れ目はない。700 Hzに比べて十分低い周波数では線形に近く、十分高い周波数では対数に近づく。

メル変換には複数の実装がある。librosaの既定はSlaney方式で、低域の線形区間と高域の対数区間を接続する。本回は数式とコードをそろえるため htk=True を指定する。自作フィルタは面積正規化をしないので、librosaでもフィルタの norm=None(MFCCでは mel_norm=None)を使う。2

Pythonでメル変換を可視化

# ===== セル1:メル尺度のグラフ =====
!pip install librosa numpy matplotlib -q

import numpy as np
import matplotlib.pyplot as plt
import librosa

# Hz → Mel の変換
f_hz = np.linspace(0, 8000, 1000)
f_mel = 2595 * np.log10(1 + f_hz / 700)

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Hz vs Mel
axes[0].plot(f_hz, f_mel, color='steelblue', linewidth=2)
axes[0].set_xlabel("周波数 (Hz)", fontsize=12)
axes[0].set_ylabel("メル (Mel)", fontsize=12)
axes[0].set_title("Hz → Mel 変換曲線", fontsize=13)
axes[0].axvline(x=700, color='red', linestyle='--', label='f = 700 Hz')
axes[0].legend()
axes[0].grid(True, alpha=0.4)

# 等間隔メルのHz位置
mel_points = np.linspace(0, f_mel[-1], 20)
hz_points  = 700 * (10 ** (mel_points / 2595) - 1)

axes[1].scatter(mel_points, hz_points, color='orange', zorder=5)
axes[1].plot(f_mel, f_hz, color='steelblue', linewidth=2)
axes[1].set_xlabel("メル (Mel)(等間隔)", fontsize=12)
axes[1].set_ylabel("対応する周波数 (Hz)", fontsize=12)
axes[1].set_title("等間隔メル → Hzの非線形対応", fontsize=13)
axes[1].grid(True, alpha=0.4)

plt.tight_layout()
plt.show()
導出①:低域では線形、高域では対数 ―― 1つの式が両方をやっている

前節の100→200 Hzと5,000→5,100 Hzは、どちらも100 Hzの差である。これを上のHTK近似式で測り直す。

\[ m(200) - m(100) = 132.74 \ \text{メル}, \qquad m(5100) - m(5000) = 19.60 \ \text{メル} \]

同じ100 Hzの差でも、この式によるメル値の増分は約6.8倍違う。 これは近似式の性質であり、聞き分けの閾値を計算したものではない。なぜそうなるのかを、式の両端から見る。

高いほうの端:\( f \) が 700 に比べて十分大きいとき、\( 1 + f/700 \approx f/700 \) なので

\[ m \;\approx\; 2595 \log_{10}\frac{f}{700} \]

この高域での近似では、周波数を2倍にすると一定量だけ増える形になる。その一定量は \(2595\log_{10}2=781.17\) メルである。メル値の増分がオクターブごとに常に一定という意味ではない。

ただし音声で扱う帯域では、落とした 1 がまだ効いている。実際に計算すると 1,000→2,000 Hz で 521.37 メル、2,000→4,000 Hz で 624.70 メル、4,000→8,000 Hz で 693.96 メルと、781.17 に少しずつ近づいていく。完全に等間隔になるのはもっと高い周波数での話である。

低いほうの端:\( x \) が小さいとき \( \log_{10}(1+x) \approx x / \ln 10 \) なので

\[ m \;\approx\; \frac{2595}{700 \ln 10} f \;=\; 1.6100\, f \]

\(f\) に比例する一次近似になる。ただし成立するのは \(f/700\) が十分小さいときであり、700 Hz以下の全域を精度よく線形とみなせるわけではない。

\( f \) 厳密な \( m \) 線形近似 \( 1.61 f \) ずれ
50 Hz 77.75 80.50 +3.5%
100 Hz 150.49 161.00 +7.0%
200 Hz 283.23 322.00 +13.7%

200 Hz ですでに 1 割以上ずれる。線形なのは「厳密に」ではなく「対数域に比べれば」という意味である。

分母の700は曲線の変化の目安になる尺度であり、折れ目ではない。この近似式を使う場合、メル軸で等間隔に置いた点はHz軸では低域ほど密に並ぶ。

2595 と 700 はどこから来たか

これらはHTK近似式で採用される定数であり、蝸牛の構造からこの授業の計算だけで導かれるものではない。自然対数で書くと係数は \(2595/\ln 10 \simeq1127\) となり、対数の底を変えただけの同じ式である。Slaney方式など別の式とは区別する。メル尺度の心理実験と近似式の選定は別途文献で確認する。1

使った数学:対数の性質 \( \log(ab)=\log a + \log b \)、\( \log_{10}(1+x)\approx x/\ln 10 \) という1次近似(数III)。


3. メルフィルタバンク—三角フィルタの配置

メルフィルタバンクとは

スペクトログラムの周波数軸を、メル尺度で等間隔に並んだ三角形フィルタ群でまとめる処理です。

三角フィルタの特徴

  • 低周波:フィルタが狭い(細かく分析)
  • 高周波:フィルタが広い(まとめて分析)
  • 聴覚に着想を得た集約であり、内耳と同一の分解能になるわけではない

フィルタバンクの処理手順

FFTスペクトル(例:1025ビン)
  ↓
三角フィルタをかける(例:40個)
  ↓
各フィルタの出力(各三角フィルタ内のエネルギーの合計)
  ↓
40次元のメルエネルギーベクトル

数式で表すと

\(k\)番目のフィルタの出力:

\[ S[k] = \sum_{n} X[n] \cdot H_k[n] \]
  • \(X[n]\):パワースペクトル(\(|D[n]|^2\))
  • \(H_k[n]\):\(k\)番目の三角フィルタ

4. メルフィルタバンクをPythonで実装

# ===== セル2:自前でメルフィルタバンクを計算する =====

import numpy as np
import matplotlib.pyplot as plt

def hz_to_mel(f):
    """Hz → Mel 変換"""
    return 2595 * np.log10(1 + f / 700)

def mel_to_hz(m):
    """Mel → Hz 変換"""
    return 700 * (10 ** (m / 2595) - 1)

def make_mel_filterbank(n_filters=26, n_fft=512, sr=16000, f_min=0, f_max=None):
    """
    メルフィルタバンクを生成する
    Parameters:
        n_filters: フィルタ数
        n_fft: FFT長
        sr: サンプリングレート
        f_min, f_max: 分析する周波数範囲
    Returns:
        filterbank: shape (n_filters, n_fft//2 + 1)
    """
    if f_max is None:
        f_max = sr / 2

    n_freqs = n_fft // 2 + 1
    freqs = np.fft.rfftfreq(n_fft, d=1 / sr)  # 奇数のFFT長にも正しく対応

    # メル尺度でのフィルタ中心点(n_filters + 2個:両端含む)
    mel_min = hz_to_mel(f_min)
    mel_max = hz_to_mel(f_max)
    mel_centers = np.linspace(mel_min, mel_max, n_filters + 2)
    hz_centers  = mel_to_hz(mel_centers)  # 対応するHzの位置

    filterbank = np.zeros((n_filters, n_freqs))
    for k in range(n_filters):
        f_left   = hz_centers[k]
        f_center = hz_centers[k + 1]
        f_right  = hz_centers[k + 2]
        # 三角フィルタの計算
        for i, f in enumerate(freqs):
            if f_left <= f <= f_center:
                filterbank[k, i] = (f - f_left) / (f_center - f_left)
            elif f_center < f <= f_right:
                filterbank[k, i] = (f_right - f) / (f_right - f_center)
    return filterbank, freqs, hz_centers

# フィルタバンクを生成・可視化
fb, freqs, centers = make_mel_filterbank(n_filters=26, n_fft=512, sr=16000)

plt.figure(figsize=(12, 5))
for k in range(fb.shape[0]):
    plt.plot(freqs, fb[k], alpha=0.7)
plt.xlabel("周波数 (Hz)", fontsize=12)
plt.ylabel("フィルタの重み", fontsize=12)
plt.title("メルフィルタバンク(26フィルタ、0〜8000Hz)", fontsize=13)
plt.xlim(0, 8000)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

print(f"フィルタバンクの形状: {fb.shape}")
print(f"フィルタ中心周波数 (Hz): {centers[1:-1].round(0)}")

5. ログメルスペクトログラム

なぜ「対数」を取るか

  1. 強弱の広い範囲を圧縮する(聴覚の大きさの知覚との関連はあるが、ラウドネスそのものではない)
  2. 静かな音と大きな音の範囲が非常に広い(dBスケールが必要)
  3. 機械学習モデルが扱いやすい数値範囲になる

計算式

\[ \text{LogMel}[k] = \log\left(\max(S[k], \epsilon)\right) \]
  • \(S[k]\):\(k\)番目のメルフィルタバンクの出力(エネルギー)
  • \(\epsilon\):log(0)を避けるための正の下限(例:\(10^{-10}\))

実習では自然対数の代わりに、power_to_db により \(L[k]=10\log_{10}(\max(S[k],\epsilon)/S_{\mathrm{ref}})\) を使う。対数の底・基準・下限を変えると数値も変わる。既定の top_db=80 は最大値から80 dB下で切り詰める表示・計算上の処理である。3

Pythonでログメルスペクトログラムを描く

# ===== セル3:ログメルスペクトログラムの描画 =====
import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import Audio, display

audio_path = librosa.example("libri1")  # 朗読音声。初回はダウンロード
y, sr = librosa.load(audio_path, sr=16000, mono=True)
display(Audio(y, rate=sr))

# 25 ms窓・10 msホップ。FFT長512は窓長400より長い
n_fft, win_length, hop_length = 512, 400, 160
D = librosa.stft(y, n_fft=n_fft, win_length=win_length,
                 hop_length=hop_length)
S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
S_mel = librosa.feature.melspectrogram(
    y=y, sr=sr, n_fft=n_fft, win_length=win_length,
    hop_length=hop_length, n_mels=128, htk=True, norm=None)
S_mel_db = librosa.power_to_db(S_mel, ref=np.max)

fig, axes = plt.subplots(2, 1, figsize=(12, 8))
img1 = librosa.display.specshow(S_db, sr=sr, hop_length=hop_length,
                               x_axis="time", y_axis="log",
                               ax=axes[0], cmap="magma")
axes[0].set_title("通常スペクトログラム(対数周波数軸・相対dB)")
fig.colorbar(img1, ax=axes[0], format="%+.0f dB")
img2 = librosa.display.specshow(S_mel_db, sr=sr, hop_length=hop_length,
                               x_axis="time", y_axis="mel", htk=True,
                               ax=axes[1], cmap="magma")
axes[1].set_title("ログメルスペクトログラム(HTK・128帯域)")
fig.colorbar(img2, ax=axes[1], format="%+.0f dB")
plt.tight_layout()
plt.show()

6. DCT(離散コサイン変換)の役割

DCTとは

離散コサイン変換(DCT)は、信号をコサイン関数の重ね合わせで表す変換です。

\[ C[n] = \alpha_n \sum_{k=0}^{K-1} L[k] \cos\left(\frac{\pi n (k + 0.5)}{K}\right) \]
  • \(K\):メルフィルタの数
  • \(n\):ケプストラム次数
  • \(C[n]\):\(n\)番目のMFCC係数
  • \(L[k]\):対数メルパワー(実習ではdB値)
  • \(\alpha_0=1/\sqrt K\)、\(\alpha_n=\sqrt{2/K}\;(n>0)\):正規直交DCT-IIの係数(norm='ortho')。正規化の流儀を比べるときは、この係数もそろえる。4

メルフィルタバンクの対数出力にDCTをかける目的

  1. 低次係数の選択:DCT後に一部の係数を残して次元を減らす。DCT自体は、全係数を保てば可逆
  2. 相関の低減:隣接帯域の滑らかな変化を整理しやすい。直交する基底を使っても、データの無相関性・統計的独立性は保証されない
  3. 全体レベルと形状の表現:C[0]は対数メルパワーの平均に比例する。波形の総エネルギーの対数とは別の量

ケプストラム(Cepstrum)

「スペクトルのスペクトル」という概念。

\[ \text{実ケプストラム} = \operatorname{Re}\left\{\text{IFFT}\left(\log\max(|\text{FFT}(x)|,\epsilon)\right)\right\} \]
  • 音源の周期性と声道の滑らかな包絡を分けて考える手がかりになる。常に完全分離できるわけではない
  • MFCCは、メルフィルタバンクで集約した対数値にDCTをかける関連した特徴量。上の実ケプストラムと同じ変換ではない

なぜ低次のDCT係数だけを使うか

DCT係数次数 表すもの
C[0] 対数メルパワーの平均に比例。採用・除外は設定次第
低次係数 メル軸上で緩やかに変わるスペクトル包絡
高次係数 メル軸上のより細かな変化。音韻・話者・雑音など複数の情報を含みうる

「C[1]はF1」のような一対一の対応はない。librosaで n_mfcc=13 とすると、C[0]〜C[12]を含む13係数が返る。13次元が十分かは課題と実験で判断する。

導出②:なぜ「対数をとってから」変換するのか(核心)

有声音の短い区間では、音源 \(e[n]\) が声道のフィルタ \(h[n]\) を通ると近似できる。声道をその区間で線形・時不変とみなせば、畳み込みで書ける。

\[ x[n] \;=\; e[n] * h[n] \]

ここで \( h[n] \) は声道の特性である。畳み込みは、周波数の側で見ると掛け算になる。

\[ |X[k]| \;=\; |E[k]| \cdot |H[k]| \]

知りたいのは、ふつう \( |H[k]| \) のほうである。母音を決めているのはフォルマント、つまり声道の形だからである。ところが観測できるのは積のほうで、掛け算のままでは2つを引き離せない。

ここで対数をとる。

\[ \log|X[k]| \;=\; \log|E[k]| \;+\; \log|H[k]| \]

ゼロでない振幅について、掛け算が足し算になった。 これがケプストラム解析で対数を使う理由の一つである。実装の下限値やメルフィルタでの集約を含めると、この等式がそのまま厳密に成立するわけではない。

足し算にさえなれば、あとは2つの成分の性質の違いで分けられる。周波数軸に沿って見ると、

  • \( \log|E[k]| \) は、有声音ではF0に対応する調波の細かな構造を持つ
  • \( \log|H[k]| \) は、フォルマントの山をつくるゆるやかなうねりである

細かい構造と緩やかな変化の違いを、周波数軸に沿った変換で捉える。実ケプストラムの一例は

\[ c[q] = \operatorname{Re}\left\{\text{IFFT}\left(\log\max(|\text{FFT}(x)|,\epsilon)\right)\right\} \]

と書ける。MFCCではさらにメルフィルタでの集約・DCT・係数の選択が入る。音源と声道の寄与が分かれやすいという見方は役立つが、ピッチとフォルマントの完全な分離を保証するものではない。

ケプストラムという名前

spectrum(スペクトル)の前半を綴りごとひっくり返して cepstrum。周波数軸をさらに変換した先なので、ふつうのスペクトルと取り違えないように作られた語である。横軸の単位も frequency をひっくり返して quefrency と呼ぶ。名前の遊びに見えるが、「もう一段、外側にいる」ことを忘れないための工夫である。

畳み込みが周波数で積になること(畳み込み定理)の証明と、位相まで含めた複素ケプストラムの扱いは、この授業で扱う数理の範囲外である。

使った数学:対数の性質 \( \log(ab)=\log a+\log b \)(数II)。畳み込みが積になるという事実(結果のみ使用)。

導出③:DCTは何をしているのか ―― 低次係数を選ぶ理由と限界

上の式をもう一度見る。

\[ C[n] = \alpha_n \sum_{k=0}^{K-1} L[k]\cos\left(\frac{\pi n(k+0.5)}{K}\right) \]

第4回と同じく内積を使うが、こちらはメル帯域に沿って並んだ対数値 \(L[k]\) と余弦基底の内積である。変換する軸も境界条件もDFTとは異なる。

まず \( n = 0 \) を入れてみる。\( \cos 0 = 1 \) なので

\[ C[0] = \frac{1}{\sqrt K}\sum_{k=0}^{K-1}L[k] = \sqrt K\,\overline L \]

したがってC[0]は、対数メルパワーの平均に比例する。音声のゲインを変えると主にC[0]が変化するが、スペクトルの形にも依存する。C[0]を捨てるか、含めるか、波形の対数エネルギーに置き換えるかは設計上の選択であり、この実習では含める。

次に、なぜ低次だけで足りるのか。メルフィルタバンクは三角フィルタを重ねて並べるので、隣り合うフィルタは同じ周波数成分を共有している。つまり \( \log S[k] \) の隣どうしは強く相関している。

滑らかに変わる列では、情報が低次の余弦成分に集まりやすい。声道の包絡はその例である。ただし、重なったフィルタを使えばどんなデータでもDCT後に無相関になるわけではない。基底の直交と、観測データの統計的な相関は別である。

低次係数だけを残すと、メル軸上の細部を省きながら包絡を近似できる。その代わり、有用な細部も失いうる。13次元は慣用的な設定例であり、数式から「13で十分」と導かれるものではない。 次元数は対象の音声・認識課題・モデルに合わせて評価する。

DCT が「相関を落とす変換として最も良い」といえるのは、対象の相関がある特定の形をしているときに限られる。主成分分析との関係を厳密に述べるにはその条件を確かめる必要があり、この授業で扱う数理の範囲外である。

使った数学:余弦の値 \( \cos 0 = 1 \)、ベクトルの内積と直交(線形代数I)、第4回の導出①。


7. MFCCの計算ステップ全体

MFCC計算のフローチャート

入力:音声波形 x[n]
  │
  ▼
① プリエンファシス(高域強調、採用する場合)
   x'[n] = x[n] - α · x[n-1]   (α ≈ 0.97)
  │
  ▼
② フレーム分割 + 窓掛け(ハニング窓)
   窓長25ms、ホップ10ms(この場合、重複は15ms)
  │
  ▼
③ FFT → パワースペクトル
   |X[k]|²
  │
  ▼
④ メルフィルタバンクを適用
   S[m] = Σ H_m[k] · |X[k]|²
  │
  ▼
⑤ 対数を取る
   L[m] = log max(S[m], ε) など(実習ではdB)
  │
  ▼
⑥ DCTを適用
   C[n] = α_n Σ L[m] · cos(π·n·(m+0.5)/M)
  │
  ▼
出力:MFCC(例:13次元ベクトル)
各フレームに1つ → 全体で (13 × フレーム数) の行列

プリエンファシスは必須ではなく、librosa.feature.mfcc は自動では行わない。本回のコードも省略する。FFTの前には必要に応じてゼロパディングを行い、DCTの後に残す係数を選ぶ。


8. MFCC のPython実装(librosa 使用)

# ===== セル4:同じログメル値からMFCCを計算・可視化 =====
import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import Audio, display

y, sr = librosa.load(librosa.example("libri1"), sr=16000, mono=True)
n_fft, win_length, hop_length = 512, 400, 160
n_mfcc = 13
S_mel = librosa.feature.melspectrogram(
    y=y, sr=sr, n_fft=n_fft, win_length=win_length,
    hop_length=hop_length, n_mels=40, htk=True, norm=None)
# 基準1のdB値。前セルのref=np.maxとはC[0]の値が変わりうる
S_mel_db = librosa.power_to_db(S_mel, ref=1.0)
mfcc = librosa.feature.mfcc(S=S_mel_db, n_mfcc=n_mfcc,
                           dct_type=2, norm="ortho")

print(f"MFCCの形状: {mfcc.shape}(C[0]〜C[12]を含む)")
print(f"窓長: {win_length / sr * 1000:.1f} ms")
print(f"フレーム中心の間隔: {hop_length / sr * 1000:.1f} ms")
print("係数ごとの平均:", mfcc.mean(axis=1).round(2))
print("係数ごとの標準偏差:", mfcc.std(axis=1).round(2))

fig, axes = plt.subplots(3, 1, figsize=(12, 10))
librosa.display.waveshow(y, sr=sr, ax=axes[0])
axes[0].set_title("音声波形")
img1 = librosa.display.specshow(S_mel_db, sr=sr, hop_length=hop_length,
                               x_axis="time", y_axis="mel", htk=True,
                               ax=axes[1], cmap="magma")
axes[1].set_title("MFCCの入力と同じログメル値(HTK・40帯域・基準1)")
fig.colorbar(img1, ax=axes[1], format="%+.0f dB")
img2 = librosa.display.specshow(mfcc, sr=sr, hop_length=hop_length,
                               x_axis="time", ax=axes[2], cmap="coolwarm")
axes[2].set(title="MFCC(13次元)", ylabel="ケプストラム係数の次数")
fig.colorbar(img2, ax=axes[2])
plt.tight_layout()
plt.show()

9. デルタ・デルタデルタ特徴量

1フレームの特徴量に、時間変化を明示的に加える

1フレームのMFCCは短い分析窓のスペクトル形状を表し、音声全体はその系列になる。デルタは隣接フレームの変化を特徴量として明示的に追加する方法である。MFCCの系列をRNNやTransformerなどへ渡し、時間変化をモデルに学習させる構成もある。

「あ → い」の変化中に何が起きているか → 微分で捉える

デルタ(Δ)特徴量:時間変化を局所的に近似

\[ \Delta C_t[n] = \frac{\sum_{k=1}^{K} k \left(C_{t+k}[n] - C_{t-k}[n]\right)}{2 \sum_{k=1}^{K} k^2} \]

例えば \(K=2\) なら前後2フレームを使う。上式はフレーム番号に対する傾きである。秒あたりの変化にするなら、ホップの秒数で割る。librosaではSavitzky–Golayフィルタで微分を近似し、order=2 は二階微分の直接推定で、delta(delta(mfcc)) と常に一致するわけではない。端の処理方法も指定する。5

  • MFCC単体:スペクトルの形(母音の種類など)
  • ΔMFCC:スペクトルの変化(音素間の遷移)
  • ΔΔMFCC:変化の変化(加速度的な変化)

一般的な次元数の組み合わせ

構成 次元数 用途
MFCC 13 最小構成
MFCC + Δ 26 標準的
MFCC + Δ + ΔΔ 39 音声認識の定番
MFCCを増やす 20, 40 スペクトルの細部を増やす設定例。性能は評価して判断
# ===== セル5:デルタ・デルタデルタ特徴量 =====
import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt

y, sr = librosa.load(librosa.example("libri1"), sr=16000, mono=True)
hop_length = 160
mfcc = librosa.feature.mfcc(
    y=y, sr=sr, n_mfcc=13, n_fft=512, win_length=400,
    hop_length=hop_length, n_mels=40, htk=True, mel_norm=None)
# 単位は1フレームあたり。端では最近傍値を延長する
mfcc_d = librosa.feature.delta(mfcc, width=5, order=1, mode="nearest")
# 2階微分を局所多項式から直接推定(1階を2回かける方式とは別)
mfcc_dd = librosa.feature.delta(mfcc, width=5, order=2, mode="nearest")
mfcc_full = np.concatenate([mfcc, mfcc_d, mfcc_dd], axis=0)
print(f"連結後の形状: {mfcc_full.shape}(39次元 × フレーム数)")

fig, axes = plt.subplots(3, 1, figsize=(12, 10))
for ax, feat, title in zip(
        axes, [mfcc, mfcc_d, mfcc_dd],
        ["MFCC", "ΔMFCC(時間変化の近似)", "ΔΔMFCC(二次の変化)"]):
    img = librosa.display.specshow(feat, sr=sr, hop_length=hop_length,
                                   x_axis="time", ax=ax, cmap="coolwarm")
    ax.set(title=title, ylabel="係数次数")
    fig.colorbar(img, ax=ax)
plt.tight_layout()
plt.show()

10. MFCCの実用的な次元数と設定

次元数の選択

n_mfcc n_mels 設定の意味・例
13 26〜40 従来の音声認識(GMM-HMM)
20 40 C[0]〜C[19]を残す例
40 80〜128 より多くのケプストラム係数を残す例

WhisperはMFCCではなく対数メルスペクトログラム、wav2vec 2.0は波形を入力する。 Whisperのメル帯域数はモデルによって80または128であり、「80次元MFCCを使う」と説明しない。6

その他の重要な設定パラメータ

パラメータ 典型値 意味
sr 16000, 22050 サンプリングレート(Hz)
n_fft 512, 1024, 2048 FFT長。窓長と別に設定できる
win_length 400(16 kHz時) 窓長25 msの例
hop_length 256, 512 フレームシフト幅
fmin 0, 80 分析する最低周波数
fmax sr/2以下 フィルタの上限。8,000 Hzならsrは16,000 Hz以上が必要
window 'hann' 窓関数の種類
# ===== セル6:同じログメル入力でMFCC次元数を比較 =====
import librosa
import librosa.display
import matplotlib.pyplot as plt

y, sr = librosa.load(librosa.example("libri1"), sr=16000, mono=True)
hop_length = 160
S_mel = librosa.feature.melspectrogram(
    y=y, sr=sr, n_fft=512, win_length=400, hop_length=hop_length,
    n_mels=80, htk=True, norm=None)
S_db = librosa.power_to_db(S_mel, ref=1.0)
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
for ax, n_mfcc in zip(axes, [13, 20, 40]):
    mfcc = librosa.feature.mfcc(S=S_db, n_mfcc=n_mfcc, norm="ortho")
    img = librosa.display.specshow(mfcc, sr=sr, hop_length=hop_length,
                                   x_axis="time", ax=ax, cmap="coolwarm")
    ax.set(title=f"MFCC({n_mfcc}次元)", ylabel="係数次数")
    fig.colorbar(img, ax=ax)
plt.tight_layout()
plt.show()

テスト問題(全5問、各1点、Google Formsで回答)


問1

メル尺度(Mel scale)について正しい説明はどれか。

A. 全周波数帯域を等間隔の線形スケールで表したもの
B. 人間の聴覚の非線形性に基づき、低音域を圧縮し高音域を拡張したもの
C. 人間の聴覚特性を反映し、低音域を細かく、高音域を粗く扱うもの
D. 音声の基本周波数(F0)を自動検出するための尺度

正解:C

解説:
メル尺度は音の高さの知覚に基づく非線形の尺度です。メル軸で等間隔にフィルタを置くと、Hz軸上では一般に低域で狭く、高域で広くなります。これは周波数差が聞き取れるかの閾値や、内耳の分解能をそのまま表したものではありません。

Bは「高音域を拡張」が逆の説明です(実際は高音域が圧縮・粗く扱われます)。Aは線形スケールなのでメル尺度の特徴に反します。Dはピッチ検出の話であり、メル尺度の目的ではありません。


問2

MFCCの計算ステップとして、正しい順番はどれか。

A. FFT → メルフィルタバンク → 対数 → DCT
B. DCT → FFT → メルフィルタバンク → 対数
C. メルフィルタバンク → FFT → 対数 → DCT
D. FFT → 対数 → メルフィルタバンク → DCT

正解:A

解説:
MFCCの計算順序は以下の通りです:

  1. FFT(高速フーリエ変換):時間領域の信号を周波数領域に変換する
  2. メルフィルタバンク:FFTから求めたパワースペクトルを帯域ごとに重み付きで集約する
  3. 対数(log):下限を設けて対数を取り、値の範囲を圧縮する
  4. DCT(離散コサイン変換)と係数の選択:帯域方向を変換し、残す係数を選んで次元を減らす

この順序の覚え方:「フ(FFT)メ(メルフィルタ)対(対数)D(DCT)」


問3

librosaで13次元のMFCCを計算するコードとして正しいものはどれか。

A. mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
B. mfcc = librosa.stft(y, n_fft=13)
C. mfcc = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=13)
D. mfcc = librosa.feature.mfcc(y=y, sr=sr, n_fft=13)

正解:A

解説:
librosa.feature.mfcc() がMFCCを計算する関数で、n_mfcc パラメータで次元数(MFCC係数の数)を指定します。

  • B:librosa.stft() はSTFTを計算する関数(MFCCではない)
  • C:librosa.feature.melspectrogram() はメルスペクトログラムを返す(MFCCではない)
  • D:n_fft=13 はFFTの長さを13にすることを意味し、MFCCの次元数ではない(かつ13というFFT長は実用上使わない)

問4

デルタ(Δ)MFCC特徴量について正しい説明はどれか。

A. MFCCの次元数を増やして、より多くの周波数帯域を分析したもの
B. MFCC係数の時間的な変化(1次差分)を表す特徴量
C. DCT係数をさらにDCTにかけた2段階変換の結果
D. ノイズ除去のためにMFCCを平滑化したもの

正解:B

解説:
デルタ(Δ)MFCCは、MFCCの時間方向の変化率の近似です。単純な隣接差だけでなく、前後数フレームから傾きを推定します:

\[\Delta C_t = \frac{\sum_{k=1}^{K} k (C_{t+k} - C_{t-k})}{2\sum_{k=1}^{K}k^2}\]

1フレームのMFCCにΔMFCCを加えると、近傍での変化を明示的に表せます。MFCC(13) + ΔMFCC(13) + ΔΔMFCC(13) = 39次元は従来の音声認識で使われる構成例です。MFCC系列を時系列モデルで処理する方法もあり、デルタが常に必須という意味ではありません。


問5

同じメルフィルタバンクを使い、MFCCでは一部の低次係数だけを残す場合、ログメルスペクトログラムの特徴として適切なものはどれか。

A. DCTによる次元削減により、MFCCより情報量が少なくなる
B. DCT後の高次係数を切り捨てないため、メル帯域に含まれる細かな情報を保持できる
C. メルフィルタバンクを使わないため、線形の周波数分解能が保たれる
D. ログメルスペクトログラムはリアルタイム計算が不可能なため、バッチ処理専用

正解:B

解説:
ログメルスペクトログラムは、DCT後に一部の係数を切り捨てる処理を行いません。全係数を保持したDCT自体は可逆であり、DCTをするだけで必ず情報が減るわけではありません。

  • MFCC:メルフィルタバンクの対数出力(例:40次元)→ DCT → 低次の係数のみ(例:13次元)を使用
  • ログメルスペクトログラム:同じメルフィルタバンクの対数出力(例:40次元)を使用

ログメル表現でも、元の波形から位相や周波数の細部は失われています。そのうえで、低次MFCCへの切り詰めによる情報損失を加えないことが特徴です。Whisperはログメルを使い、wav2vec 2.0は生波形を使います。どちらが有効かは、モデルや学習条件も含めて評価します。

Aは逆の説明。Cはメルフィルタバンクを使うので誤り。Dは誤り(リアルタイム計算可能)。


まとめと次回予告

本回のまとめ

概念 ポイント
メル尺度 人間の聴覚特性(低音を細かく、高音を粗く)を数値化
メルフィルタバンク 三角形フィルタをメル尺度で等間隔に配置
ログメルスペクトログラム メルエネルギーの対数。深層学習で多用
DCT メル帯域方向の変換。全係数なら可逆、低次係数の選択で次元削減
MFCC 音声認識の定番特徴量。13〜40次元
Δ・ΔΔ特徴量 時間変化を特徴量に加える。39次元が定番

次回(第6回・オンライン同期、録画後日公開)予告

ピッチ・エネルギー・ゼロ交差など、スペクトルとは異なるアプローチで 音声の特性を捉える手法を学びます。音声活動検出(VAD)への応用も!

参考文献・資料

  • Davis, S. B. & Mermelstein, P. (1980). Comparison of parametric representations for monosyllabic word recognition in continuously spoken sentences. IEEE Transactions on Acoustics, Speech, and Signal Processing, 28(4), 357–366. DOI。
  • librosa 0.11.0, feature.mfcc。
  • Huang, X. et al. (2001). Spoken Language Processing. Prentice Hall.

内容確認・訂正:2026年10月20日。


  1. Slaney, M. (1998), Auditory Toolbox(MFCCと蝸牛モデルは別の表現); librosaのメル変換の説明。 ↩↩

  2. librosa 0.11.0, hz_to_mel、filters.mel(htkとフィルタの正規化)。 ↩

  3. librosa 0.11.0, power_to_db。 ↩

  4. SciPy, dct(DCT-IIと正規直交化)。 ↩

  5. librosa 0.11.0, feature.delta(Savitzky–Golay法・微分次数・端点処理)。 ↩

  6. Radford et al. (2023), Whisper原論文と公式実装; Baevski et al. (2020), wav2vec 2.0原論文。モデル別の入力設定は第8回も参照。 ↩