pyannote.audioがメジャーアップデートし3.0.0がリリースされました

2023.09.28

こんちには。

データアナリティクス事業本部 インテグレーション部 機械学習チームの中村です。

pyannote.audio v3が2023/09/26に公開されました。

少し使い方の異なる部分がありましたので、ブログを再度投稿します。

以下は過去のpyannote.audio v2のブログとなりますので、こちらも併せてご参照ください。

pyannote.audioとは

pyannote.audioとは話者ダイアライゼーションを行うライブラリです。

ベースのフレームワークはPyTorchとなっており、end-to-endの話者ダイアライゼーションを実現します。

話者ダイアライゼーションとは、どこの時間でどの話者がしゃべったのか、話者認識をせずに実施する技術のことを指します。

v3のアップデートについては以下に記載があります。

v3のポイントをいくつかピックアップしました。

  • より良い性能をもつ音声区間検出と話者ダイアライゼーションを使用し、性能を向上
  • APIの変更により、話者埋め込みベクトルが取得しやすくなった

その他様々なアップデートがされているようですので、詳細は公式をご覧ください。

モデルの構成

pyannote.audioはHugging Faceで展開されているライブラリを以下のように複数使用してその処理を実現しています。

トップのモジュールはpyannote/speaker-diariation-3.0で、v3でリポジトリ自体が変わっています。

依存モジュールとしてpyannote/segmentation-3.0も、v3でリポジトリ自体が変わっています。

またv2においてEmbeddingで使われていたspeechbrain/spkrec-ecapa-voxcelebhbredin/wespeaker-voxceleb-resnet34-LMに変わっています。

動かしてみた

今回は前回記事同様、オフラインで実行する場合の手順を見ていきます。

使用環境

Google Colaboratoryを使います。

ハードウェアアクセラレータはGPU T4、ハイメモリはオフで実施します。

Pythonのバージョン情報は以下です。

!python --version

# Python 3.10.12

モジュールのインストール

以下でインストールできます。

!pip install pyannote.audio

関連しそうなモジュールのバージョンは以下のようになっています。

!pip freeze | grep \
    -e "torch==" -e "torch" -e "pyannote" -e "ipython==" -e "huggingface-hub"

# huggingface-hub==0.17.3
# ipython==7.34.0
# pyannote.audio==3.0.0
# pyannote.core==5.0.0
# pyannote.database==5.0.1
# pyannote.metrics==3.2.1
# pyannote.pipeline==3.0.1
# pytorch-lightning==2.0.9
# pytorch-metric-learning==2.3.0
# torch @ https://download.pytorch.org/whl/cu118/torch-2.0.1%2Bcu118-cp310-cp310-linux_x86_64.whl#sha256=a7a49d459bf4862f64f7bc1a68beccf8881c2fa9f3e0569608e16ba6f85ebf7b
# torch-audiomentations==0.11.0
# torch-pitch-shift==1.2.4
# torchaudio @ https://download.pytorch.org/whl/cu118/torchaudio-2.0.2%2Bcu118-cp310-cp310-linux_x86_64.whl#sha256=26692645ea061a005c57ec581a2d0425210ac6ba9f923edf11cc9b0ef3a111e9
# torchdata==0.6.1
# torchmetrics==1.2.0
# torchsummary==1.5.1
# torchtext==0.15.2
# torchvision @ https://download.pytorch.org/whl/cu118/torchvision-0.15.2%2Bcu118-cp310-cp310-linux_x86_64.whl#sha256=19ca4ab5d6179bbe53cff79df1a855ee6533c2861ddc7389f68349d8b9f8302a

Hugging Faceコンソールでの操作

最初のダウンロードにはHugging Faceでの操作が必要ですので、まずはHugging Faceのアカウントを作成してログインします。

その後、以下2つのリポジトリのuser conditionAcceptする必要があります。

Accept画面は以下のように表示されるので、必要事項を入力してボタンを押下してください。

その後、ユーザ管理画面の以下のURLにアクセスし、トークンを作成しておきます。

モデル取得

まずは以下でキャッシュフォルダを変更しておきます。

import os

# 注: デフォルト(未指定)の場合は ~/.cache/huggingface/hubとなります
os.environ['HUGGINGFACE_HUB_CACHE'] = './assets'

その後、以下でHugging Faceからモデルを取得します。

HF_TOKEN = "{作成したHugging Faceのトークン}"

from huggingface_hub import hf_hub_download, snapshot_download

config_path = hf_hub_download(repo_id="pyannote/speaker-diarization-3.0",
    filename="config.yaml",
    use_auth_token=HF_TOKEN)

segmentation_model_path = hf_hub_download(repo_id="pyannote/segmentation-3.0",
    filename="pytorch_model.bin",
    use_auth_token=HF_TOKEN)

snapshot_download("hbredin/wespeaker-voxceleb-resnet34-LM")

次にpyannote/speaker-diarization-3.0config.yamlpyannote/segmentation-3.0pytorch_model.binについて、実体を分かりやすい場所にコピーしておき、元フォルダ自体は削除します。

!cp -L {segmentation_model_path} ./assets/pytorh_model.bin
!cp -L {config_path} ./assets/config.yaml
!rm -rf ./assets/models--pyannote--segmentation-3.0
!rm -rf ./assets/models--pyannote--speaker-diarization-3.0

本時点でファイル構成は以下のようになります。

!find ./assets

# ./assets
# ./assets/config.yaml
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/1b116fbd9e410a379801292073d336707b27c97c
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/7bb2f06e9df17cdf1ef14ee8a15ab08ed28e8d0ef5054ee135741560df2ec068
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/a6344aac8c09253b3b630fb776ae94478aa0275b
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/d151b72237c3df707fd6cb31e783903171804fa2
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/LICENCE.md
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/speaker-embedding.onnx
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/.gitattributes
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/README.md
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/refs
# ./assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/refs/main
# ./assets/pytorh_model.bin

こちらをそのままzipに固めておきます。

!zip -r assets.zip ./assets

こちらをダウンロードするなどしてどこか別の場所に置いておきます。

ノートブック再起動

Google Colaboratoryを一旦、「ランタイムを接続解除」をして削除します。

その後は再度セットアップが必要ですので、pyannote.audioをインストールします。

!pip install pyannote.audio

モデル読込

ダウンロードしたzipファイルassets.zipをまずアップロードし、任意の場所で展開します。

いまはカレントディレクトリにassets.zipがあると想定します。

こちらを例えば./tmpに展開します。

!unzip assets.zip -d ./tmp

# Archive:  assets.zip
#    creating: ./tmp/assets/
#   inflating: ./tmp/assets/config.yaml  
#    creating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/
#    creating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/1b116fbd9e410a379801292073d336707b27c97c  
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/7bb2f06e9df17cdf1ef14ee8a15ab08ed28e8d0ef5054ee135741560df2ec068  
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/a6344aac8c09253b3b630fb776ae94478aa0275b  
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/blobs/d151b72237c3df707fd6cb31e783903171804fa2  
#    creating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/
#    creating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/LICENCE.md  
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/speaker-embedding.onnx  
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/.gitattributes  
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/snapshots/354ddb4cbab6f6ff45cb763eac517873927dcf74/README.md  
#    creating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/refs/
#   inflating: ./tmp/assets/models--hbredin--wespeaker-voxceleb-resnet34-LM/refs/main  
#   inflating: ./tmp/assets/pytorh_model.bin

展開したフォルダに応じて、config.yamlを以下のように書き換えます。

import yaml

with open("./tmp/assets/config.yaml", 'r') as yml:
    config = yaml.safe_load(yml)

config["pipeline"]["params"]["segmentation"] = "./tmp/assets/pytorh_model.bin"

with open("./tmp/assets/config.yaml", 'w') as f:
    yaml.dump(config, f)

最後にHUGGINGFACE_HUB_CACHEを展開したフォルダに応じて書き換えます。こちらは絶対パスで指定しておきます。

import os
import pathlib

os.environ['HUGGINGFACE_HUB_CACHE'] = str(pathlib.Path("./tmp/assets").absolute())

こちらで準備が整いました。

話者ダイアライゼーションのテスト

準備が整ったので、話者ダイアライゼーション処理をしてみます。

今回は公式のノートブックで使用されていた音声を使います。

!wget -q http://groups.inf.ed.ac.uk/ami/AMICorpusMirror/amicorpus/ES2004a/audio/ES2004a.Mix-Headset.wav
!ls -ltra ./ES2004a.Mix-Headset.wav

# -rw-r--r-- 1 root root 33579394 Mar 14  2005 ./ES2004a.Mix-Headset.wav

以下でpipelineをインスタンス化します。

from pyannote.audio import Pipeline
from pyannote.core import Segment, notebook, Annotation
import json
import polars as pl

pipeline = Pipeline.from_pretrained("./tmp/assets/config.yaml")

# send pipeline to GPU (when available)
import torch
pipeline.to(torch.device("cuda"))

上記のコードですがv3の変更点として、明示的にpipeline.toでGPU実行する指定をする必要があります。

次に実際に音声ファイルを処理します。

%%time
diarization = pipeline("./ES2004a.Mix-Headset.wav")

# CPU times: user 8min 10s, sys: 1.27 s, total: 8min 11s
# Wall time: 8min 17s

前回と比較して処理時間が増えたことが分かりました。

以下を実行すると結果を確認できます。

diarization

部分的な出力

部分的なdiarization結果を得るためには、以下のようにSegmentクラスを使うことで可能となっています。

from pyannote.core import Segment, notebook
start = 0
length = 600
EXCERPT = Segment(start, start+length)
notebook.crop = EXCERPT
diarization

またその後、選択した描画範囲をリセットするには以下のコードを実行します。

notebook.reset()

詳細な結果の取得

各区間ごとの詳細を得るには、以下のようにgeneratorを使って得ることができます。

for segment, track_name, label in diarization.itertracks(yield_label=True):
    print(f"{segment.start=:.1f}, {segment.end=:.1f}, {track_name=}, {label=}")

# segment.start=10.9, segment.end=14.8, track_name=1, label='SPEAKER_01'
# segment.start=14.9, segment.end=15.0, track_name=1, label='SPEAKER_01'
# segment.start=15.0, segment.end=15.1, track_name=4, label='SPEAKER_04'
# segment.start=17.9, segment.end=18.4, track_name=4, label='SPEAKER_04'
# segment.start=18.8, segment.end=20.3, track_name=1, label='SPEAKER_01'
# segment.start=22.3, segment.end=23.7, track_name=1, label='SPEAKER_01'
# segment.start=25.1, segment.end=26.5, track_name=2, label='SPEAKER_02'
# segment.start=29.1, segment.end=32.4, track_name=2, label='SPEAKER_02'
#
# ...以降略...

発話率の計算

v2と同じですが、発話率の統計情報を計算するには、以下のようにします。

sorted(diarization.chart())

# [('SPEAKER_00', 132.13921901528124),
#  ('SPEAKER_01', 359.830220713072),
#  ('SPEAKER_02', 239.06621392190178),
#  ('SPEAKER_03', 20.933786078098542),
#  ('SPEAKER_04', 86.36672325976205)]

結果をテキストで出力

テキスト出力については、for_jsonというメソッドがv3で廃止されているようですので、write_labまたはwrite_rttmを使用します。

with open("result_rttm.txt", "wt") as f:
    # diarization.write_lab(f)
    diarization.write_rttm(f)

write_rttmの場合、出力される結果は以下のようになります。

SPEAKER ES2004a.Mix-Headset 1 10.891 3.905 <NA> <NA> SPEAKER_01 <NA> <NA>
SPEAKER ES2004a.Mix-Headset 1 14.898 0.136 <NA> <NA> SPEAKER_01 <NA> <NA>
SPEAKER ES2004a.Mix-Headset 1 15.034 0.102 <NA> <NA> SPEAKER_04 <NA> <NA>
SPEAKER ES2004a.Mix-Headset 1 17.869 0.509 <NA> <NA> SPEAKER_04 <NA> <NA>
SPEAKER ES2004a.Mix-Headset 1 18.820 1.528 <NA> <NA> SPEAKER_01 <NA> <NA>

...以降略...

話者埋め込みの取得

v3ではreturn_embeddings=Trueとすることで、ダイアライゼーション結果の他に、話者の埋め込みベクトルが取得できるようにAPIが変わっています。

以下のようにして実施します。

diarization, embeddings = pipeline("./ES2004a.Mix-Headset.wav", return_embeddings=True)

for s, speaker in enumerate(diarization.labels()):
    # embeddings[s] is the embedding of speaker `speaker`
    print(speaker, embeddings[s].shape)

# SPEAKER_00 (256,)
# SPEAKER_01 (256,)
# SPEAKER_02 (256,)
# SPEAKER_03 (256,)
# SPEAKER_04 (256,)

それぞれ256次元のベクトルで表現されていることが分かります。

まとめ

いかがでしたでしょうか。本記事ではpyannote.audioのv3についての使用方法を見ていきました。

本記事の内容が皆様の参考になれば幸いです。