pyannote.audioを使った話者ダイアライゼーションをオフラインで動かす方法

2023.09.21

こんちには。

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

今回はpyannote.audioを使った話者ダイアライゼーションをオフラインで動かす方法を見ていこうと思います。

以下の記事のオフライン版となります。

こちらを以下の用途を目的としてオフラインモデルとして読み込めるように検討していきます

  • ネットワーク的に閉じた環境でジョブを実行したい
  • 推論時に毎回ダウンロードすることなしに高速に処理を開始できるようにしたい

pyannote.audioとは

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

GitHubは以下となります。

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

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

詳細は以下の過去のブログもご参照ください。

モデルの構成

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

トップのモジュールはpyannote/speaker-diariationで、依存モジュールとしてpyannote/segmentationspeechbrain/spkrec-ecapa-voxcelebがあるという構成となっています。

またpyannote/speaker-diariationpyannote/segmentationは使用する場合にHugging Faceのトークンが必要で、普通に以下のようにトークンを指定して毎回ダウンロードが必要になります。

pipeline = Pipeline.from_pretrained(
    "pyannote/speaker-diarization@2.1"
    , use_auth_token="<Hugging Faceコンソールでの操作で取得したトークン>"
)

こちらを以下の用途を目的としてオフラインモデルとして読み込めるように検討していきます。

オフライン化の方法

pyannote.audio側

pyannote/speaker-diariationpyannote/segmentationについてはオフライン化の手順が以下のノートブックで言及されています。

このうちOffline useの節に方法の記載があります。

ただし説明例がpyannote/voice-actrivity-detectionとなっている点はご注意ください。

pyannote.audio自体に様々なライブラリがあるため、pyannote/voice-actrivity-detectionpyannote/speaker-diarizationに読み替えます。

すなわち、今回の場合はpyannote/speaker-diarizationからconfig.yamlをダウンロードし、

pyannote/segmentationからpytorch_model.binをダウンロードします。

そして最初にダウンロードしたconfig.yamlの以下のsegmentationの部分がpytorch_model.binのパスを指すように修正します。

pipeline:
  name: pyannote.audio.pipelines.SpeakerDiarization
  params:
    clustering: AgglomerativeClustering
    embedding: speechbrain/spkrec-ecapa-voxceleb
    embedding_batch_size: 32
    embedding_exclude_overlap: true
    segmentation: ./pytorch_model.bin
    segmentation_batch_size: 32

params:
  clustering:
    method: centroid
    min_cluster_size: 15
    threshold: 0.7153814381597874
  segmentation:
    min_duration_off: 0.5817029604921046
    threshold: 0.4442333667381752

GitHubに記載されているのは、ここまででpyannote/voice-actrivity-detectionの場合はこの手順で問題無く動作します。

しかし、pyannote/speaker-diarizationの場合、それ以外の依存ライブラリとしてspeechbrain/spkrec-ecapa-voxcelebがあるため、こちらもオフラインで動作するよう対応が必要となります。

speechbrain側

speechbrain/spkrec-ecapa-voxcelebは特にトークン等を必要としないパブリックなライブラリで、デフォルトではモデル使用時に以下のキャッシュディレクトリにモデルファイルが保存されます。

~/.cache/huggingface/hub

今回、このキャッシュディレクトリはHUGGINGFACE_HUB_CACHEという環境変数を設定すれば変更が可能であることを利用し、キャッシュディレクトリの内容物をzipで固めて、ポータブルに扱えるようにしてみます。

やってみた

使用環境

Google Colaboratoryを使います。ハードウェアアクセラレータはGPU、GPUのタイプはT4、ラインタイム仕様は標準で実施します。

主なバージョン情報は以下です。

!python --version

# Python 3.10.12

パッケージのバージョンは以下です。(こちらは準備を一通り実施した後に確認できます)

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

# huggingface-hub==0.17.3
# ipython==7.34.0
# pyannote.audio==2.1.1
# pyannote.core==4.5
# pyannote.database==4.1.3
# pyannote.metrics==3.2.1
# pyannote.pipeline==2.3
# pytorch-lightning==1.6.5
# pytorch-metric-learning==1.7.3
# speechbrain==0.5.12
# torch==1.11.0
# torch-audiomentations==0.11.0
# torch-pitch-shift==1.2.4
# torchaudio==0.11.0
# torchdata==0.6.1
# torchmetrics==0.11.4
# torchsummary==1.5.1
# torchtext==0.12.0
# torchvision==0.12.0

Hugging Faceコンソールでの操作

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

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

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

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

環境設定

pyannote.audioに関するインストールをします。

# for speechbrain
!pip install -qq torch==1.11.0 torchvision==0.12.0 torchaudio==0.11.0 torchtext==0.12.0
!pip install -qq speechbrain==0.5.12

# pyannote.audio
!pip install -qq pyannote.audio==2.1.1

# for visualization purposes
!pip install -qq ipython==7.34.0

モデル取得

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

import os
os.environ['HUGGINGFACE_HUB_CACHE'] = './assets'  # デフォルト(未指定)の場合は ~/.cache/huggingface/hub

その後、以下で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",
    filename="config.yaml",
    use_auth_token=HF_TOKEN)

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

snapshot_download("speechbrain/spkrec-ecapa-voxceleb")

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

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

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

!find ./assets

# ./assets
# ./assets/config.yaml
# ./assets/pytorh_model.bin
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/refs
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/refs/main
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/72c6fa7b170cbfd169df5e09326cd97febdf086a
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/fd9e3634fe68bd0a427c95e354c0c677374f62b3f434e45b78599950d860d535
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/2d5abbd526328179f0516569cb9ff5ddd289a1fc
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/c79d72cc1197b4b32646fb55b18f4baf69a33dc1
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/ce0fb75939a3929f13c0d2e3c848781a80b04006
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/2e8b3b4d97ae58d7daa78954a5c7f5b8abb42934
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/0575cb64845e6b9a10db9bcb74d5ac32b326b8dc90352671d345e2ee3d0126a2
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/99d49dbaf2c68699660d729649d87b80ebbd9b9d
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/1978c5e7f20d5ffd14c8a932a7e85a09816f3da9
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/a38e3061f91ba51b02b7ef4c5fd4d088659c183d
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/config.json
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/embedding_model.ckpt
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/.gitattributes
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/label_encoder.txt
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/example1.wav
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/example2.flac
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/hyperparams.yaml
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/classifier.ckpt
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/README.md
# ./assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/mean_var_norm_emb.ckpt

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

!zip -r assets.zip ./assets

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

モデル取得

使用する際は、assets.zipをまずアップロードし、任意の場所で展開します。いまはカレントディレクトリにassets.zipがあると想定します。

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

!unzip assets.zip -d ./tmp

# Archive:  assets.zip
#    creating: ./tmp/assets/
#   inflating: ./tmp/assets/config.yaml  
#   inflating: ./tmp/assets/pytorh_model.bin  
#    creating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/
#    creating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/refs/
#  extracting: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/refs/main  
#    creating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/72c6fa7b170cbfd169df5e09326cd97febdf086a  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/fd9e3634fe68bd0a427c95e354c0c677374f62b3f434e45b78599950d860d535  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/2d5abbd526328179f0516569cb9ff5ddd289a1fc  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/c79d72cc1197b4b32646fb55b18f4baf69a33dc1  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/ce0fb75939a3929f13c0d2e3c848781a80b04006  
#  extracting: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/2e8b3b4d97ae58d7daa78954a5c7f5b8abb42934  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/0575cb64845e6b9a10db9bcb74d5ac32b326b8dc90352671d345e2ee3d0126a2  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/99d49dbaf2c68699660d729649d87b80ebbd9b9d  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/1978c5e7f20d5ffd14c8a932a7e85a09816f3da9  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/blobs/a38e3061f91ba51b02b7ef4c5fd4d088659c183d  
#    creating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/
#    creating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/
#  extracting: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/config.json  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/embedding_model.ckpt  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/.gitattributes  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/label_encoder.txt  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/example1.wav  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/example2.flac  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/hyperparams.yaml  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/classifier.ckpt  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/README.md  
#   inflating: ./tmp/assets/models--speechbrain--spkrec-ecapa-voxceleb/snapshots/5c0be3875fda05e81f3c004ed8c7c06be308de1e/mean_var_norm_emb.ckpt

展開したフォルダに応じて、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")

音声ファイルを処理します。

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

# CPU times: user 49.1 s, sys: 460 ms, total: 49.6 s
# Wall time: 50 s

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

diarization

その他、処理結果の扱いは前回の記事に譲ります。

まとめ

いかがでしたでしょうか。本記事ではダウンロード済みのモデルを使用してpyannote.audioのspeaker diarizationを行う方法を見ていきました。

これでAWS Batchなどでも動作させる目途が得られたと思います。

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