J-CHATを機械学習で扱いやすいデータに整形
2話者が対話しているモノラルな音声を,話者分離をしたのちに書き起こしを作成する手順をメモしておきます.この記事では話者分離をしてステレオ音声として保存するところまで書きます.
pyannoteでも分離可能なんですが,精度がいまいちだったのでAsteroidを使いました.(Colabで実行しています.)
扱う音声データ
以下のように,チャネル1,サンプリング周波数が22050(Hz),1分41秒の対話音声データを扱います.
https://drive.google.com/file/d/1D3N_wTC5d4fX08jbsbyEw5CdK_cZNF5F/view?usp=sharing
$ soxi 15f97caafef4a4b352e54781236cf2b4.wav
Input File : '15f97caafef4a4b352e54781236cf2b4.wav'
Channels : 1
Sample Rate : 22050
Precision : 16-bit
Duration : 00:01:41.54 = 2238891 samples ~ 7615.28 CDDA sectors
File Size : 4.48M
Bit Rate : 353k
Sample Encoding: 16-bit Signed Integer PCM
もろもろインポートします.
import librosa
import numpy as np
import matplotlib.pyplot as plt
librosaで波形を見てみます.
file_name = "/content/15f97caafef4a4b352e54781236cf2b4.wav"
y, sr = librosa.load(file_name)
time = np.arange(0, len(y)) / sr
plt.plot(time,y)
plt.xlabel("Time(s)")
plt.ylabel("Sound Amptitude")
plt.show()
話者分離(ステレオ音声ファイルを出力)
Asteroidを使って2話者に分離します.
!pip install asteroid
でAsteroidをインストールしておいてください.
from asteroid.models import ConvTasNet
import torch
import torchaudio
# モデルのロード(最大2話者対応のモデル)
model = ConvTasNet.from_pretrained("JorisCos/ConvTasNet_Libri2Mix_sepclean_16k")
# 音声データの整形
waveform_torch = torch.tensor(y).unsqueeze(0) # (1, 1, T)
resampler = torchaudio.transforms.Resample(orig_freq=22050, new_freq=16000)
waveform_torch = resampler(waveform_torch)
# モデルで話者分離(出力:話者数 x サンプル数)
separated_sources = model.separate(waveform_torch)
separated_sources_np = separated_sources.cpu().numpy()
それぞれの音声を出力したかったら,以下のプログラムを動かします.
import IPython.display as ipd
ipd.display(ipd.Audio(separated_sources_np[0, 0], rate=16000))
ipd.display(ipd.Audio(separated_sources_np[0, 1], rate=16000))
それぞれの音声を波形で見てみます.
time = np.arange(0, len(separated_sources_np[0, 0])) / sr
plt.plot(time,separated_sources_np[0, 0])
plt.xlabel("Time(s)")
plt.ylabel("Sound Amptitude")
plt.show()
time = np.arange(0, len(separated_sources_np[0, 1])) / sr
plt.plot(time,separated_sources_np[0, 1])
plt.xlabel("Time(s)")
plt.ylabel("Sound Amptitude")
plt.show()
左が話者A,右が話者Bの波形です.(分離されていることがわかります.)
ステレオ音声として保存
import soundfile as sf
# separated_sources_np: shape = (1, 2, T) → (2, T)
stereo_data = separated_sources_np[0] # shape: (2, T)
# 転置して shape を (T, 2) にする → ステレオ形式
stereo_data = stereo_data.T # shape: (T, 2)
# 保存
sf.write("separated_stereo.wav", stereo_data, samplerate=16000)
このようにチャネルごとに話者が割り振られた2チャネル音声になりました.