音声認識メモ(HMM)その3(HTK HCopyコマンド(3))
"もしもし"というWAVE音声データをプロットしてみると以下のとおりであった。
- 発声の時間は2秒間で、サンプリングレートは16kHz(出力結果として得られるサンプル数は32,000)
- 1サンプルは2バイト(C言語でのshort(−32,768〜 32,767))の範囲の値を取る
- 横軸がサンプリング番号(1〜32000)、縦軸が10進数に変換した波形データ
このデータから、1フレーム分(400サンプル、25ms)を切り出すと以下のとおり。
フレーム番号:1(サンプル番号1〜400)
フレーム番号:2(サンプル番号161〜560)
フレーム番号「1」のデータに対して、MFCCが計算されるまでをトレースしてみる。
※以降の表は、「HTK」のプログラム内部で保持する配列データを表したもの(上段が配列のインデックス、下段が配列の値)
フレーム番号:1(サンプル番号1〜400)の配列
プリエンファシス係数を「0.97」とした場合の変換後の状態
変換は、隣り合う前後の差を求めたもの(↓)
波形の勾配が大きい(=高周波数)ほど値の絶対値が大きくなる
s[400] = s[400] - (s[399] * 0.97) s[1] = s[1] * (1.0 - 0.97)
フレーム番号:1(サンプル番号1〜400)(変換後)
続けて、ハミング窓を掛けた後の状態
ハミング窓は、中央の値を強調したもの(↓)
続けてDFT(離散フーリエ変換)を適用する。
適用する前の状態(↓)、DFTのサイズは「512」、401〜512は0パディング
適用した後の状態(↓)
ソース上、indexが2の時は「0」をセットしている。
続けて、メルフィルタバンクを適用する。
チャネル数はパラメータ"NUMCHANS"で指定する(ここでは「24」)
ソース部分は以下のとおり(HSigP.cのWave2FBank関数より抜粋し加工)
/* Fill filterbank channels */ float real; /* real parts */ float imag; /* imag parts */ float amplitude; /* amplitude of k'th fft channel */ /* fill bins */ /* info.klo:2, info.khi:256 */ for (k = info.klo; k <= info.khi; k++) { real = fft[2*k-1]; imag = fft[2*k]; amplitude = sqrt(real*real + imag*imag); bin = info.loChan[k]; /* bin range(0-24) */ /* bin: 0 k: 2 -> 3 bin: 1 k: 4 -> 6 bin: 2 k: 7 -> 8 bin: 3 k: 9 -> 11 bin: 4 k: 12 -> 15 bin: 5 k: 16 -> 19 ... bin: 24 k:231 -> 256 */ /* apply lower channel weights */ /* range: 0 < weight < 1 */ amplitude2 = amplitude * info.loWeight[k]; if (bin > 0){ fbank[bin] += amplitude2; } /* numChans:24 */ if (bin < info.numChans){ fbank[bin+1] += amplitude - amplitude2; } }
続けて、対数をとる。
対数を取ることで、信号が掛け算から足し算になり、分離できる。
続けて、離散コサイン変換(Discrete Cosine Transform)により、Cepstrum領域に移す。
使う式は以下。
http://kom.aau.dk/~tb/kurser/HTKBook/node64_id.html
パラメータ"NUMCEPS"で指定した数の係数を取り出す。
ここでは「12」とする。
「N」はフィルタバンクの次元数(パラメータ"NUMCHANS"で指定した「24」)、「m」はフィルタバンクの配列
変数「i」は「1」から始まっていることに注目。
int N = 24; for (i=1; i<=12; i++) { c[i] = 0.0; for (j=1; j<=N; j++){ c[i] += fbank[j] * cos(i * PI / (float)N * (j-0.5)); } c[i] *= sqrt(2.0/(float)N); }
計算後の状態(↓)
続けて、重み付けする。
使用する重み付け係数(Cepstral liftering coef)は以下の式のカッコ部分。
「n」がCepstral領域のindex(1から12)に該当する。
http://www1.icsi.berkeley.edu/Speech/docs/HTKBook/node283_id.html
L(パラメータCEPLIFTER)をデフォルトの「22」とした場合、indexが"11"でピークになる。
int L = 22; cepWin[n] = 1.0 + ( (L/2) * sin(n * (PI/L)) );
重み付けをした結果
これがMFCC1次の1〜12のデータに該当する(↓)