TensorFlowメモ(Simple Audio Recognition)
TensorFlowのチュートリアル「Simple Audio Recognition」を試してみる。
docs/audio_recognition.md at master · tensorflow/docs · GitHub
ソースコード
tensorflow/tensorflow/examples/speech_commands at master · tensorflow/tensorflow · GitHubどのような処理か
Tensorflowのモデルとしては、数字認識「MNIST」と同じく「分類」(classify)問題を扱う。
クラス数は以下の「12」個。
- silence
- unknown word
- "yes"
- "no"
- "up"
- "down"
- "left"
- "right"
- "on"
- "off"
- "stop"
- "go"
"yes"から"go"までが識別したい単語。
[silence]は無音(音が無い状態)、[unknown word]は未知語を表す。
[unknown word](未知語)は、他の11個のいずれにも当てはまらない音声が該当する。
デフォルトの音声データセットを見てみると、"bird"だったり、"happy"といった音が入っていて、これらは[unknown word]としてクラス分けされる。
音声データセットはどんなもの?
https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.02.tar.gz
音声データは「one second audio clip」、1秒間のオーディオクリップ(wavファイル)
試しに、データセットの中の一つをSoX(Sound eXchange)コマンドを使って確認してみる。
% soxi speech_dataset/happy/00970ce1_nohash_0.wav Channels : 1 Sample Rate : 16000 Precision : 16-bit Duration : 00:00:00.98 = 15701 samples ~ 73.5984 CDDA sectors File Size : 31.4k Bit Rate : 256k Sample Encoding: 16-bit Signed Integer PCM
チャンネル数は「1」(モノラル)、サンプリング周波数は「16000Hz」、これを16bit符号有り整数でデジタル化している。
音声ファイルについては、単純な音を作ってみると理解しやすいかもしれない。
音を作る - ichou1のブログ
音声データをTensorflowのインプットとして整える
サンプリング周波数が「16000」(16k)なので、1秒(1000ミリ秒)あたり16000個のサンプル数値が取れる。ここから、一定の固まり(「frame」と呼ぶ)で切り出す。
切り出す際は、少しずつズラしながら切り出す。
ソース中に「window_size_samples」という数値が出てくるが、これは切り出す量を表している。
デフォルトは「480」個、時間にすると「30」ミリ秒。
また、ソース中に出てくる「window_stride_samples」は、ズラす量を表している。
デフォルトは「160」個、時間にすると「10」ミリ秒。
上の図を時間軸で表すと以下のようになる。
「98」個のフレームが得られる。
このまま扱うことはせず、これを特徴量に置き換える。
デフォルトでは「MFCC」を使う。
音というのはいくつかの「純音」に分解できるので、その音を表す特徴的な純音の周波数を一定の数、抽出したもの。
抽出する数のことを"次元"と呼び、ソース中に出てくる「fingerprint_width」の「40」という数値は次元を意味する。
音声認識メモ(HMM)その2(HTK HCopyコマンド(2)) - ichou1のブログ
最終的に、インプットのTensorは以下の形になる。
# Input Tensor shape (batch_size, 98, 40 , 1)
Tensorflowモデルを組み立てる
今回、試した環境のバージョンは「1.14」
% python -c 'import tensorflow as tf; print(tf.__version__)' 1.14.0
モデルはいくつかの種類が用意されている。
- single_fc
- conv
- low_latency_conv
- low_latency_svdf
- tiny_conv
- tiny_embedding_conv
今回はデフォルトの「conv」について見てみる。
ざっくりとした構成は以下のとおり。
- 畳み込み
- MAXプーリング
- 畳み込み
- 乗算(matmul)
「MNIST」とそう変わらないので、要点のみ書く。
1. 畳み込み層(1番目)
フィルタの形状
# (filter_height, filter_width, in_channels, out_channels) (20, 8, 1, 64)
図にすると以下のとおり。縦軸が時間軸に該当する。
strideとpaddingの形状。
strides=[1, 1, 1, 1] padding='SAME'
このレイヤの後に活性化関数(ReLU)とドロップアウト。
2. MAXプーリング層
kernelとstrideの形状
ksize=[1, 2, 2, 1] strides=[1, 2, 2, 1]
プーリング層による処理が終わった時点でのTensorの形状は以下のとおりとなる。
# Output Tensor shape (batch_size, 49, 20, 64)
3. 畳み込み層(2番目)
フィルタの形状
# (filter_height, filter_width, in_channels, out_channels) (10, 4, 64, 64)
strideとpaddingの形状。
strides=[1, 1, 1, 1] padding='SAME'
このレイヤの後に活性化関数(ReLU)とドロップアウト。
4. matmul
重み行列の掛け算。活性化関数は通さずに終わる。
以下はソースの抜粋。
def create_conv_model(fingerprint_input, model_settings, is_training): ... final_fc = tf.matmul(flattened_second_conv, final_fc_weights) + final_fc_bias return final_fc
重みパラメータの形状
# 49 * 20 * 64 = 62720 (62720, 12)
「12」はラベルの数
損失関数に「tf.losses.sparse_softmax_cross_entropy」をセットして、18000ステップ、トレーニングする。
optimizerのデフォルトは「GradientDescentOptimizer」
終わりに
コマンド用の単語音声を想定しているようなので、音を音素に分解したりせず、ひとまとまりとして扱っている。