音声認識Whisperと話者識別Pyannote.audioで議事録自動作成

スポンサーリンク
Whisper
スポンサーリンク

Pythonの音声認識ライブラリWhisperと話者識別ライブラリPyannote.audioで面倒な議事録の自動作成をしてみます。WhisperはOpenAIからMITライセンスで提供されています。Pyannote.audioもMITライセンスで提供されています。いずれも適切なライセンス表示下で改変、配布、商用利用が認められており、非常に使いやすいライブラリです。

前準備

WhisperとPyannote.audioの導入については、過去記事で紹介していますので、そちらをご参照ください。また、必須ではありませんが、GPU環境での実施が推奨されています。以下、参考記事へのリンクを示しますので、Whisperのセットアップ、Pyannote.audioのセットアップ、GPU環境構築については、詳しくは下記のリンクをご参照し、準備をしてください。

Whisperのセットアップ
多言語AI音声認識モデルWhisperの使いこなし~インストールからWebUI実装まで
OpenAIのWhisperで日本語文字起こし環境構築と使用感
Pyannote.audioのセットアップ
Pythonで話者識別ライブラリPyannote.audioを使ってみる
GPU環境構築
WindowsへのNVIDIA CUDAのGPU環境構築
パソコン選び
生成系AIを使うためのGPU搭載おすすめパソコン

準備ができたところで、今回使用する仮想環境を準備します。まず、好きなフォルダにターミナルなどでPython3.8の仮想環境作成を作成し、そこに必要なライブラリをインストールします。(参考記事:Windowsで複数のバージョンのPythonをインストールする

> py -3.8 -m venv venv
> .\venv\Scripts\Activate.ps1
(venv)> python -m pip install -U pip setuptools
(venv)> pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117
(venv)> pip install git+https://github.com/openai/whisper.git
(venv)> pip install jupyterlab
(venv)> git clone https://github.com/pyannote/pyannote-audio
(venv)> pip install -r ./pyannote-audio/requirements.txt
(venv)> pip install pyannote.audio
(venv)> pip install huggingface_hub
(venv)> ./venv/Scripts/Activate.ps1
(venv)> jupyter lab

オーディオファイルの加工

もし、手持ちのオーディオファイルを用いる場合、前もって適切な形に加工します。特にPyannoteはwavファイルしか対応していませんので、pydudなどでwavファイルに変換しましょう。(参考記事:Pythonライブラリpydudで直感的なオーディオファイル加工

議事録の作成コード

今回は、サンプルコードとしてVOICEVOXエンジンで生成した合成音声を用います。(VOICEVOXの関連記事はこちら

VOICEVOXで生成した会議の音声 ※VOOCEVOX:ずんだもん、VOICEVOX:四国めたん、VOICEVOX:波音リツ、VOICEVOX:冥鳴ひまり

もし、サンプルで使う音声がなければ、ダウンロードをして使っていただけたらと思います。

Jupyter Labのインタラクティブ対話環境で確認しながら実装してみます。

音声データ取得部と音声解析部でそれぞれコードを実装します。

# from huggingface_hub import notebook_login
## 最初のみpyannote.audioのトークンでログイン
# notebook_login()


from pyannote.audio import Pipeline
import whisper

# 音声データ取得部
# Pyannote.audioで話者識別の実施
pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization@2.1")
diarization = pipeline("meeting.wav")
speaker_list = [[turn.start,turn.end, speaker, ""]  for turn, _, speaker in diarization.itertracks(yield_label=True)]

# Whisperで音声認識の実施
model = whisper.load_model("medium")
result = model.transcribe("meeting.wav")

Pyannote.audioから次のようなデータが得られます。

speaker_list
[[0.4978125, 5.492812499999999, 'SPEAKER_01'],
 [6.606562500000001, 8.1253125, 'SPEAKER_03'],
 [8.985937500000002, 15.1284375, 'SPEAKER_00'],
 [15.9721875, 19.4653125, 'SPEAKER_03'],
 [20.2753125, 27.9028125, 'SPEAKER_02'],
 [28.712812500000002, 36.39093750000001, 'SPEAKER_03'],
 [37.251562500000006, 46.51593750000001, 'SPEAKER_00'],
 [47.376562500000006, 54.83531250000001, 'SPEAKER_02'],
 [55.628437500000004, 63.6609375, 'SPEAKER_03'],
 [64.4709375, 69.1959375, 'SPEAKER_00'],
 [70.0734375, 75.5071875, 'SPEAKER_02'],
 [75.5071875, 75.5578125, 'SPEAKER_01'],
 [76.1821875, 85.4634375, 'SPEAKER_01']]

一方、Whisperからは以下のデータが得られます。

for i in range(len(result['segments'])):
    talk_data = result['segments'][i]
    print("start={:.1f}s stop={:.1f}s {}".format(talk_data['start'],talk_data['end'], talk_data['text']))
0.0s : 6.4s 「それでは会議を始めます 今日はAIについて自由に話し合いたいと思います」
6.4s : 8.8s 「AIって何だろう?」
8.8s : 15.8s 「AIは人工知能のことだよ コンピュータが人間のように考えることができるようになっているんだ」
15.8s : 20.1s 「AIがあればどんなことができるの?」
20.1s : 28.5s 「AIを使って自動運転の車ができるようになったり スマホで音声入力できるようになったりするんだ」
28.5s : 37.1s 「でもAIを使って人間が仕事をしなくてもよくなっちゃうと思うと怖いよね」
37.1s : 47.2s 「それはそうだけどAIを使って医者が病気を早く見つけられるようになったり 災害の時に人を救えるようになったりすることもできるんだ」
47.2s : 55.4s 「AIは良いことも悪いこともあるけど みんなで使って良いことができるようにすることが大事だと思う」
55.4s : 64.3s 「そうだね AIを使って人間がもっと幸せになれるようにすることが大事だと思う」
64.3s : 69.9s 「AIを使って環境をもっときれいにできるようにすることも大事だと思う」
69.9s : 76.0s 「AIを使って人間がもっと楽しく生きられるようにすることも大事だと思う」
76.0s : 87.5s 「話がまとまったね AIの未来について話し合った結果 みんなで使って良いことができるようにすることが大事だということを確認しました」

Whisperでは無音部も音声出力範囲に入るのに対し、Pyannote.audioでは、音声を発生した時間で記録しているため、Pyannote.audioのほうがやや音声の範囲が狭くなっているのがわかります。それらの結果からWhisperとPyannote.audioの結果を、以下のコードで紐づけしてやります。

# Whisperの発話範囲から、Pyannote.audioの発話者を特定する関数
def speaker_selector(s,e,t, speaker_dict={}):
    while t <= len(speaker_list):
        if s<= speaker_list[t][0] and e >= speaker_list[t][1]:
            if speaker_list[t][2] in speaker_dict:
                return speaker_dict[speaker_list[t][2]],t
            else:
                return speaker_list[t][2],t
        else:
            if len(speaker_list) >=t-2:
                if e >= speaker_list[t+1][0]:
                    t += 1
                else:
                    return "unknown",t
            else:
                return "unknow",t
# 議事録の出力
speaker_turn = 0
for i in range(len(result['segments'])):
    talk_data = result['segments'][i]
    speaker, speaker_turn = speaker_selector(talk_data['start'], talk_data['end'], speaker_turn)
    print("{:.1f}s --- {:.1f}s {} {}".format(talk_data['start'],talk_data['end'], speaker, talk_data['text']))
0.0s --- 6.4s SPEAKER_01 それでは会議を始めます 今日はAIについて自由に話し合いたいと思います
6.4s --- 8.8s SPEAKER_03 AIって何だろう?
8.8s --- 15.8s SPEAKER_00 AIは人工知能のことだよ コンピュータが人間のように考えることができるようになっているんだ
15.8s --- 20.1s SPEAKER_03 AIがあればどんなことができるの?
20.1s --- 28.5s SPEAKER_02 AIを使って自動運転の車ができるようになったり スマホで音声入力できるようになったりするんだ
28.5s --- 37.1s SPEAKER_03 でもAIを使って人間が仕事をしなくてもよくなっちゃうと思うと怖いよね
37.1s --- 47.2s SPEAKER_00 それはそうだけどAIを使って医者が病気を早く見つけられるようになったり 災害の時に人を救えるようになったりすることもできるんだ
47.2s --- 55.4s SPEAKER_02 AIは良いことも悪いこともあるけど みんなで使って良いことができるようにすることが大事だと思う
55.4s --- 64.3s SPEAKER_03 そうだね AIを使って人間がもっと幸せになれるようにすることが大事だと思う
64.3s --- 69.9s SPEAKER_00 AIを使って環境をもっときれいにできるようにすることも大事だと思う
69.9s --- 76.0s SPEAKER_02 AIを使って人間がもっと楽しく生きられるようにすることも大事だと思う
76.0s --- 87.5s SPEAKER_01 話がまとまったね AIの未来について話し合った結果 みんなで使って良いことができるようにすることが大事だということを確認しました

うまく紐づけができました。ここで、実際の発言から、SPEAKER_00, SPEAKER_01,SPEAKER_02,SPEAKER_03はそれぞれ、波音リツ、冥鳴ひまり、四国めたん、ずんだもんですので、SPEAKER部を辞書で変換してやります。

# Whisperの発話範囲から、Pyannote.audioの発話者を特定する関数
def speaker_selector(s,e,t, speaker_dict={}):
    while t <= len(speaker_list):
        if s<= speaker_list[t][0] and e >= speaker_list[t][1]:
            if speaker_list[t][2] in speaker_dict:
                return speaker_dict[speaker_list[t][2]],t
            else:
                return speaker_list[t][2],t
        else:
            if len(speaker_list) >=t-2:
                if e >= speaker_list[t+1][0]:
                    t += 1
                else:
                    return "unknown",t
            else:
                return "unknow",t

# 発話者の変換辞書(出力された議事録から、当てはめる)
speaker_dict = {"SPEAKER_00":"波音リツ",
               "SPEAKER_01":"冥鳴ひまり",
               "SPEAKER_02":"四国めたん",
               "SPEAKER_03":"ずんだもん"}

# 議事録の出力
speaker_turn = 0
for i in range(len(result['segments'])):
    talk_data = result['segments'][i]
    speaker, speaker_turn = speaker_selector(talk_data['start'], talk_data['end'], speaker_turn)
    print("{:.1f}s --- {:.1f}s {} {}".format(talk_data['start'],talk_data['end'], speaker, talk_data['text']))
0.0s --- 6.4s 冥鳴ひまり 「それでは会議を始めます 今日はAIについて自由に話し合いたいと思います」
6.4s --- 8.8s ずんだもん 「AIって何だろう?」
8.8s --- 15.8s 波音リツ 「AIは人工知能のことだよ コンピュータが人間のように考えることができるようになっているんだ」
15.8s --- 20.1s ずんだもん 「AIがあればどんなことができるの?」
20.1s --- 28.5s 四国めたん 「AIを使って自動運転の車ができるようになったり スマホで音声入力できるようになったりするんだ」
28.5s --- 37.1s ずんだもん 「でもAIを使って人間が仕事をしなくてもよくなっちゃうと思うと怖いよね」
37.1s --- 47.2s 波音リツ 「それはそうだけどAIを使って医者が病気を早く見つけられるようになったり 災害の時に人を救えるようになったりすることもできるんだ」
47.2s --- 55.4s 四国めたん 「AIは良いことも悪いこともあるけど みんなで使って良いことができるようにすることが大事だと思う」
55.4s --- 64.3s ずんだもん 「そうだね AIを使って人間がもっと幸せになれるようにすることが大事だと思う」
64.3s --- 69.9s 波音リツ 「AIを使って環境をもっときれいにできるようにすることも大事だと思う」
69.9s --- 76.0s 四国めたん 「AIを使って人間がもっと楽しく生きられるようにすることも大事だと思う」
76.0s --- 87.5s 冥鳴ひまり 「話がまとまったね AIの未来について話し合った結果 みんなで使って良いことができるようにすることが大事だということを確認しました」

議事録がうまく作成できました。

発言者を自動識別するには?

例えば、いつも同じメンバーでの会議であれば、識別したい会話の前に、発言者が分かっている音声データをつなげてやることで、既知の音声データから発言者を取得して、音声データ全体の発言者を特定することもできるかと思います。

他にもいろいろ記事をあげているので、良かったらご覧ください。不明な点があれば、投稿フォームからご質問ください。

Whisper
スポンサーリンク
鷹の目週末プログラマー

コメント