LoginSignup
3
4

More than 1 year has passed since last update.

SpeechBrainで音声の感情分析をしてみる

Last updated at Posted at 2022-10-30

SpeechBrainで音声の感情認識ができるようなので試してみました。

SpeechBrainでの感情認識のサンプル

SpeechBrainには各種タスクを実行するためのレシピが多数用意されています。今回は音声の感情認識を行いたいので以下のレシピを使います。IEMOCAPというデータセットを使って感情認識を行うレシピです。

speechbrain/recipes/IEMOCAP/emotion_recognition

今回はtrain_with_wav2vec2.pyのほうを使っていますが、train.pyでも同様に動くと思います。

データセットが入手できない

IEMOCAPは音声と表情のラベルがセットになったデータセットなのですが、こちらのダウンロードには審査があるようで、大学以外の人がダウンロードするのは難しそうです。今回は諦めて他のデータセットを使うことにしました。

利用するデータセット

今回はこちらのデータセットを使います。
The Ryerson Audio-Visual Database of Emotional Speech and Song (RAVDESS)
https://zenodo.org/record/1188976#.Y1493HbP0Q8
Livingstone, Steven R.; Russo, Frank A. (CC BY-NC-SA 4.0)
ライセンスがCC BY-NC-SA 4.0なので注意してください。

こちらのデータセットは8種類の感情がラベル付けされています。wavのファイル名の'-'で区切られた3番目の数値が感情のラベルです。

今回は以下のフォルダに展開しています。

cd speechbrain/recipes/IEMOCAP/dataset_test
curl -O 'https://zenodo.org/record/1188976/files/Audio_Speech_Actors_01-24.zip'
unzip Audio_Speech_Actors_01-24.zip

展開するとActor_01, Actor_02 と話者ごとにフォルダ分けされたwavファイルが入っています。

hparams/train_with_wav2vec2.yamlを修正

データフォルダを修正します。今回はglob.glob()を使うので以下の形式のパスにしています。
out_n_neuronsは感情ラベルの数です。元は4ですがデータセットに合わせて8に変更しました。

...
data_folder: ../dataset_test/**/*.wav
...
out_n_neurons: 8

iemocap_prepare.pyを修正

以下の部分を修正します。

# 追加
import glob

...

# 音声数の変更
NUMBER_UTT = 1440

...

def prepare_data(
    data_original,
    save_json_train,
    save_json_valid,
    save_json_test,
    split_ratio=[80, 10, 10],
    different_speakers=False,
    test_spk_id=1,
    seed=12,
):
    ...

    #ここはコメントアウト
    #data_original = data_original + "/Session"
def transform_data(path_loadSession):
    ...

    # 話者の数が変わるので10 -> 24に変更    
    #speaker_dict = {str(i + 1): [] for i in range(10)}
    speaker_dict = {str(i + 1): [] for i in range(24)}
    
    # ここを今回のデータセットに合わせて変更
    '''
    speaker_count = 0
    for k in range(5):
        session = load_session("%s%s" % (path_loadSession, k + 1))
        for idx in range(len(session)):
            if session[idx][2] == "F":
                speaker_dict[str(speaker_count + 1)].append(session[idx])
            else:
                speaker_dict[str(speaker_count + 2)].append(session[idx])
        speaker_count += 2

    return speaker_dict
    '''
    
    # path_loadSessionはtrain.yaml(train_with_wav2vec2.yaml)のdata_folderのパスが来る
    # ../dataset_test/**/*.wav のようにAudio_Speech_Actors_01-24.zipを展開した先をしてする
    wav_file_list = glob.glob(path_loadSession)
    print('path_loadSession:', path_loadSession)
    print('wav_file_list:', wav_file_list[:5])
    
    for wav_file in wav_file_list:
        dirname = os.path.dirname(wav_file)
        dirname = dirname.split('/')[-1]
        
        print('dirname:', dirname)
        
        basename = os.path.basename(wav_file)

        # フォルダ名のActor_10の10を取り出してspeaker名にする
        speaker = str(int(dirname.replace('Actor_', '')))
        # emotionは'-'区切りで3つ目の要素
        # このデータセットでは8種類ある
        emotion = basename.split('-')[2]
        # utteranceはとりあえず空で
        utterance = ''
        speaker_dict[speaker].append([wav_file, emotion, utterance])
        
    print('speaker_dict:', speaker_dict)
    
    return speaker_dict

train実行

以下のコマンドで学習を実行すると途中で止まりました。wavファイルを読み込んだときにShapeの形が変わってしまうことがあるようです。

python train_with_wav2vec2.py hparams/train_with_wav2vec2.yaml

原因は一部のファイルのチャンネル数が2になっていた

こちらの記事を参考にしました。チャンネル数が1より大きい場合はmeanを使って平均を取るとよさそうです。
https://stackoverflow.com/questions/67508634/speechbrain-dataio-prepare-function-with-csv

train_with_wav2vec2.pyを修正します。

# この2つを追加
import torchaudio
import torch

def dataio_prep(hparams):
    ...
    # Define audio pipeline
    @sb.utils.data_pipeline.takes("wav")
    @sb.utils.data_pipeline.provides("sig")
    def audio_pipeline(wav):
        """Load the signal, and pass it and its length to the corruption class.
        This is done on the CPU in the `collate_fn`."""
        sig = sb.dataio.dataio.read_audio(wav)
        
        # 2チャンネルの場合は平均をとって1チャンネルに変換
        info = torchaudio.info(wav)
        if info.num_channels > 1:
            sig = torch.mean(sig, dim=1)
        #print('sig shape:', sig.shape)            
        
        return sig

学習結果

最後まで学習が進みました。test lossとerror_rateが小さくなっているので学習は進んだようです。

speechbrain.utils.train_logger - Epoch: 30, lr: 3.14e-05, wave2vec_lr: 3.14e-06 - train loss: 5.87e-03 - valid loss: 1.37, valid error_rate: 1.60e-01
speechbrain.utils.checkpoints - Saved an end-of-epoch checkpoint in results/train_with_wav2vec2/1993/save/CKPT+2022-10-29+23-48-26+00
speechbrain.utils.checkpoints - Deleted checkpoint in results/train_with_wav2vec2/1993/save/CKPT+2022-10-29+23-47-33+00
speechbrain.utils.checkpoints - Loading a checkpoint from results/train_with_wav2vec2/1993/save/CKPT+2022-10-29+23-41-19+00
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 36/36 [00:01<00:00, 18.11it/s]
speechbrain.utils.train_logger - Epoch loaded: 22 - test loss: 8.23e-01, test error_rate: 1.25e-01
3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4