【colaboratory】APIを使ってGoogleDrive内の音声データから議事録を作成

プログラミング
この記事は約16分で読めます。

高性能なGPUを持っていなくても、colaboratoryを利用することで、音声データから自動的に議事録を作成することが可能です。

Pyannote.audio、Whisper等のモジュール、ChatGPT、Notion等のAPIを使って、Colaboratory上で音声データからの議事録を作成して、保存できるようなコードを作りました。

以下、colaboratoryのリンクです。

Google Colab

※不要な機能は適宜削除して利用してください

主要な機能について

pyannote.audio 音声データを解析し、話している人を認識、区別する
Whisper 音声データを解析し、文字として出力する
ChatGPT 人工知能(AI)を使ったチャットサービスであり、色々な事ができる
Notion ドキュメント作成や社内Wiki、タスク管理などのさまざまな機能を備えたアプリ
なお、ChatGPT及びNotionを利用するためには、それぞれ公式ホームページからAPIキーを取得する必要があります。(取得方法については、説明を省略します)

使う前の準備

音声データを保存

・GoogleDriveに、録音データ(.wav)を”yyyymmdd”の形式でアップロードする。

(例 今日の日付が2023年5月28日の場合、”20230528.wav”として保存する

APIへのアクセス情報を入力

・上記colaboratoryのリンクを開き、openAIの組織情報、APIキーを入力する

・notionのアクセストークン、投稿したいデータベースIDを入力する
# openAIのアクセス情報を定義
openai.organization = "ここにopenaiの組織情報を入力"
openai.api_key = "ここにopenaiのAPIキーを入力"

# notionへのアクセス情報を定義
NOTION_ACCESS_TOKEN = "ここにNotionのアクセストークンを入力"
NOTION_DATABASE_ID = "ここにNotionのデータベースIDを入力"

GPUを使用する設定に変更

最初に「編集」→「ノートブックの設定」→「NoneをGPUに変更」→「保存」

コードを順番に実行

・一番上のコードを実行し、「Googleドライブに接続」→「許可」を選択

・二番目のコードを実行し、「許可」を選択

・「ランタイム」→「以降のセルを実行」を選択

※もしくは「ctrl+F10」を押す

これで議事録が作成される。

コードの説明

試しに実行した場合の動きを確認してみる

pyannote.audio

pipインストール

!pip install datetime
!pip install sys
!pip install io

# Install pyannote.audio packages
# 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

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

実行

from pyannote.audio import Pipeline
import sys
import datetime
from pytz import timezone

# 音声ファイルはGoogleDriveのルート(MyDrive内)に"yyyymmdd"の形式で保存する
now = datetime.datetime.now()
now = now.astimezone(timezone('Asia/Tokyo')) #colaboは世界標準時刻のため、タイムゾーンを東京に変更
daytime = now.strftime("%Y-%m-%d %H:%M:%S")
base_path = r"/content/drive/MyDrive/"
ymd = now.strftime("%Y%m%d") + ".wav"
filepath = base_path +  ymd
print("filepath: " + filepath)

# 音声データ取得部
# Pyannote.audioで話者識別の実施
pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization@2.1", use_auth_token="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")
diarization = pipeline(filepath) #(filepath, num_speakers=xx)で話者数の設定が可能
speaker_list = [
    [turn.start, turn.end, speaker, ""]
    for turn, _, speaker in diarization.itertracks(yield_label=True)
]
print(speaker_list)
・GoogleDriveのルートに保存した音声データを解析し、誰がどの時間に発言したのかをlistにしている。
・pipeline(filepath)の部分はpipeline(filepath, num_speakers=2)に変更すると、話している人数を指定して解析することが可能
出力結果
[[0.4978125, 1.9490625000000001, ‘SPEAKER_01’, ”],
[6.184687500000001, 6.707812500000001, ‘SPEAKER_01’, ”],
[8.8509375, 11.922187500000003, ‘SPEAKER_01’, ”],
[9.559687500000003, 10.099687500000002, ‘SPEAKER_00’, ”]
・・・]

Whisper

pipインストール部分

# Install whisper packages
!pip install git+https://github.com/openai/whisper.git

実行部分

import whisper

# Whisperで音声認識の実施
model = whisper.load_model("large")
print("filepath: " + filepath)
result = model.transcribe(filepath)

for i in range(len(result["segments"])):
    talk_data = result["segments"][i]
    print(
        str(i) + " start={:.1f}s stop={:.1f}s {}".format(
            talk_data["start"], talk_data["end"], talk_data["text"]
        )
    )
・GoogleDriveのルートに保存した音声データを解析し、台詞を時間ごとに整理している。
・resultは辞書型であり、’text’、’segments’、’language’がそれぞれ格納されている。
・segments(talk_data)には、下記のようなデータが格納されている。
talk_dataに入っているデータ(list型の中に辞書型が格納されている)
[{‘id’: 0, ‘seek’: 0, ‘start’: 0.0, ‘end’: 2.0, ‘text’: ‘今日のスーパーで何買う?’, ‘tokens’: […], ‘temperature’: 0.0, ‘avg_logprob’: 略, ‘compression_ratio’: 略, …},
{‘id’: 1, ‘seek’: 0, ‘start’: 6.0, ‘end’: 7.0, ‘text’: ‘何買う?’, ‘tokens’: […], ‘temperature’: 0.0, ‘avg_logprob’: 略, ‘compression_ratio’: 略, …},
{‘id’: 2, ‘seek’: 0, ‘start’: 9.0, ‘end’: 10.0, ‘text’: ‘なんで?’, ‘tokens’: […], ‘temperature’: 0.0, ‘avg_logprob’: 略, ‘compression_ratio’: 略, …}
・・・]

誰がどの発言をしたのかを組み合わせる

pyannote.audioとWhisperのデータを組み合わせて、台詞の発言者を区別する。

# Whisperの発話範囲から、Pyannote.audioの発話者を特定する関数
def speaker_selector(s, e, speaker_dict={}):
    longest_duration = 0
    longest_speaker = None

    for speaker in speaker_list:
        speaker_start = speaker[0]
        speaker_end = speaker[1]

        if s <= speaker_start <= e or s <= speaker_end <= e:
            duration = min(speaker_end, e) - max(speaker_start, s)
        elif speaker_start <= s and e <= speaker_end: duration = e - s else: continue if duration > longest_duration:
            longest_duration = duration
            longest_speaker = speaker[2]
        elif duration == longest_duration:   
            longest_speaker = "unknown"     
    
    return longest_speaker

# 標準出力をキャプチャするために、新しいストリームを作成
captured_output = io.StringIO()
sys.stdout = captured_output

# speakerが紐づけられた音声の書き起こしをターミナルに出力
speaker_turn = 0
for i in range(len(result["segments"])):
    talk_data = result["segments"][i]
    speaker = speaker_selector(talk_data["start"], talk_data["end"])
    print(speaker, talk_data["text"])

# 標準出力を元に戻す
sys.stdout = sys.__stdout__

# キャプチャされた出力をテキストとして格納
voice_text = captured_output.getvalue()

print("voice_text: " + voice_text)
出力結果
SPEAKER_01 今日のスーパーで何買うの?
SPEAKER_01 何買うの?
SPEAKER_01 なんで?
SPEAKER_01 来週の夜ご飯
SPEAKER_00 鶏肉にしようかなって思ってるけど
・・・
・「なんで?」の発言者はSPEAKER_00である。あまり精度が高いとは言えない。ちなみに、speaker_selector()については、ChatGPTに以下の指示をして作成してもらった。
#あなたはエンジニアです。オンとオフをランダムで繰り返している電球A,B,Cがあります。それぞれon状態を開始した時刻と、offに切り替わった時刻をlistデータに格納しています。このlistデータを参照し、ある一定の期間中にon状態の時間が最も長かった電球を特定するための関数をpythonでcodingしてほしい。

ChatGPT

pipインストール部分

!pip install openai

実行部分

import openai

# openAIのアクセス情報を定義
openai.organization = "ここにopenaiの組織情報を入力"
openai.api_key = "ここにopenaiのAPIキーを入力"
# ChatGPTcにテキストを渡してサマリーを作成
summary = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=[
        {
            "role": "system",
            "content": "Create a summary from the transcript of the discussion.",
        },
        {
            "role": "user",
            "content": "We discussed the meeting. The transcript is written in Japanese.",
        },
        {
            "role": "assistant",
            "content": "Please give us the text of the transcript of the discussion.",
        },
        {"role": "user", "content": voice_text},
        {
            "role": "assistant",
            "content": "The transcriptions may contain typographical errors and should be converted appropriately.",
        },
        {
            "role": "user",
            "content": "The [Decision] and [Notes] and [Todo] sections should be itemized.",
        },
        {"role": "system", "content": "以上を踏まえて、日本語でサマリーを作成します。"},
    ],
)

print("summaryは右: " + str(summary))
発言リストを解析し、内容を要約してもらう。
※一度に送付できる文字数(トークン数)に限度があり、大体4000文字を超えた文章を要約しようとするとエラーとなってしまう。

Notion

実行部分

import requests

# notionへのアクセス情報を定義
NOTION_ACCESS_TOKEN = "ここにNotionのアクセストークンを入力"
NOTION_DATABASE_ID = "ここにNotionのデータベースIDを入力"
url = "https://api.notion.com/v1/pages"

# notionに投稿
notion = f"{summary['choices'][0]['message']['content']}"

headers = {
    "accept": "application/json",
    "Notion-Version": "2022-06-28",
    "Authorization": f"Bearer {NOTION_ACCESS_TOKEN}",
}

json_data = {
    "parent": {"type": "database_id", "database_id": NOTION_DATABASE_ID},
    # プロパティ
    "properties": {
        # タイトル
        "title": {"title": [{"text": {"content": daytime}}]},
    },
    # 本文
    "children": [
        # ブロック
        {
            "object": "block",
            "heading_2": {"rich_text": [{"text": {"content": notion}}]},
        },
        {
            "object": "block",
            "paragraph": {
                "rich_text": [
                    {
                        "text": {
                            "content": voice_text,
                        },
                    }
                ],
            },
        },
    ],
}

response = requests.post(url, json=json_data, headers=headers)
print("response.textは右: " + str(response.text))
・ChatGPTが要約したテキストを、Notionへ投稿する。
※送付できるデータ量に上限がある。
完成した投稿がこちら

テキストファイルとしてColaboratoryからGoogleDriveへデータを送付する

実行部分

#GoogleDriveへファイルを送付するため
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from google.colab import auth
from oauth2client.client import GoogleCredentials

auth.authenticate_user()
gauth = GoogleAuth()
gauth.credentials = GoogleCredentials.get_application_default()
drive = GoogleDrive(gauth)

# ファイルに保存するパスとファイル名
output_file_path = now.strftime("%Y%m%d") + ".txt"

# ファイルに返事を保存
with open(output_file_path, "w", encoding="utf-8") as output_file:
    output_file.write(str(voice_text))

# ファイルへの保存が完了したことを表示
print("Chat response saved to", output_file_path)

#GoogleDriveへダウンロード
upload_file = drive.CreateFile()
upload_file.SetContentFile(output_file_path)
upload_file.Upload()
・Notionへデータを送付しない場合は、発言リストをtxtデータとしてGoogleDriveへ送付する。

参考にしたサイト

音声認識Whisperと話者識別Pyannote.audioで議事録自動作成
Pythonの音声認識ライブラリWhisperと話者識別ライブラリPyannote.audioで面倒な議事録の自動作成をしてみます。WhisperはOpenAIからMITライセンスで提供されています。Pyannote.audioもMITライセンスで提供されています。いずれも適切なライセンス表示下で改変、配布、商用利用が...
PythonでプロットしたグラフをGoogle ColaboratoryからGoogle driveにアップロードする方法

コメント

タイトルとURLをコピーしました