初めに
reazon-research/reazonspeechのデータは主に音声認識(STT)のデータセットとして想定されたデータ?みたいです。
そのため、音声合成(TTS)のデータセットとしてはそのままでは使うには雑音などが入っている音声が多いため、どのようなデータが入っているのか分析して使えるようにしていきます
reazon-research/reazonspeechのデータは以下の記事でも扱っています。
デモ
データ分析した結果をヒストグラムでグラフにした結果は以下になります。
以下を見る感じ雑音が比較的少ないものは、100以上であることがわかるためフィルタリングする際には100以上で切っても良さそうです
以下で使用したGolobを公開しています
開発環境
- Google Colob(フリー)
準備
分析に必要なライブラリを入れます
!pip install datasets librosa IPython !pip install numpy scipy datasets !pip install soundfile
分析手順
データをダウンロード・ロード
まずは huggingfaceからデータをダウンロード及びロードします。 その際に ログインが必要になるため、事前にログインを行なっておきます
!huggingface-cli login
ダウンロード及びロードの処理を行います。今回はGoogle Colobの容量の限界があるため、smallサイズでダウンロードします
from datasets import load_dataset # データセットをロード ds = load_dataset("reazon-research/reazonspeech", "small", trust_remote_code=True)
WAND-SNRを使って音声データの分析
各音声データのWADA-SNRを取得して、jsonに保存します。
from datasets import load_dataset import numpy as np def wada_snr(wav): # Direct blind estimation of the SNR of a speech signal. # # Paper on WADA SNR: # http://www.cs.cmu.edu/~robust/Papers/KimSternIS08.pdf # # This function was adapted from this matlab code: # https://labrosa.ee.columbia.edu/projects/snreval/#9 # init eps = 1e-10 # next 2 lines define a fancy curve derived from a gamma distribution -- see paper db_vals = np.arange(-20, 101) g_vals = np.array([0.40974774, 0.40986926, 0.40998566, 0.40969089, 0.40986186, 0.40999006, 0.41027138, 0.41052627, 0.41101024, 0.41143264, 0.41231718, 0.41337272, 0.41526426, 0.4178192 , 0.42077252, 0.42452799, 0.42918886, 0.43510373, 0.44234195, 0.45161485, 0.46221153, 0.47491647, 0.48883809, 0.50509236, 0.52353709, 0.54372088, 0.56532427, 0.58847532, 0.61346212, 0.63954496, 0.66750818, 0.69583724, 0.72454762, 0.75414799, 0.78323148, 0.81240985, 0.84219775, 0.87166406, 0.90030504, 0.92880418, 0.95655449, 0.9835349 , 1.01047155, 1.0362095 , 1.06136425, 1.08579312, 1.1094819 , 1.13277995, 1.15472826, 1.17627308, 1.19703503, 1.21671694, 1.23535898, 1.25364313, 1.27103891, 1.28718029, 1.30302865, 1.31839527, 1.33294817, 1.34700935, 1.3605727 , 1.37345513, 1.38577122, 1.39733504, 1.40856397, 1.41959619, 1.42983624, 1.43958467, 1.44902176, 1.45804831, 1.46669568, 1.47486938, 1.48269965, 1.49034339, 1.49748214, 1.50435106, 1.51076426, 1.51698915, 1.5229097 , 1.528578 , 1.53389835, 1.5391211 , 1.5439065 , 1.54858517, 1.55310776, 1.55744391, 1.56164927, 1.56566348, 1.56938671, 1.57307767, 1.57654764, 1.57980083, 1.58304129, 1.58602496, 1.58880681, 1.59162477, 1.5941969 , 1.59693155, 1.599446 , 1.60185011, 1.60408668, 1.60627134, 1.60826199, 1.61004547, 1.61192472, 1.61369656, 1.61534074, 1.61688905, 1.61838916, 1.61985374, 1.62135878, 1.62268119, 1.62390423, 1.62513143, 1.62632463, 1.6274027 , 1.62842767, 1.62945532, 1.6303307 , 1.63128026, 1.63204102]) # peak normalize, get magnitude, clip lower bound wav = np.array(wav) wav = wav / abs(wav).max() abs_wav = abs(wav) abs_wav[abs_wav < eps] = eps # calcuate statistics # E[|z|] v1 = max(eps, abs_wav.mean()) # E[log|z|] v2 = np.log(abs_wav).mean() # log(E[|z|]) - E[log(|z|)] v3 = np.log(v1) - v2 # table interpolation wav_snr_idx = None if any(g_vals < v3): wav_snr_idx = np.where(g_vals < v3)[0].max() # handle edge cases or interpolate if wav_snr_idx is None: wav_snr = db_vals[0] elif wav_snr_idx == len(db_vals) - 1: wav_snr = db_vals[-1] else: wav_snr = db_vals[wav_snr_idx] + \ (v3-g_vals[wav_snr_idx]) / (g_vals[wav_snr_idx+1] - \ g_vals[wav_snr_idx]) * (db_vals[wav_snr_idx+1] - db_vals[wav_snr_idx]) # Calculate SNR dEng = sum(wav**2) dFactor = 10**(wav_snr / 10) dNoiseEng = dEng / (1 + dFactor) # Noise energy dSigEng = dEng * dFactor / (1 + dFactor) # Signal energy snr = 10 * np.log10(dSigEng / dNoiseEng) return snr # データを前処理するための関数。ファイルパスを必要としないため、少し変更します。 def preprocess_audio(data): # データが整数型の場合、浮動小数点型に変換 if data.dtype == np.int16: data = data.astype(np.float32) / np.iinfo(np.int16).max elif data.dtype == np.int32: data = data.astype(np.float32) / np.iinfo(np.int32).max # ステレオをモノラルに変換(必要があれば) if len(data.shape) == 2: data = data.mean(axis=1) return data import librosa import IPython.display as ipd from IPython.display import Audio, display import random from concurrent.futures import ProcessPoolExecutor print("データ数: ",len(ds['train'])) import json # 結果を保存するリスト results = [] # 音声データの前処理とSNR計算を行う関数 def process_audio_data(index): # 音声データを前処理 audio_data = ds['train'][index]['audio']['array'] preprocessed_data = preprocess_audio(audio_data) # WADA-SNRを計算 snr = wada_snr(preprocessed_data) # データセットから情報を取得 audio_path = ds['train'][index]['audio']['path'] file_name = ds['train'][index]['name'] transcription = ds['train'][index]['transcription'] # 音声ファイルを読み込む(実際にはこの処理はSNR計算に不要かもしれません) # audio, sr = librosa.load(audio_path, sr=16000) return { "ファイル名": file_name, "SNR値": snr, "トランスクリプション": transcription } # 並列処理で関数を実行 with ProcessPoolExecutor(max_workers=4) as executor: for result in executor.map(process_audio_data, range(len(ds['train']))): print("ファイル名:" + result["ファイル名"] + ", SNR値" + str(result["SNR値"])) # print("トランスクリプション: ", result["トランスクリプション"]) results.append(result) # 結果をJSONファイルに保存 with open('audio_analysis_results.json', 'w') as f: json.dump(results, f, ensure_ascii=False, indent=4) print("JSONファイルが保存されました。") from google.colab import files # Google Colabからファイルをダウンロード files.download("audio_analysis_results.json")
分析結果をヒストグラムで表示
分析結果を上記でjsonに保存したため、そのjsonからデータの散らばりを確認するためにヒストグラムで表示をします。
import json import matplotlib.pyplot as plt # JSONファイルからデータをロード file_path = 'audio_analysis_results.json' with open(file_path, 'r') as file: data = json.load(file) # WADA-SNR値のリストを抽出 snr_values = [item['SNR値'] for item in data] # ヒストグラムを描画 plt.hist(snr_values, bins=200, edgecolor='black') # binsは適宜調整してください plt.xlabel('SNR value') plt.ylabel('number of occurances') plt.title('Histogram of SNR values') plt.grid(True) plt.show()
WADA-SNR値が100以上のデータ個数を取得
データをフィルタリングする際に、仮にWADA-SNR値が100以上を使うとした際に全体のうちどのくらいのデータが有効になるのかを調べます
# WADA-SNR値が100以上のデータの数をカウント count_snr_above_100 = sum(1 for item in data if item['SNR値'] >= 100) print(f"SNR値が100以上のデータの数: {count_snr_above_100}") # SNR値が100以上のデータの数: 3364
またsmallでの全体のデータ数は以下で取得できます
print("データ数: ",len(ds['train'])) # データ数: 62047
そのため、3364/62047がWADA-SNR値が100以上のため 100以上のみを使う場合(small)、5.4%になりそうです
備考
Windowsでのプロセスエラー対応
Windowsで処理する場合は、以下のようなエラーが出るのでコードを書き換えます
エラー
This probably means that you are not using fork to start your child processes and you have forgotten to use the proper idiom in the main module: if __name__ == '__main__': freeze_support()
修正コード
if __name__ == '__main__': freeze_support() # 並列処理で関数を実行 with ProcessPoolExecutor(max_workers=4) as executor: for result in executor.map(process_audio_data, range(len(ds['train']))): print("ファイル名:" + result["ファイル名"] + ", SNR値" + str(result["SNR値"])) # print("トランスクリプション: ", result["トランスクリプション"]) results.append(result) # 結果をJSONファイルに保存 with open('audio_analysis_results.json', 'w') as f: json.dump(results, f, ensure_ascii=False, indent=4) print("JSONファイルが保存されました。")
並列処理対応
以下で並列処理ができます
from datasets import load_dataset import numpy as np import json from multiprocessing import Pool import os import IPython.display as ipd from IPython.display import Audio, display from concurrent.futures import ProcessPoolExecutor (省略) if __name__ == '__main__': ds = load_dataset("reazon-research/reazonspeech", "all", trust_remote_code=True) total_data = len(ds['train']) print("データ数: ", total_data) # 並列処理のためのプールを作成 num_cores = os.cpu_count() print("num cores:",num_cores) pool = Pool(processes=num_cores) # 並列処理を実行 results = [] for i, result in enumerate(pool.imap(process_audio_data, range(total_data)), 1): results.append(result) print(f"処理中: {i}/{total_data} ({i/total_data*100:.2f}%)") # プールを閉じる pool.close() pool.join() # 結果をJSONファイルに保存 with open("reazonspeech-all-wada-snr.json", "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=4) print("JSONファイルが保存されました。")