前回に続いて、今回用いて文字起こしを実施していきます。今回は以下順序の3にあたります。
実行順序
1.動画ファイルから音声データを抽出する。
2.音声データを適当な大きさのファイルに分割する。
3.分割したファイル各々に対して、AIを使って文字起こしを実施する。
4.文字起こししたファイルたちをAIを用いて要約し、議事メモのサマリを作成する。
application_service.pyでいうと以下のところです。
import asyncio
from audio_extractor import AudioExtractor
from audio_splitter import AudioSplitter
from summarizer_factory import SummarizerFactory
from speech_to_text import Transcriber
class MeetingMinutesCreationService():
# 対象動画ファイル指定
target_file = "./data/画面収録 2024-02-28 9.05.31.mov"
async def execute(self):
# 音声ファイル抽出
audio_extractor = AudioExtractor(self.target_file)
audio_file_name = audio_extractor.extract_audio()
# 音声ファイルの分割
splitter = AudioSplitter()
separated_files: list[str] = splitter.split_wav(audio_file_name)
# 音声→文字変換
transcriber = Transcriber()
transcribed_files = await transcriber.perform_transcription(separated_files)
# 文字列の要約
summarizer = SummarizerFactory.get_instance().create_summarizer_instance()
summarizer.summarize_text(transcribed_files)
1. 準備
今回はWatson DiscoveryのSpeechToTextというサービスを用います。こちらはIBM Cloud上で展開されているサービスで、ローカルからAPIを呼び出すことで、処理を実行します。
そのためAPIキーが必要になります。この手順については以下のページを参照ください。IBMidの登録が必要になります。
https://cloud.ibm.com/docs/speech-to-text?topic=speech-to-text-gettingStarted&locale=ja
上記のページはspeech to textの入門ページで、pythonの実行環境がなくてもコマンドのみでspeech to textの機能を試せるので、興味があればお試しください。
liteプランは毎月500分の音声ファイルまで無料で使えます。私はliteプランで本件を試しています。
※料金プランの詳細が気になる方はこちらを参照ください。ちなみにこのブログから上記プランに入ったところで私には一切実入しませんので、あしからず。
2. 実装
Watson Discoveryとやりとりするライブラリ、環境変数を読み出すためのライブラリをインストールします。
ibm-watson
moviepy
python-dotenv
scipy
pip install -r requirements.txt
ということで、めんどいので、コードを以下に記載します。
import asyncio
import datetime
import json
from env_settings import EnvSettings
from ibm_watson import SpeechToTextV1
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
class Transcriber():
# 各設定の初期化
def __init__(self) -> None:
env_settings = EnvSettings()
self.api_key = env_settings.get_value_of('WD_API_KEY') # IBM watson discovery speech to text APIKey
self.url = env_settings.get_value_of('WD_URL') # IBM Cloud Tokyo Region
self.wd_concurrent_cons = int( env_settings.get_value_of('WD_CONCURRENT_CONS') or "5") # WD concurrent connections Count
self.speech_to_text = SpeechToTextV1(authenticator=IAMAuthenticator(self.api_key))
self.speech_to_text.set_service_url(self.url)
async def perform_transcription(self, target_files):
semaphore = asyncio.Semaphore(self.wd_concurrent_cons)
tasks = []
for target_file in target_files:
task = asyncio.create_task(self.__transcript_voice_file(target_file, semaphore))
tasks.append(task)
output_files = await asyncio.gather(*tasks)
return output_files
async def __transcript_voice_file(self, target_file, semaphore) -> str:
async with semaphore:
print(' start transcription at ' , datetime.datetime.now(), ', target = ', target_file) # 開始のゴング
loop = asyncio.get_running_loop()
speech_recognition_results = await loop.run_in_executor(None, self.__recognize_sync, target_file) # WDへのリクエストを非同期化
# result全量出力
Transcript=json.dumps(speech_recognition_results, indent=2, ensure_ascii=False)
with open(target_file + '.Result.txt', 'w') as file_obj:
file_obj.write(Transcript)
# textのみ出力
with open(target_file + '.ResultTextOnly.txt', 'w', newline='\n') as file_obj:
for line in speech_recognition_results['results']:
file_obj.write(line['alternatives'][0]['transcript'].replace(' ', '') + '\n')
print(' end transcription at ' , datetime.datetime.now(),', target = ', target_file) # 終了のゴング
return target_file + '.ResultTextOnly.txt'
def __recognize_sync(self, target_file):
with open(target_file, 'rb') as audio_file:
return self.speech_to_text.recognize(
audio=audio_file,
model='ja-JP_Telephony',
content_type='audio/wav',
).get_result()
みたらわかる方であれば、以下は飛ばしてしまって問題ありません。
少し補足します。
概要
本クラスのインスタンスが呼ばれるのは、perform_transcriptionメソッドであり、音声ファイル数分ループさせていて、それぞれのループでリクエストを非同期で投げています。レスポンスを受け取り次第、transcribeした文字列をファイル出力しています。
すべてのループが完了した時点で、ファイル名のリストをreturnします。
環境変数
コンストラクタで必要な変数の設定をします。APIキー含む環境変数の値は.envファイルで管理しています。.envとのやりとりはenv_settingsでラップしています。
非同期処理
javacriptやAPIなどに詳しい方にとっては当然かとは思いますが、今回外部システムにリクエストを投げるので非同期処理が必要となります。これをしないと1リクエストずつ投げて待ってレスポンスを受け取るを繰り返すことになるので、処理時間が発散します。
とはいえ、音声ファイルが存在する分だけ一度にリクエストを投げるのも気が引けるので、semaphoreを設定して、一度に投げるリクエストの上限を設定しています。上限値は.envから取得するような実装にしています。
3. まとめ
Watson DiscoveryのSpeechToTextを使って音声ファイルから文字起こしを実施しました。
どの程度の精度になるかについては次回の投稿でまとめて記載します。
最後まで読んでいただきありがとうございます。
質問等はコメント欄かお問合せにてよろしくおねがいいたします。