ichou1のブログ

主に音声認識、時々、データ分析のことを書く

音声ファイル特徴量変換(その4)STFT(TensorFlow)

TensorFlowで短時間フーリエ変換Short-time Fourier Transform)を求めるには、「tf.signal.stft」関数が提供されている。
https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/signal/stft

音声データは「yes」という一秒間の発話データ。
f:id:ichou1:20200216111412p:plain

「libROSA」パッケージで計算する方法は以下をご参照。
work-in-progress.hatenablog.com

TensorFlow 1.X系のAPIを使ったコード

import tensorflow as tf
from tensorflow.python.ops import io_ops

tf.compat.v1.disable_eager_execution()

# Audio Data
audio_path = 'speech_dataset/yes/0a7c2a8d_nohash_0.wav'

with tf.compat.v1.Session(graph=tf.Graph()) as sess:

    wav_filename_placeholder = tf.compat.v1.placeholder(tf.string, [])
    wav_loader = io_ops.read_file(wav_filename_placeholder)

    # Load
    data, sr = tf.audio.decode_wav(wav_loader,
                                   desired_channels=1)

    # channelの次元を削除
    data_ = tf.squeeze(data)

    # batch_sizeの次元を追加
    data__ = tf.expand_dims(data_, axis=0)

    # Input: A Tensor of [batch_size, num_samples]  
    # mono PCM samples in the range [-1, 1].
    stfts = tf.signal.stft(data__,
                           frame_length=480,
                           frame_step=160,
                           fft_length=512)

    # 振幅を求める
    spectrograms = tf.abs(stfts)

    feature = sess.run(
        spectrograms,
        feed_dict={wav_filename_placeholder: audio_path}
    )

今回、試した環境のバージョンは「1.14」

% python -c 'import tensorflow as tf; print(tf.__version__)'
1.14.0

結果を表示させてみる。

print('feature shape: ', feature.shape) 
print('feature type: ', type(feature))
feature shape:  (1, 98, 257) 
feature type:  <class 'numpy.ndarray'>

Tensorの形状は以下のとおり。

# tf.signal.stft
# Output shape
(batch_size, frame_size, fft_size // 2 + 1)

「libROSA」パッケージを使った場合の形状とは次元の並びが異なる。

# librosa.stft
# Output shape
(fft_size // 2 + 1, frame_size)

また、パディングの設定により、frame数が変わってくる。
「libROSA」パッケージは「パディングあり」(101 frame)、「TensorFlow API」は「パディングなし」(98frame)

プロットしてみる。

import numpy as np
import librosa

# 「batch_size」の次元を減らす
feature_ = np.squeeze(feature, 0)

# 次元を入れ替える (frame, DFT_index) --> (DFT_index, frame)
amp = tensor_.transpose(1, 0)

# 振幅をデシベルに変換(基準値は「振幅の最大値」)
amp_db = librosa.amplitude_to_db(amp, ref=np.max)

# plot
import matplotlib.pyplot as plt
import librosa.display

librosa.display.specshow(amp_db,
                         sr=16000,
                         hop_length=160, 
                         y_axis='linear',
                         x_axis='time')

plt.title('yes/0a7c2a8d_nohash_0.wav')
plt.colorbar(format='%+2.0f').set_label('[dB]')
plt.ylim(0, 8000)
plt.tight_layout()
plt.show()

f:id:ichou1:20200226213918p:plain

比較してみる。
右側が「libROSA」パッケージを使った結果。
f:id:ichou1:20200226213918p:plainf:id:ichou1:20200222101912p:plain

TensorFlow 2.X系のAPIを使ったコード

「TensorFlow 1.X」系は「1.15」で最後になるようなので、「TensorFlow 2.X」系に上げてみる。

pip install --upgrade tensorflow
python -c 'import tensorflow as tf; print(tf.__version__)'
2.1.0

「TensorFlow 2.X」環境下でも先ほどのソードコードは動作するが、「2.X系」に書き直してみる。

import tensorflow as tf
from tensorflow.python.ops import io_ops

# Audio Data
audio_path = 'speech_dataset/yes/0a7c2a8d_nohash_0.wav'

# Load Audio File
def load_data(filename):

    wav_loader = io_ops.read_file(filename)
    data, sr = tf.audio.decode_wav(wav_loader,
                                   desired_channels=1)

    # channelの次元を削除
    data_ = tf.squeeze(data)

    # batch_sizeの次元を追加
    data__ = tf.expand_dims(data_, axis=0)

    return data__, sr

# compute STFT
def get_stft_spectrogram(data):
    # Input: A Tensor of [batch_size, num_samples] 
    # mono PCM samples in the range [-1, 1]. 
    stfts = tf.signal.stft(data,
                           frame_length=480,
                           frame_step=160,
                           fft_length=512)

    # 振幅を求める
    spectrograms = tf.abs(stfts)

    return spectrograms


# 音声データ読み込み
audio_data, sr = load_data(audio_path)

# 特徴量を求める
feature = get_stft_spectrogram(audio_data)

「TensorFlow 2.X」系では、 「Eager Execution」がデフォルトで有効になる。

# 「Eager Execution」が有効か確認する
print(tf.executing_eagerly())
True