📝

GPT4+AzureのSTT+各種TTS(VOICEROID/VOICEPEAK/OpenAI/pyttsx3)の実験

2023/12/10に公開

概要

「音声認識+会話生成+音声合成」の構成を、以下の観点で試しました。

  • 課題a. 自然に会話できるくらいの速度を出す
    • 施策1. 特に音声合成部分が遅いので、できるだけ高速な音声合成を探す(発話品質とのトレードオフになりそうだがその探索も含めて)
    • 施策2. GPTからの応答をストリームとして扱い、補完全体を待たずに、文単位で音声合成する
      • 初期化プロンプトでも、以下の工夫を入れる
        • 短い文章で区切って話す
        • 間投詞やフィラー等を使用する
  • 課題b. 認識エラーによる会話の破綻を防ぐ
    • 施策3. 音声認識時の精度(スコア)をGPTに与えることによって、聞き直しなどを自律的に行う事で会話全体の品質を上げることを試みる

全ソースコードはこちらにあります:
https://github.com/mkt-tokoi/23_Talker

各施策の結果とコードの対応箇所など

施策1. 特に音声合成部分が遅いので、できるだけ高速な音声合成を探す(発話品質とのトレードオフになりそうだがその探索も含めて)

音声合成 速度 読み仮名解釈 コード 抑揚など
OpenAI text2openai.py 英語話者っぽい日本語イントネーション
VOICEPEAK text2voicepeak.py 最も自然に近く感じる。ちょっとわざとらしいくらい
VOICEROID text2voiceroid.py そこそこ自然
pyttsx3 text2pyttsx3.py いかにも合成音声っぽい感じ

※読み仮名解釈、というのは、仮名以外の文字列の読み上げ時に、変なフリガナになったりしてないか、という意味です。

施策2. GPTからの応答をストリームとして扱い、補完全体を待たずに、文単位で音声合成する

main.pyon_new_user_message関数のforループのあたりで実装しています。
以下がポイント:

  • 補完API呼出時に、streamオプションを使う
  • チャンクをつなげていって、句点が出たら文章完成と見なして、音声合成する

初期化プロンプトでも、以下の工夫を入れています。

あなたは音声対話型チャットボットです。
ユーザと会話する際、1つ1つの文章が短くなるように区切って回答してください。
それでも話し出すまでに時間がかかりそうな場合は、(人間のように)適当なフィラーや間投詞を使って時間を稼いでください。
その場合はフィラーや間投詞の後にも「。」を付けてください。

ユーザの発話からAIの発話までの"間"が、自然かどうか? という観点で、各TTSで実際に会話した印象をまとめる。

  • pyttsx3 ... OK。ギリギリ自然。
  • VOICEROID, OpenAI ... 惜しい。許せる範囲。
  • VOICEPEAK ... 自然な会話というには間が長く感じた。(3秒くらい。数字で書くとシビアだけど実際に話すと昔のWEB会議より遅いと感じた)

施策3. 音声認識時の精度(スコア)をGPTに与えることによって、聞き直しなどを自律的に行う事で会話全体の品質を上げることを試みる

main.pymainで、音声認識+評価結果取得関連の処理をしています。

音声認識器の初期化と、発話評価の有効化:

# 音声認識器を作成
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.pyon_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