SpeechBrainを使った話者分離の性能検証 ~SepFormerを用いて~

こんにちは。音声認識の研究をしている石井です。

今回は、SpeechBrainを使った話者分離モデルの中でも、2020年に発表されたSepFormer *1についてその性能を検証しました。 評価は訓練済みモデルに対してYouTubeから入手した音源を用いて行いました。 結果、一部の音源に対して上手く複数の人声を分離できたことはありましたが、多くの場合で複数人の声が入ったり、ノイズが残ったりするなどして、残念ながらあまり高い効果は見られませんでした。

SpeechBrainとは

SpeechBrainはPyTorchを用いた最先端の音声ツールキットで、オープンソースで開発が進められています。 SpeechBrainが課題とする技術は今回用いた話者分離に限らず、音声認識・話者認識・音声強調・言語特定など幅広い分野に渡り、そういった課題に対して既にいくつかのシステムで高いパフォーマンスを発揮しています。

研究のモチベーション

話者分離システムを研究する目的としては、日常の会議の録音などを分離してから文字起こしをすることで、いちいち音声を再生して発言者の声を確認しなくとも、誰がこの発言をしたのか容易に特定できるようになる、ということが挙げられます。さらに開発側の観点では、分離後の音声データを他の学習のためのデータセットに用いることで、音声認識等の技術でより精度の高いモデル獲得につながる可能性があります。

論文の紹介

今回モデル評価の対象とした SepFormer は Subakan, Ravanelli, et al. (2020) によって示されました。 SepFormerは従来の音声分離ネットワークと比べて、回帰型ニューラルネットワーク (RNN)を用いておらず、代わりにTransformerを基にすることで並行処理をより容易にし、計算時間の短縮を図っているという特徴があります。 Transformerを提示したVaswani, Shazeer, et al. (2017) *2では機械翻訳での性能を検証していました。今回の論文ではそれを音声分離で効果測定しています。

ここで用いられたモデルは、学習された重み付け関数(マスク)をベースとし、エンコーダ、デコーダ、マスキングネットワークから構成されています (Fig. 1)。

まず、エンコーダは複数音声が入った音源をとり、1層の畳み込みレイヤを用いて短時間フーリエ変換の表現を学習します。 次に、マスキングネットワークはエンコードされた表現をとり、分離されるべきそれぞれのスピーカに対して重みを予測します (Fig. 2)。

最後に、デコーダは重み調整済みソースを畳み込みレイヤの変換行列にかけて、それぞれ分離された音声を出力します。

論文での検証結果は、WSJ0-2mixデータセットでSI-SNRi (scale-invariant signal-to-noise ratio improvement)が22.3 dB、WSJ0-3mixデータセットで19.5 dBとなったとのことです。

ここで、SI-SNRは以下のように定義されます *3

\displaystyle{
\begin{align}
\left\{
\begin{array}{ll}
&s_{target} := \frac{\langle\hat{s}, s\rangle s}{\|s\|^2} &\\
&e_{noise} := \hat{s}-s_{target} &\\
&SI\text{-}SNR := 10\log_{10} \frac{\|s_{target}\|^2}{\|e_{noise}\|^2} &
\end{array}
\right.
\end{align}
}

ここで、

  • \displaystyle{ \hat{s}\in\mathbb R^ {1 \times T} }: 予測クリーンソース (正規化する)
  • \displaystyle{ s\in\mathbb R^ {1 \times T} }: オリジナルクリーンソース(平均を0となるように移動)
  • \displaystyle{ |s|^ 2=\langle s, s\rangle }: シグナルの力
  •  T: 分類予測するソースの数

この結果はDPRNNやDPTNetを用いたものに比べ優れており、さらに計算時間やメモリ利用量の点でも勝っています (Fig. 3; WSJ0-3mixで他のモデルと比較した場合でも同様の傾向)。 これは、著者によると、Transformerによる並列演算だけでなく、畳み込みレイヤのストライドを8とすることでモデルの計算量を減らすことに成功しているからだといいます。

検証手法

SepFormerの追加検証はGoogle Colab上で、YouTubeから入手した音源を用いて行いました。SpeechBrainのチュートリアルも参照 *4

まず、 SpeechBrainをインストール:

pip install speechbrain

そして、以下で訓練済みのWSJ0-2/3mixのモデルを用い、実際に用意した音源を分離します。 ここで、model2/3はそれぞれWSJ0-2/3mixのモデルを用い、音源を2/3分割。 関数split2/3で分離したい音源を取って分離後の音声ファイルを保存。 関数play_split2/3Google Colab上で分離前後のファイルをIPython.displayで簡単に確認するためのものです。

import speechbrain as sb
from speechbrain.pretrained import SepformerSeparation as separator
import torchaudio
import os
import IPython

# Use a pretrained model for SepFormer on WSJ0-2Mix
model2 = separator.from_hparams(source="speechbrain/sepformer-wsj02mix", savedir='pretrained_models/sepformer-wsj02mix')
# Use a pretrained model for SepFormer on WSJ0-3Mix
model3 = separator.from_hparams(source="speechbrain/sepformer-wsj03mix", savedir='pretrained_models/sepformer-wsj03mix')

def split2(PATH):
    est_sources = model2.separate_file(path=PATH)
    root, ext = os.path.splitext(PATH)
    torchaudio.save(root + '_1of2' + ext, est_sources[:, :, 0].detach().cpu(), 8000)
    torchaudio.save(root + '_2of2' + ext, est_sources[:, :, 1].detach().cpu(), 8000)

def play_split2(PATHtoSource):
    print("Source: ", PATHtoSource)
    IPython.display.display(IPython.display.Audio(PATHtoSource))

    root, ext = os.path.splitext(PATHtoSource)
    print("1st split: " + root + "_1of2" + ext)
    IPython.display.display(IPython.display.Audio(root + '_1of2' + ext))
    print("2nd split: " + root + "_2of2" + ext)
    IPython.display.display(IPython.display.Audio(root + '_2of2' + ext))

def split3(PATH):
    est_sources = model3.separate_file(path=PATH)
    root, ext = os.path.splitext(PATH)
    torchaudio.save(root + '_1of3' + ext, est_sources[:, :, 0].detach().cpu(), 8000)
    torchaudio.save(root + '_2of3' + ext, est_sources[:, :, 1].detach().cpu(), 8000)
    torchaudio.save(root + '_3of3' + ext, est_sources[:, :, 2].detach().cpu(), 8000)

def play_split3(PATHtoSource):
    print("Source:", PATHtoSource)
    IPython.display.display(IPython.display.Audio(PATHtoSource))

    root, ext = os.path.splitext(PATHtoSource)
    print("1st split: " + root + "_1of3" + ext)
    IPython.display.display(IPython.display.Audio(root + '_1of3' + ext))
    print("2nd split: " + root + "_2of3" + ext)
    IPython.display.display(IPython.display.Audio(root + '_2of3' + ext))
    print("3rd split: " + root + "_3of3" + ext)
    IPython.display.display(IPython.display.Audio(root + '_3of3' + ext))

なお、作業をGoogle Colab上で完結させるため、音源はyoutube-dlを用いてダウンロードし、検証に使う範囲をffmpegで切り取って用いました *5 *6

# youtube-dlをインストール
!curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
!chmod a+rx /usr/local/bin/youtube-dl

!youtube-dl --extract-audio --audio-format mp3 https://www.youtube.com/watch?v=URL_for_the_source_video -o /path/to/audio_source_original.mp3
!ffmpeg -i audio_source_original.mp3 -ss <開始時間> -to <終了時間> audio_source_cut.mp3

音源の用意ができた後、model2を用いて2分割しIPython.displayで結果を確認します (3分割も同様)。

PATH = "/path/to/audio_source_cut.mp3"
split2(PATH)
play_split2(PATH)

検証結果

合計9つの動画から音源を入手し2/3分割しました。 このうち3つの音源である程度満足できる結果が得られた一方、その他の音源では複数の声を拾ったり、雑音しか残っていなかったりなどあまりうまくいきませんでした。 ここでは一部の音源に対して参照のためのYouTube動画を提示し、実験結果を文章で説明します。 また、無料版Google Colab上での分離処理にかかった時間は20秒前後の音源で約50秒、1分前後の音源で3分強ほどだった。

音源1 (全範囲)

テレビ東京のニュース番組WBSから。 効果音 → 田中アナ(女性) → 全日空社長(男性)VTR → 田中アナ、の順。

このまま2分割すると、1つ目のファイルでは効果音とVTR後の田中アナの声が、2つ目のファイルでは元の音源にノイズが入っただけのような印象で、あまりうまくいかなかった。

効果音、田中アナ、全日空社長で3分割できるかと思いmodel3を試すと、1つ目は全日空社長だけ上手く拾い、2つ目は田中アナメインだが全日空社長の声も少し残る、3つ目は始めの効果音だけはっきり、残りはノイズっぽくなった。これはそれなりに上手く言った印象。

一方ではじめの効果音の2秒間をカットし、残りの部分で2分割すると、全日空社長のVTR前後で分けられているように思われた。

音源2 (0:5:50 - 0:6:15)

若い男性3人の対談。3分割してみたが、やはり声質が似ているからか同じファイルが3つ出来上がったのではと思うほど分けられていなかった。

音源3 (0:0:07 - 0:0:25)

気象予報番組で女性キャスター、男性予報士、BGMの構成。2分割したところ1つ目で男性とBGMを、2つ目は女性のみをおよそ綺麗に分けられた。

音源4 (0:1:00 - 0:1:20)

テレビ番組で女優のEmma Watsonと男性MC (Jimmy Fallon)との対談。2分割した結果そこそこ上手く1人ずつ分けられた。

音源5 (0:1:30 - 0:2:32)

男女それぞれ2人ずつの議論。2・3分割ともに上手くいかず、いずれのファイルにも全員の声が入ってしまった。

音源6 (0:0:40 - 0:1:02)

こちらは男性2人+女性1人の議論。3分割したところ、1つ目のファイルは男性2人の声を拾い、2つ目は3人の声、3つ目は女性のみ拾おうとしている傾向が見られたが、全体的にノイズっぽくなってしまった。

音源7 (0:1:17 - 0:2:30)

女性2人+男性司会者の議論。3分割するもほぼ全員の声が3つすべてのファイルに入ってしまった。

手元で実験した限りでは、音源の言語が日本語か英語であるかは分離精度にあまり関係がないように思われます。 また、2人の音源の一部で上手くいっていたのに対し、3人以上ではほとんど上手く分離できなかったことを鑑みると、人数が増えるごとに分離するのが難しくなる傾向があるのかもしれません。

今後の課題

より様々な条件で、実験で用いたモデルがどのような反応を示すか試したいです。 例えば、通訳を介した複数の言語が入った音源や発話者の性別、BGM・ノイズの有無はどれほど分離精度に影響するのでしょうか...?

さらに、音声分離後の用途(音声認識、文字起こし、...?)などから、どれくらいのモデル精度が必要なのかもはっきりわかればよさそうです。 また、SepFormerと他の音声分離モデルとを比較することで、より精度・計算コスト的に有利な方法も導き出せるとよいのではないでしょうか。

*1:Subakan, C., Ravanelli, M., Cornell, S., Bronzi, M., & Zhong, J. (2020). Attention is All You Need in Speech Separation. https://doi.org/10.48550/ARXIV.2010.13154

*2:Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, L., & Polosukhin, I. (2017). Attention Is All You Need. https://doi.org/10.48550/ARXIV.1706.03762

*3:Luo, Y., & Mesgarani, N. (2019). Conv-TasNet: Surpassing Ideal TimeFrequency Magnitude Masking for Speech Separation. IEEE/ACM Transactions on Audio, Speech, and Language Processing, 27(8), 1256-1266. https://doi.org/10.1109/taslp.2019.2915167

*4:https://huggingface.co/speechbrain/sepformer-wsj02mix

*5:https://github.com/ytdl-org/youtube-dl

*6:https://qiita.com/yang_orz/items/4f19e88a456e56aadc55