📝
GPT4+AzureのSTT+各種TTS(VOICEROID/VOICEPEAK/OpenAI/pyttsx3)の実験
概要
「音声認識+会話生成+音声合成」の構成を、以下の観点で試しました。
- 課題a. 自然に会話できるくらいの速度を出す
- 施策1. 特に音声合成部分が遅いので、できるだけ高速な音声合成を探す(発話品質とのトレードオフになりそうだがその探索も含めて)
- 施策2. GPTからの応答をストリームとして扱い、補完全体を待たずに、文単位で音声合成する
- 初期化プロンプトでも、以下の工夫を入れる
- 短い文章で区切って話す
- 間投詞やフィラー等を使用する
- 初期化プロンプトでも、以下の工夫を入れる
- 課題b. 認識エラーによる会話の破綻を防ぐ
- 施策3. 音声認識時の精度(スコア)をGPTに与えることによって、聞き直しなどを自律的に行う事で会話全体の品質を上げることを試みる
全ソースコードはこちらにあります:
各施策の結果とコードの対応箇所など
施策1. 特に音声合成部分が遅いので、できるだけ高速な音声合成を探す(発話品質とのトレードオフになりそうだがその探索も含めて)
音声合成 | 速度 | 読み仮名解釈 | コード | 抑揚など |
---|---|---|---|---|
OpenAI | 〇 | 〇 | text2openai.py |
英語話者っぽい日本語イントネーション |
VOICEPEAK | △ | 〇 | text2voicepeak.py |
最も自然に近く感じる。ちょっとわざとらしいくらい |
VOICEROID | ◎ | 〇 | text2voiceroid.py |
そこそこ自然 |
pyttsx3 | ◎ | 〇 | text2pyttsx3.py |
いかにも合成音声っぽい感じ |
※読み仮名解釈、というのは、仮名以外の文字列の読み上げ時に、変なフリガナになったりしてないか、という意味です。
施策2. GPTからの応答をストリームとして扱い、補完全体を待たずに、文単位で音声合成する
main.py
のon_new_user_message
関数のfor
ループのあたりで実装しています。
以下がポイント:
- 補完API呼出時に、streamオプションを使う
- チャンクをつなげていって、句点が出たら文章完成と見なして、音声合成する
初期化プロンプトでも、以下の工夫を入れています。
あなたは音声対話型チャットボットです。
ユーザと会話する際、1つ1つの文章が短くなるように区切って回答してください。
それでも話し出すまでに時間がかかりそうな場合は、(人間のように)適当なフィラーや間投詞を使って時間を稼いでください。
その場合はフィラーや間投詞の後にも「。」を付けてください。
ユーザの発話からAIの発話までの"間"が、自然かどうか? という観点で、各TTSで実際に会話した印象をまとめる。
- pyttsx3 ... OK。ギリギリ自然。
- VOICEROID, OpenAI ... 惜しい。許せる範囲。
- VOICEPEAK ... 自然な会話というには間が長く感じた。(3秒くらい。数字で書くとシビアだけど実際に話すと昔のWEB会議より遅いと感じた)
施策3. 音声認識時の精度(スコア)をGPTに与えることによって、聞き直しなどを自律的に行う事で会話全体の品質を上げることを試みる
main.py
のmain
で、音声認識+評価結果取得関連の処理をしています。
音声認識器の初期化と、発話評価の有効化:
# 音声認識器を作成
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config, audio_config=audio_config)
# 発話の評価を有効にする
pronunciation_config = speechsdk.PronunciationAssessmentConfig(
reference_text="",
grading_system=speechsdk.PronunciationAssessmentGradingSystem.HundredMark,
granularity=speechsdk.PronunciationAssessmentGranularity.Phoneme,
enable_miscue=False)
pronunciation_config.enable_prosody_assessment()
pronunciation_config.apply_to(speech_recognizer)
発話評価:
result = speech_recognizer.recognize_once()
if result.reason == speechsdk.ResultReason.RecognizedSpeech: # 認識成功時
# 評価結果を取得
pronunciation_assessment_result = speechsdk.PronunciationAssessmentResult(result)
# 結果テキストを取得
user_message = result.text
score = pronunciation_assessment_result.accuracy_score
main.py
のon_new_user_message
関数の冒頭のあたり: スコアが一定値以下の場合は、systemプロンプトで補足する
if score < 80:
messages.append({
"role": "system",
"content": f"[assistant向けの補足情報] 上記のuser発話の認識率は{score}%でした。"
f"userが実際に発話した内容と異なっている可能性に留意してください。"
})
これを付けると、音声認識処理がかなり遅くなってしまい、VOICEROIDでも発話評価無しのVOICEPEAKくらいになってしまう。
感想、今後の課題など
- AIが「もういっかい言って」と聞き直してくれるようにするため、発話評価の仕組みを取り入れてみたが、レスポンスが遅くなる副作用が重いので、プロンプトで何とかしたい。
- 音声でやりとりしていると、より会話が弾む、気がする。反面、間が空いたときに何か喋らないと...というプレッシャーを感じてしまうのは我ながら可笑しかったが、これは慣れの問題だろう。
- マイク入力と音声合成出力が、同じオーディオデバイスになっていると、音声合成がマイク入力に入ってしまうので、今回はAI発話中は音声認識しないようなフローにした。
- フィルタをかけるとか、デバイスを入出力で分けるとか、すると"AI発話中にユーザが割って入る"ことができるようになるだろう。
- 以前ためしたAzureの音声認識の精度が良かったので、今回の実験を行った。実際に会話の中で使うのは難しいかもしれないと思っていたが杞憂で、突飛なことを言わない限りちゃんと会話が成立する事に感動した。
- メール、スケジュールの確認などの機能を組み込んでエージェント化してみたい。
- Looking Glassでアバターと目を合わせて話して、情緒をハックされるか試してみたい。
Discussion