[Python] 転職のための音声認識入門 ② 可視化・特徴量抽出 編

# はじめに
### このパートの目的
音声信号に対する Python を用いた特徴量抽出処理を勉強しました。
太字にフォーカスし、それぞれの概要・用語メモ・Python の常套句をメモします。他は Google 先生に尋ねれば十分でした。
- **データセットの準備**
- 音声信号読み込み・簡単な可視化
- 周波数解析
- スペクトログラム
- **対数メルフィルタバンク特徴量**
- **メル周波数ケプストラム特徴量**

またこのパートは続編です。
前回はこちら

### 教材
詳細はこちらを参考ください。

https://book.impress.co.jp/books/1120101083


# データセットの準備
詳細は参考文献を参照ください。
- VSCode と Annaconda 環境の構築（手こずった）と sox のインストール。こちらの記事に助けられました↓

https://qiita.com/lunar777s/items/c0ce5821aacf493eedb2

- https://github.com/ry-takashima/python_asr をローカルにクローン
- 00prepare/00download_data.py で音声・ラベルデータをダウンロード。データセット "BASIC 5000" を使う。
- 00prepare/01prepare_wav.py で音声データをダウンサンプリング（一般的らしい）
- 00prepare/02prepare_label.py で音声データ名とテキスト・ひらがな列・音素列に対応付け
- 00prepare/03subset_data.py でデータの分割割り当て
    - 評価: 0001~0250
    - 開発: 0251~0500 
    - 学習(大データ量): 0501~5000
    - 学習(小データ量): 0501~1500 動作確認用

# 音声ファイル の 読み込み と 表示
### 音声ファイルのパラメーター
- 圧縮・非圧縮
    - 音声認識には非圧縮音声ファイルを用いるのが一般的
    - 非圧縮: wav など
    - 圧縮: mp3 など
- チャネル数: マイク数
    - 1: モノラル
    - 2: ステレオ
- サンプリング
    - 音声は時間連続信号
    - 時間連続信号を時間離散信号にすること
    - サンプリング周期: サンプリングする時間周期
    - **サンプリング周波数**: サンプリングする時間周期の逆数 (Hz)
- 量子化
    - 音声は音圧連続信号
    - 音圧連続信号を音圧離散信号にすること
    - **サンプルサイズ**・ビット深度: 最終量子化ビット総数
        - 一般的に2のべき乗 (ビット演算なので)
### Python 主要モジュールと常套句
メモの書き方が気持ち悪いですがご容赦ください。
- モジュール wave: wav データを読み込むためのモジュール
    - `with wave.open(wavファイル名/パス) as wav`: wav ファイル読み込みの with 句
    - `wav.getframerate()`: wav ファイルのサンプリング周波数取得
    - `wav.getsampwith()`: wav ファイルのサンプルサイズ取得
    - `wav.getnchannels()`: wav ファイルのチャネル数の取得
    - `wav.getnframes()`: wav ファイルのサンプル数（長さ）を取得
    - `wav.readframes(num_samples)`: wav データの読み込み、バイナリ値
    - `np.frombuffer(waveform, dtype=np.int16)`: バイナリ値列を整数変換
- モジュール matplotlib (as plt): おなじみグラフ描画モジュール
    - `np.arange(num_samples)/sampling_freq`: プロット用横軸
    - `plt.figure(figsize=(10, 4))`: 描画領域作成
    - `plt.plot(x軸, 波形データ)`: 実プロット
    - `plt.xlabel('time')`/`plt.ylabel('value')`: ラベル作成
    - `plt.xlim([0, num_samples / sampling_freq])`: x軸表示制限。サンプル数÷サンプリング周波数(1/s)=時間(s)
    -  `plt.savefig('./hoge.png')`

# 周波数解析
### 主要語句
- フーリエ解析: 音の高さごとのパワーの解析に用いられる。
    - ざっくり説明: あらゆる信号はあらゆる正弦波の重ね合わせで表現できるよ、各周波数の振幅・位相を解析するのがフーリエ解析だよ。離散信号に対して高速化を図ったフーリエ解析が FFT (Fast Fourier Transform) だよ。
- 振幅スペクトル: 横軸周波数 / 縦軸振幅
    - フロアリング処理: 通常は縦軸を対数にする。その際に非ゼロにするために微小な数をオフセットさせること
    - サンプリング定理: あるサンプリング周波数で解析できる周波数成分は、そのサンプリング周波数の半分までという定理
    - ナイキスト周波数: サンプリング周波数の半分。FFT の解析上限周波数。
- パワースペクトル: 振幅スペクトルの二乗
- 位相スペクトル: 横軸周波数 / 縦軸移送 (-pi, pi]など
    - 音声認識ではあまり使用されない

### Python 主要モジュールと常套句
- `解析開始インデックス = np.int(解析開始時刻 * サンプリング周波数)`
- `解析対象フレーム = 波形[解析開始インデックス : 解析開始インデックス + FFT サイズ]`
- `スペクトル = np.fft.fft(解析対象フレーム)`: FFT

```python
振幅スペクトル = np.abs(スペクトル) 
振幅スペクトル = 振幅スペクトル[ : np.int(fft_size/2) + 1]
振幅スペクトル(対数) = np.log(振幅スペクトル + 1E-7) # 1E-7 はフロアリング処理
```

# 短時間フーリエ変換によるスペクトログラムの表示
- 短時間フーリエ変換: 音声波形を時間で分割しそれぞれ FFT し時間軸に並べる変換。
- スペクトログラム: 短時間フーリエ変換によって得られる図。横軸が時間、縦軸が周波数
- フレーム: 短時間フーリエ変換で分割に使う時間枠
    - フレームサイズ: フレームの長さ
    - フレーム間隔・フレームシフト: フレームの間隔
    - オーバーラップ: あるフレームと次のフレームをどれだけ重ねるか
        - どれくらいオーバーラップさせる？
        - > 通常のハニング窓の設定では、50％ 程度のオーバラップが適当です。

https://www.onosokki.co.jp/HP-WK/c_support/faq/fft_common/fft_spectrum_12.htm

一方 MATLAB では 75% が規定値となっています。

https://jp.mathworks.com/help/signal/ref/stft.html

- 窓関数: オーバーラップするとフレームのみを並べた時に不連続が生じ、FFT に影響を及ぼす。それを軽減するために開始点と終点を0にして影響を低減しつつ、元の信号の特徴もある程度残すことができる関数。
    - ハミング窓:
    ```math
    w(n) = 0.54-0.46\cos(\frac{2\pi n}{N-1})
    ```
    - 窓関数はほかにもあるよ。選択基準について↓
    - > 窓関数の選択は簡単な作業ではありません。アプリケーションによって、適切な窓関数は異なるからです。窓関数を選択するには、信号の周波数成分を推定する必要があります。
    - > 信号に、解析対象となる周波数から程遠い、強い障害のある周波数成分が含まれている場合、高いサイドローブのロールオフ率がある平滑化窓関数を選択します。
    - > 信号に、解析対象となる周波数に近い、強い障害のある信号が含まれている場合、最低レベルのサイドローブが小さい窓関数を選択します。
    - > 解析対象となる周波数に、非常に近接した2つ以上の信号が含まれている場合、スペクトル分解能が重要です。この場合、非常に狭いメインローブのある平滑化窓関数を選択します。
    - > 単一の周波数成分の振幅確度が、与えられた周波数帯域成分の正確な位置よりも重要な場合、メインローブの幅が大きい窓関数を選択します。
    - > 信号スペクトルが、周波数成分でやや平坦あるいは広帯域である場合、均一窓 (窓なし窓) を使用します。
    - > 一般に、ハニング (ハン) 窓は95%のケースで十分です。周波数分解能に優れており、スペクトル漏れは軽減されています。信号の特性が不明で、平滑化処理を適用したい場合、ハニング窓から始めます。

https://www.ni.com/ja/shop/data-acquisition/measurement-fundamentals-main-page/analog-fundamentals/understanding-ffts-and-windowing.html


### Python 実装の流れ
1. 音声ファイル読み込み
1. フレームサイズの作成
1. FFT 対象サンプル数をフレームサイズに合わせる
1. 総フレーム数を計算
1. スペクトログラム用の行列を用意
1. 各フレームの振幅スペクトルの計算
    1. 各フレームの波形切り出し
    1. 窓関数をかける（要素積）
    1. FFT: 対数振幅スペクトル計算
    1. スペクトログラムの行列に格納
1. プロット


# 対数メルフィルタバンク特徴量
以下について説明
- スペクトログラムを計算するための前処理
- スペクトログラムをさらに変形して得る対数メルフィルタバンク特徴量

### ディザリング処理
無音に近い部分を含む音声波形は対数を計算するときに負の無限大に発散する恐れがある。
それを回避するために加える、結果に影響しない程度のとても小さなノイズのこと。
```math
dither(x(n)) = x(n)+2Dd(n)-D
```
$d(n)$: [0, 1) 内の一様な乱数
すなわち、$2Dd(n)-D$ は [-D, D) の一様乱数。

### 直流成分除去
収録ノイズなどの影響によるオフセットを取り除くこと。
自然的には振幅値の平均は0に近いはず。
具体的には、フレームごとに振幅の平均値を引く。
```math
dc_cut(x(n))=x(n)-\frac{1}{N}\sum_{n=0}^{N-1}x(n)
```

### 高域協調（プリエンファシス）
口元からマイクまでに音声は高周波ほど大きく減衰する。
この高周波分の減衰を補うこと。
畳み込みフィルタで実装される。
```math
\begin{align}
pre\_emphasis(x(n))&=\sum_{\tau=0}^{N-1}h(\tau)x(n-\tau) \\
&= x(n)-\alpha x(n-1)
\end{align}
```

### 次元
- 多変量データ: 複数の値によって構成されるデータ。ベクトル。
- 次元数: 多変量データの大きさ。ベクトルの長さ。

### フィルタバンク分析
- 振幅スペクトルやパワースペクトルの次元数を削減する方法の一つ。
- 一定周波数間隔ごとに一定範囲の周波数成分を集約する。
- フィルタバンク: バンドパスフィルタ群。多くの場合は三角形のバンドパスフィルタを用いる。
- フィルタバンク特徴量: フィルタバンクに含まれるフィルタたちによって畳み込まれたスペクトル群。

### 対数メルフィルタバンク特徴量 (FBANK)
- メルフィルタバンク: 人間の聴覚特性（例えばメル尺度）に基づいた周波数間隔のバンドパスフィルタバンク。
- メル尺度: ヒトの聴覚特性モデルの一つ。O'Shaughnessy 1987 によるモデルは以下の通り。
    ```math
    mel(f)=1127.9 \log (\frac{f}{700}+1)
    ```
    - 高い音ほど周波数変化に鈍感 → 荒く集約してしまってよい
- 対数メルフィルタバンク特徴量: 対数振幅スペクトルに対してメルフィルタバンクを畳み込み、対数をとったもの。


# メル周波数ケプストラム特徴量
### ケプストラム分析
- 発声 = 声帯による共振振動 (声帯振動) × 声道 (口腔・鼻腔・…) の共振特性 (フィルタ) 
- フォルマント: 声道の共振特性が持つ共振周波数。複数あり、間を緩やかなカーブを描く。
- ケプストラム分析: 音声のパワースペクトラムから声道の共振特性を抽出する方法の一つ。

### ケプストラム
- 対数パワースペクトルを信号としてとらえ、IFFT (逆フーリエ変換) を施したもの
    - FFT でも IFFT でも DCT (離散コサイン変換) でも、結果に変わりはない。
- 横軸はケフレンシーと呼ばれる。単位は秒に等しい。
- 低ケフレンシー成分 = パワースペクトルの包絡成分 = 声道共振特性 = フォルマント
- 高ケフレンシー成分 = パワースペクトルの微細成分 = 声帯振動 = 基本周波数
- つまり、低ケフレンシ―部分を取り出せばフォルマントを得ることができる
- リフター: ケプストラム領域でのフィルターのこと。

### メル周波数ケプストラム特徴量 (Mel-Freqency Cepstrum Coeeficient; MFCC)
- 聴覚だけでなく、発声の特徴も考慮してさらに情報を圧縮した特徴量。
- メルフィルタバンクにより圧縮されたパワースペクトル情報に対して、ケプストラム分析を施し包絡線のみを得る

### 特徴量の平均と標準偏差の計算
GMM-HMM や ディープニューラルネットワークの学習において、平均や標準偏差が利用される。

# 次回
DP マッチング


# 参考文献

https://setoti.hatenablog.com/entry/cepstrum

https://www.onosokki.co.jp/HP-WK/eMM_back/emm185.pdf

https://www.jstage.jst.go.jp/article/jsmbe/57/2-3/57_75/_pdf/-char/ja