Wizard Notes

Pythonを使った音楽信号分析の技術録、作曲活動に関する雑記

音声・オーディオ版MNIST "FSDD (Free Spoken Digit Dataset)" の紹介と,メルスペクトログラム算出

f:id:Kurene:20210629235041p:plain:w500

7万枚の手書き数字の画像+ラベルのデータセットである MNIST は,今では機械学習・深層学習のHello Worldとして非常に多くの人に利用されていて有名です.

MNISTの1つのデータは 白黒画像・28x28ピクセル,全体でも7万件なので大規模データですがデータセットのデータサイズは小さいので使いやすいです.また,内容も手書き数字10種類の画像というシンプルである程度パターンが抽出できそうなデータであるため,データ分析や機械学習の練習によく利用されているのだと思います.

そんな MNIST の音・オーディオ版のようなデータセットがないか調べたところ,このFSDD を見つけたので紹介します.

github.com

FSDD (Free Spoken Digit Dataset) の概要

FSDDは1桁の数字の発話録音で構成されるシンプルな音声データセットとなっています.

現在 (2021/6) のデータセットの状態は以下のようになっています.

  • wav 8kHz
  • 6人の話者
  • 全て英語の発音
  • 3,000件の録音
    • スピーカー数 × 数字の数 (10個) x 同数字の発話回数50)
    • 1サンプルは 1~2秒の時間信号

FSDD自体はオープンデータセットであり、話者を新規追加するためのスクリプト等も付属しています。 

ライセンスは CC BY-SA 4.0 となっています.

画像化用の付属スクリプト

FSDDのデータは音声ファイルですが,画像としてデータを保存・分析する人向けに,以下の2つの便利なスクリプトが付属しています.

  • fsdd/utils/spectogramer
    • 録音データのスペクトログラムを算出して画像として保存する
    • fsdd/spectograms に保存
  • fsdd/utils/train-test-split
    • spectogramerで作成したスペクトログラム画像を,学習データとテストデータに分ける
    • train-spectrograms
    • testing-spectrograms

メルスペクトログラムでの読込スクリプトの実装

上述のスクリプトだと生のスペクトログラムなので識別モデルの学習などには使いにくいと考え,メルスペクトログラムでデータセットを読み込みスクリプトを書いてみました.

メルスペクトログラム算出はlibrosa.feature.melspectrogramを使っています。

注意点として,10000サンプル以下のデータはいくつかあるものの, 9_theo_16.wavだけ18262サンプルだった(発話後の無発話区間がなぜか長い)ので,10,000サンプル以上の場合は10000より後ろのサンプルを扱わないようにしています。

import os
import wave
import glob
import librosa
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt


# 16-bit wavefile読み込み
def load_wav(wavfilepath):
    wf   = wave.open(wavfilepath, "r")
    sr          = wf.getframerate()
    n_ch        = wf.getnchannels()
    n_frames    = wf.getnframes()
    n_bytes     = wf.getsampwidth()
    audiobuffer = wf.readframes(n_frames)
    wf.close()

    data = np.frombuffer(audiobuffer, dtype=np.int16).astype(np.float32)

    n_samples = data.shape[0] // n_ch
    x = np.zeros((n_ch, n_samples), dtype=np.float32)
    for k in range(n_ch):
        x[k] = data[k::n_ch]
    x /= 2 ** 15 # [-1, 1) に正規化
    
    return x, sr

# 時間波形・ラベルデータ読込
dataset_path = "./recordings"
filepath_list = glob.glob(f"{dataset_path}/*.wav")
n_wavfiles = len(filepath_list)

x_orig         = [None for _ in range(n_wavfiles)]
labels_number  = [None for _ in range(n_wavfiles)]
labels_speaker = [None for _ in range(n_wavfiles)]
basename_list  = [None for _ in range(n_wavfiles)]

max_length = 5000
for k, filepath in enumerate(filepath_list):
    basename = os.path.splitext(os.path.split(filepath)[1])[0]
    basename_list[k]  = basename
    labels_number[k]  = basename.split("_")[0]
    labels_speaker[k] = basename.split("_")[0]
    
    tmp_x, sr  = load_wav(filepath)
    tmp_x      = np.squeeze(tmp_x)
    tmp_len    = tmp_x.shape[0]
    if tmp_len < 10000:
        max_length = max_length if tmp_len < max_length else tmp_len
    else:
        tmp_x = tmp_x[0:max_length]
    
    #print(tmp_len, max_length, basename)
    x_orig[k]  = tmp_x


# メルスペクトログラム算出
x = np.zeros(max_length)
x_melspecs = [None for _ in range(n_wavfiles)]

for k in range(n_wavfiles):
    print(k)
    tmp_len      = x_orig[k].shape[0]
    x[0:tmp_len] = x_orig[k]
    melspec = librosa.feature.melspectrogram(
                    y=x, sr=sr, 
                    n_fft=512, hop_length=128, 
                    n_mels=64, fmax=6000, power=1.0)
    x_melspecs[k] = melspec
    
    """
    plt.figure(figsize=(4, 3))
    plt.imshow(melspec, aspect="auto", cmap="jet", origin='lower')
    plt.savefig(f"./spectrograms/{basename_list[k]}.png")
    plt.clf()
    """
    
x_melspecs = np.array(x_melspecs)

関連記事