faster-whisper の認識精度を上げる実践テクニック集 — initial_prompt には辞書ではなく「直近の会話」を渡す
リアルタイム音声対話AI(アバター/ボイスボット)のSTTとして faster-whisper を運用する中で、実測ベースで効果のあった精度改善テクニックをまとめます。単発の文字起こしバッチではなく、会話システムに組み込む文脈での知見です。
1. initial_prompt — 一番効くのに一番誤用されやすい
whisper の initial_prompt は「この音声の直前にあった書き起こし」としてデコーダに与えられ、音が曖昧なときプロンプト中の綴りへ倒れる语彙バイアスとして働きます。固有名詞の誤認識(例:「社員数」→「シャインズ」)への最有力対策です。
やりがちな失敗: 固有名詞辞書を渡す
「会社名・製品名のリストを渡せばいい」と考えがちですが、実運用では破綻します。
- テナント(顧客企業)ごとに語彙は違い、辞書は際限なく膨らむ。20〜30語入れた程度では現場の固有名詞はカバーできない
- 登録・更新の運用コストが永続的に発生する
- 長いプロンプトには副作用がある(後述)
効いた方式: 「直近のアバター発話」をそのまま渡す
会話システムには、辞書より優れた語彙ソースが既に流れています。直前にTTSで喋らせた自分(アバター)の発話テキストです。
- 台本・回答文には、画面上の固有名詞が正しい表記で既に含まれている
- 会話が進むほど、その場のトピックの語彙が自動的にヒントに入る。登録作業ゼロ
- 実装は deque に発話を積んで末尾N文字を渡すだけ
class SttHint:
def __init__(self, max_chars=160):
self._items = deque()
self._max = max_chars
def add(self, text): # アバター発話のみ積む
...
def text(self):
return "".join(self._items)[-self._max:]
segments, info = model.transcribe(
audio, language="ja",
initial_prompt=(hint.text() or None),
)
2つの重要な注意
- ユーザー側のSTT結果は絶対に混ぜない。一度の誤認識がヒントに入ると、以後の認識がその誤りに引っ張られて固定化する。ヒントに入れてよいのは表記が保証されたテキスト(自分の発話・台本)だけ
- 長さは150〜200字程度まで。長いプロンプトは whisper がプロンプト文自体を「聞いた」ことにする幻聴の副作用を起こす
2. 入力音声の品質 — STTの前で勝負は半分決まっている
WebRTC経由でマイク音声を受ける構成では、Opusの既定ビットレート(実質30kbps前後)が地味に効きます。無圧縮PCMをWSで送っていた旧構成からWebRTCへ移行した際、限界条件(相手の音声と同時に喋る二重発話など)での誤認識が増えました。
- answer SDP の fmtp に
maxaveragebitrate=128000;useinbandfec=1を指定(answer側fmtpが送信側エンコーダを律する) - インバンドFECはTURNリレー経由等のパケット損失にも効く
- エコーキャンセラ(AEC)を使う構成では、二重発話中の近端抑圧で語頭が歪むことを前提に据える。これはSTT側では治せないので、本節の底上げ+第1節のヒント+第5節のLLM補正で挟み撃ちにする
3. confidence(avg_logprob)での足切りは危険
「信頼度が低い結果を捨てればノイズに強くなる」は直感的ですが、実測で否定されました。本物の発話でも avg_logprob は -0.8 台まで普通に落ちます。閾値を置くと実発話を捨て始め、「反応しないことがある」という最悪の症状(ユーザーから原因が見えない)を作ります。
- 足切りは入れず、値はログに出し続ける(診断には非常に有用)
- ノイズ棄却は confidence ではなく、後段(LLM/アプリロジック)の文脈判断に任せる
4. 非音声入力の病理 — 無音・純音は「遅い」
正弦波や無音を投げると、whisper は温度フォールバックを繰り返して3秒の音声に5〜8秒かかることがあります(実測)。実発話なら同じ環境で1秒未満。
- ヘルスチェックやウォームアップに正弦波・無音を使うと、レイテンシ計測が病的ケースを測ってしまう。実発話サンプルを使う
- 逆に、本番でこの遅さを観測したら「非音声が流れ込んでいる」サイン(VAD閾値・マイク経路を疑う)
関連して vad_filter=True(Silero VAD 前段)は入れておく価値があります。無音区間の除去は速度と幻聴の双方に効きます。
5. STTで完結させない — LLM後段補正が最後の砦
どれだけ整えても、二重発話の固有名詞などは一定確率で化けます。会話システムなら、LLMに「入力はSTT書き起こしである」と教えるのが費用対効果最強の一手です。
【聞き間違いの解釈】
- 入力は音声認識の書き起こしで、音の近い誤変換を含み得る(例:「社員数」→「シャインズ」)
- 語が文脈で不自然な場合、参考資料・直前の会話から音の近い語を探して解釈し、
冒頭で「◯◯のご質問ですね」と一言添える
- 音の近い語が見つからない造語は、説明を推測で作らず聞き返す
最後の一文が重要です。これが無いと、LLMは知らない単語「シャインズ」の製品紹介を堂々と創作します(実機で発生)。人間が乱れたチャット入力を文脈で読めるのと同じ能力を、明示的に許可してやるイメージです。
6. 運用の罠: デプロイ直後の「偽の劣化」
- faster-whisper のモデルは多くの構成で遅延ロードされ、初回リクエストにロード時間(GPUで数十秒〜)が乗る
- 同居プロセス(TTSのプリウォーム等)がGPU/イベントループを占有していると、/transcribe の応答が数十秒待たされ、コードの退行と見分けがつかない
- 対策: デプロイ後はロード完了ログを確認してから計測する。E2Eテストにウォームアップ確認を組み込む。「リリース直後の不合格」はまず環境要因を疑う
まとめ
| テクニック | 効果 | コスト |
|---|---|---|
| initial_prompt に直近の自分の発話(辞書は使わない) | 固有名詞誤認識に最有力 | 実装数十行・運用ゼロ |
| ユーザーSTT結果をヒントに混ぜない | 誤り固定化の防止 | 設計判断のみ |
| Opus 128kbps + inband FEC | 限界条件の底上げ | SDP munging 数行 |
| confidence足切りをしない | 「無反応」事故の防止 | むしろ削除 |
| vad_filter + 実発話でのウォームアップ/計測 | 速度と幻聴 | 設定のみ |
| LLMへの「STT入力宣言」+造語は聞き返す | 化けた語の最終補正 | プロンプト数行 |
STT単体のベンチマークを追うより、パイプライン全体(入力品質 → ヒント → 認識 → LLM補正)で誤りを挟み撃ちにする方が、体感精度は速く上がります。