7万枚の手書き数字の画像+ラベルのデータセットである MNIST は,今では機械学習・深層学習のHello Worldとして非常に多くの人に利用されていて有名です.
MNISTの1つのデータは 白黒画像・28x28ピクセル,全体でも7万件なので大規模データですがデータセットのデータサイズは小さいので使いやすいです.また,内容も手書き数字10種類の画像というシンプルである程度パターンが抽出できそうなデータであるため,データ分析や機械学習の練習によく利用されているのだと思います.
そんな MNIST の音・オーディオ版のようなデータセットがないか調べたところ,このFSDD を見つけたので紹介します.
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)