はじめに
reazon Speech espnet版 ver2.0を使う機会があり、入力から処理の流れに疑問を持った。
しかし私は分野外の大学生。そこで、このモデルの入力部分である特徴量抽出についてまとめる。
※私は初学者であるため、ものすごく大雑把に理解することを目的とする。
特徴抽出
早速本題に入る。
ESPnetでのデフォルトの音声入力は以下に従って算出される[1]。
入力音声信号→STFT→パワースペクトル→ログメルフィルタバンク
そこで以下ではSTFTから順にPythonによる視覚化を適宜挟み理解を深めていく。
短時間フーリエ変換(Short-Time Fourier Transfor:STFT)
まずフーリエ変換について抑える。
といってもここではフーリエ変換すれば、時間の関数を周波数の関数に変換できる(=周波数ごとの値が分かる)という知識にとどめる。
そして、実際には音声認識のようなシステムはデジタルの世界であるため、以下の数式で示される離散フーリエ変換(DFT)やこれを高速に計算できる高速フーリエ変換(FFT)を利用する。
$$
X[k] = \sum_{n=0}^{N-1} x[n] e^{\frac{-j2\pi kn}{N}}
$$
「こんにちは」という音声の高速フーリエ変換の結果

時間スケールが周波数スケールに変換された。
※フーリエ変換の結果の複素数の絶対値(=周波数ごとの波の高さ)を示したのが共有グラフである。
しかし困ったことにこれでは何秒にどの周波数の音が含まれていたかという時間の情報が失われる。
数単語程度をパターンマッチングするくらいならそれでもかまわないかもしれないが、連続音声認識ともなるとそれでは難しい。どうすれば時間成分を取り戻すことができるのか?その回答が短時間フーリエ変換である。
短時間フーリエ変換とは、その名の通り信号を定常的(変化なく一定)区間(これをフレームという)に分割して各フレームに対しFFTを実行することで時間ごとの周波数特徴を得る。このとき、フレーム間で情報が途切れることを防ぐために隣接フレームを重ねる(スライド幅やオーバーラップと表現される)いってしまえば力業か?
フレームは信号値をぶつ切りにすると、フレーム間に本来存在しないはずの急激な変化が生まれてしまうため、窓関数をかけることで両端を滑らかに小さくする。
さて、短時間フーリエ変換を次の数式で表すことにする[2]。
$$X(r,k)=∑_{m=0}^{L-1}x[rH+m]w[m]e^{\frac{-j2\pi kn}{N}}$$
H:スライド幅
m:フレーム内での位置(0~L-1)
L:フレーム長
r:フレーム番号
N:FFTのサイズ(例えば16kHz、32msのフレームの場合はN=512ビンという)
これは離散フーリエ変換の式に窓関数w[n]を追加しただけである。
これにより1番目、2番目、、、r番目のフレームに対しての周波数kの情報を保持することが可能となる。
パワースペクトル
フーリエ変換の絶対値の二乗によりパワースペクトルが得られる。同様のサンプルに対するグラフを共有する。FFTの時の違いとして、周波数・時間・信号のエネルギーの3つの情報を確認することができる。
※収録時のノイズ除去の影響か一致部区間の値が0になってしまっている。
$$P[r, k]=|X[r,k]|^2$$

メルフィルタバンク
例のごとくまず大雑把なイメージとして、人の聴覚特性を模倣するために特定の周波数帯に重みをつける処理である。
(補足)
人の耳は特定の周波数帯域の音の大きさを知覚する。
この周波数帯域は低いほど細かく、高いほど広くなる。(低い音は少しの音の高さの違いも感じ取りやすい)

これを再現するためにメルフィルタH[k]を導入する。
つまりl個のフィルタ(上図の三角形一つが1個のフィルタに対応)に対してパワースペクトルに重みづけすればよい。
$$E[r,m]=∑H_m[k]P[r,k]$$
$$H_m:メルフィルタ$$
さて、これの自然対数を取ったものがログメルフィルタバンクである。
補足資料 メルフィルタの導出
- 使用する周波数帯域を決定
サンプリング周波数が16kHzの場合、サンプリング定理により0~8000Hzを扱う。 - 両端をメル尺度に変換
代表的な以下の数式によりメル尺度への変換が可能
$$μ(f)=2595log_{10}(1+\frac{700}{f})$$
f=0-8000Hzの時、μ=0-2840Mel - メル尺度上で等間隔に点を配置
例えば3個のメルフィルタを作る場合、両端を含めて5個の点が必要となる。よって2840を4等分すればよい。
[0, 710, 1420, 2130, 2840]
- 点を周波数に戻す
$$f=700(10^\frac{710}{2595}−1)$$
| Mel | Hz |
| 0 | 0 Hz |
| 710 | 615 Hz |
| 1420 | 1770 Hz |
| 2130 | 3950 Hz |
| 2840 | 8000 Hz |
この時点でメル尺度では均一だった点が周波数尺度では不均一(低周波ほど細かい)になっていることが分かる。 - 周波数をFFTの周波数ビンに対応づける
f=16000,N=512のとき
1ビンあたりの周波数幅は31.25Hz
よって615Hzは
$$k=\frac{615}{31.25}≈20$$
1770Hzは
$$k=\frac{1770}{31.25}≈57$$
同様にして周波数ビンは以下のように示すことができる。
$$[0,20,57,126,256]$$ - 点を頂点として三角形フィルタを作る
3で共有した図の値を4で求めた周波数ビンに置き換える。
なぜこんなに面倒な処理をするのか?
FFTの値は周波数分解能に応じた離散周波数ビン上の値として保持されるため。
参考文献
[1] espnet2.asr.frontend.default.DefaultFrontend
[2]短時間フーリエ変換