サブスク疲れで、使った分だけ払うAI英語学習アプリを個人開発して公開した話(③AI音声の読み間違いを、綴りを伏せたAIに聞かせて見つけた話)
①設計思想と技術構成編、②AIに1.9万件を「査読」させた話編 の続きです。今回は、「AIが読み上げた音声が、意図どおりに発音されているか」を、AIで検査した話を書きます。先に書いておくと、私は音声を自分の耳で全数確認できていません。その前提での、検査の仕組みの話です。
TL;DR
- 英単語アプリの音声(OpenAIのTTS)が、日本語由来の語(sake)や同じ綴りで読みが違う語(lead・bow)などで、発音記号(IPA)と違う読み方をしていることが分かった
- 最初に考えた「音声とIPAを見せて、一致しているかAIに聞く」方式(
gpt-audio)は、較正で誤読を25%見逃し、正しい音声を20%誤判定し、約15%は「聞けない」と拒否された - 採用したのは、綴りもIPAも伏せて「聞こえた音」だけを書かせ、あとでテキスト側でIPAと比較する「ブラインド書き起こし」方式(聞き手は
gpt-audio-1.5)。3票の多数決で、較正用のセットでは誤読の見逃しが0/50、誤判定が1/44まで下がった(ただしモデルも変えたので、方式だけの効果とは言い切れない) - 本番の1,127件に適用して、確定不一致が165件。そこから32件を、書き起こし・IPA・辞書の発音を突き合わせて再点検すると、「実際の読み違い」と言えるのは約2/3だった。判定器は「怪しい候補を絞る道具」で、直す前に自分の耳で聴く必要がある
- 別の不良として、0.36秒の「シュド」という雑音だけの音声も見つかった。ファイルサイズの閾値では検出しきれず、**全33,128本(男女2声ぶん)を文字起こしで照合(約$6.4・26.5分)して、壊れた音声は約0.1〜0.2%**と分かった
発端: 「sake」が「サケイ」と読まれていた
英単語アプリには、単語ごとに発音記号(IPA)と、TTSで作った音声を付けています。次のような食い違いが見つかりました。
単語: sake(日本酒)
発音記号: /ˈsɑːki/ (サーキ)
実際の音声: 「サーケイ」寄り (語尾が "kay" になっている)
単語: lead(金属の鉛)
発音記号: /lɛd/ (レッド)
実際の音声: 「リード」 (動詞の lead の読み)
gpt-4o-mini-tts の instructions に「この語は"sah-kee"と読んで」と書いても、読みは変わりませんでした。効いたのは、送るテキスト自体を発音どおりのスペルに置き換える方法(sake → sah-kee)でした(この挙動は、私が使ったモデルでの観察です)。
問題は、「どの語が、どのくらい間違っているのか」を、1.6万語の規模でどう調べるかです。全部を人が聴くのは現実的ではありません。
方式を3回作り直した
①音声とIPAを見せて、一致か聞く → 使えなかった
gpt-audio に音声とIPAを渡して「一致していますか」と聞く方式です。読みが分かっている52本の音声に、正しいIPAと偽のIPA(最小対: led/liːd、sushi/suːˈʃiː など)を組み合わせた110試行で較正したところ、3票の多数決でも:
- 誤読の見逃し: 25%
- 正しい音声の誤判定: 20%
- 約15%の呼び出しで「音声を聞けません」と拒否
でした。IPAを先に見せると、その読みに引きずられて「合っている」と答えてしまうのだろう、と推測しています(ただし、誤判定20%は「正しい音声を不一致と言う」側なので、この仮説だけでは説明できません)。
②軽量モデル(gpt-audio-mini) → 幻聴
聞こえていない別の音を書き起こしました(全然違う語を「幻聴」する)。
③ブラインド書き起こし+テキスト比較 → 採用
綴りもIPAも見せず、聞こえた音だけを書かせます。一括判定では、綴り字風のrespellingと、IPAの両方を書かせました。聞き手は gpt-audio-1.5 です。
出てきた書き起こしを、あとでテキスト側でIPAと比較します。比較には別の軽いモデル(gpt-4.1-mini)を使い、音節数・強勢の位置・強勢母音・子音・文字読みかどうかを構造化して見ています。強勢の位置は、IPAから音節と強勢を数えてコード側でも判定して上書きしました。
「音声を判定する」のではなく、「音声を書き起こす」と「書き起こしを比較する」に分けたのが、効いたと考えています。ただし、①から③では聞き手のモデルも gpt-audio から gpt-audio-1.5 に替えているので、方式だけの効果とは切り分けられていません。拒否がなくなったのは、モデル差の可能性が大きいです。
特定の語を再確認するときの落とし穴
一括判定のあとで、sakeの音声を作り直して、「本当に直ったか」を確かめる場面がありました。そのときに使った簡略版(respellingだけを書かせる)と、踏んだ落とし穴です。
PROMPT = (
"Listen to this short audio clip of a single word or phrase. "
"Do NOT guess the spelling. Write only how it sounds, as a hyphen-separated "
"English-style respelling with the stressed syllable in CAPITAL letters, "
"on one line. Output nothing else."
)
-
拒否票・ゴミ出力を除外する: 「Sorry, I can't help」「JSONを返す」「audio clipをください」などが
gpt-audio-1.5でも混ざりました。票に数えると結果が崩れるので、キーワードで除外して、有効票が3票そろうまで最大8〜14回呼びます。文脈を説明する前置きを足すと、逆に全拒否になったので、プロンプトは最小のままにしました -
分類は「ハイフン連結」と「空白分割」の両方に対応する:
yer-SAH-keeのように語頭に別の語が連結したり、SAW KEEのように空白で分割されたりします。完全一致で数えると、実際は合っているのに不合格扱いになります
import re
# BAD: 拒否・ゴミ出力の語句パターン(例: r"sorry|can't|cannot|json|clip|please|assist")
KEE = re.compile(r"s(ah|aa|a|aw|ar)[-\s]?k(ee|ey|i)\b", re.I) # sake の "kee"。ハイフン・空白の両方に効く
valid = [v for v in results if v and not BAD.search(v)] # results: 聞き手の出力(複数回)
hits = sum(1 for v in valid if KEE.search(v))
- 前後に無音を足す: 音声の前後に0.5〜0.7秒の無音をつけると、語頭の子音(g→d、b→d など)の聞き違いが減りました(全体の精度は同等でした)
- どうしても拒否される長文は、文末だけを切り出す: ffmpegで末尾1.8秒だけにすると通ることがありました
較正用セットでの最終的な数字は、3票の多数決で「誤読の見逃し 0/50・正しい音声の誤判定 1/44」(TTS自体が意図どおり読んでいない語を除いた集合)です。TTSの実際の不良を含めた全110試行では、見逃し2%・誤判定14%でした。
本番の1,127件に当てた結果と、その「精度」
1.6万語のうち、怪しいものに絞って1,127件を判定しました(同綴り異音語・日本語由来語・外来語・フランス語・略語・特殊なIPAなど、区分ごとに抽出。1件あたり概算で約$0.003〜0.007)。
| 区分(抜粋) | 判定数 | 確定不一致 |
|---|---|---|
| 同綴り異音語(lead・bowなど) | 190 | 21 |
| 日本語由来語 | 309 | 40 |
| 外来語 | 248 | 26 |
| 略語(語読み型) | 79 | 16 |
| 短すぎる音声 ※後述の雑音 | 37 | 35 |
| 対照群(普通の語) | 60 | 2 |
| 全体(他の区分を含む) | 1,127 | 165 |
(フランス語44件・特殊なIPA24件など、他の区分は省略しています。)
対照群(60語)でも2件、約3%が「不一致」と出ます。これくらいは方式自体の誤検出とみて差し引く必要があります(60語なので、あくまで目安です)。標識をつけた区分は、それより高く出ています。
ここで大事なのは、確定不一致165件から無作為に32件を選んで、書き起こし・IPA・辞書の発音を突き合わせて再点検したところ、「実際の読み違い」と言えるのは約2/3(21件)だったことです。この段階では耳では聴いていません(書き起こしテキストの再点検です)。残りは、判断が分かれる差、アクセント差(アメリカ英語の t が d のように聞こえる等)、IPA表記の側の問題、判定器の誤検出でした。判定器は、粗い差(別の語として読む・強勢位置・子音・音節数・文字読み)には強い一方、母音の微妙な違いや語尾の弱母音は信用できません。
「AIが不一致と言った」だけでは直さず、直す候補は、最終的に自分の耳で聴いてから差し替える、という運用にしました。
別の不良: 「シュド」という雑音
調べている途中で、別の種類の不良が見つかりました。ファイルサイズがちょうど5,760バイト(0.36秒)の音声が、「シュド」という無内容の雑音になっていました。音声ディレクトリ全体で193ファイルです。
- 35語(5,760Bの33語と、極端に短い数語)を原綴りで再合成して判定したところ、24語は合格、11語は不合格。そのうち chin・troops・speaker・feedback は、3回とも同じ「シュド」でした(同じ綴りで繰り返し壊れる語がある)
- 末尾に「.」を足す、単語を2回言わせる(
chin, chin)と直る例があった - 実際にアプリが使う139本は、文字起こしで内容を確かめながら再生成して差し替えました(133本は1回目で成功、6本は2回目)。無料の範囲の基本語(cow・face・rice など)も29語壊れていたので、最初の印象を損なっていたはずです
そこで「返ってきたmp3が短すぎるときは、キャッシュせず再試行する」ガードを入れました。ところが、後で全数を調べると、サイズの閾値だけでは足りないことが分かりました。
全数検査: 33,128本を文字起こしで照合
「壊れた音声は、文字起こしすれば別の語になるはず」と考えて、gpt-4o-transcribe(英語プロンプト付き)で、本番の単語音声のうち本文が現在の語と一致する**33,128本(男女2声ぶん)**を文字起こしし、綴りと照合しました(軽量な文字起こしモデルは、英語の音声を日本語や韓国語の文字で返すことがあり、不向きでした)。
- エラー0、費用は約$6.4(約¥970)、所要は約26.5分
- 一致度が低い272本を、別の設定(mini+プロンプト・プロンプトなし・whisper)で二次確認
- 括弧つき・略語・数字・同音語・STT側の拒否(不適切語など)を除いた結果、壊れている疑いが強いものが約20本、「別の語に聞こえる」ものが約60本
サイズで見分けられない例もありました。8KB未満の音声(十数本を抽出)を文字起こしすると、ほぼ全てが不良でした(3,456Bの音声が「체크해봐」、6,528Bが「Gitarren」など。壊れた音声には、gpt-4o-transcribe でも韓国語などが返ります)。さらに8,832Bの音声(thumb→「Hair」)や、9,600Bの音声にも不良が混じっていました。バイト数だけでは判別できません。
対応としては、疑いの強いものを再生成して、綴りと0.9以上一致した42本だけを差し替えました(同音語や、綴りと発音が食い違う語 knee・eye・owe・mosque・pho などは「元が正しい可能性」で除外)。この差し替えも、文字起こしとの一致度だけで決めていて、耳では確認していません。壊れた音声の実際の発生率は、**33,128本中の約0.1〜0.2%**でした。
いまは、合成のたびにも照合している
全数検査は一度きりなので、音声を新しく作るたびに、短いテキスト(40字以内)は文字起こしで照合するようにしました。別の語に聞こえたら再試行(最大3回)、最後まで晴れなければ最も綴りに近い音声を採用します(同音語で「再生できない語」を作らないため)。費用は1回あたり約$0.0001、遅延は約1〜2秒です。
限界
- 耳での確認は、まだ一部だけです。判定は「別の音声LLMが書き起こした音」と「IPA」の照合で、真値は聞き手のモデルに依存します
- 判定器は、同じ系統(OpenAI)のTTSの音声を、同じ系統のモデルで聞いているので、系統的な聞き違いは3票でも残りえます
- 詳しく調べたのは、怪しいものに絞った1,127語(全1.6万語の約7%)です。残りの約1.5万語の潜在的な不良は未確認です(全件を照合するのは、費用の面で見送りました)
- 一括判定の総費用は、較正込みで約$8でした(単価は概算です)
- 音素認識器(wav2vec2系など)は試していません。IPAの表記ゆれ(米英・辞書差)と相性が悪そうで、LLMの書き起こしのほうが構造を比較しやすいと考えたためです
学んだこと
- **「AIに一致を聞く」より「AIに聞こえた音を書かせて、人間側のコードで比べる」**方向のほうが、私の環境ではうまくいった(モデルの更新も混ざっているので、断定はできません)
- AIの判定は「候補を絞る道具」。確定不一致の精度は、書き起こしの再点検で約2/3。直す前に、自分の耳で聴く
- サイズや長さのような簡単な指標は、不良の一部しか捕まえない。全数を安く検査できるなら、全数を見るほうが早い(約$6.4で済んだ)
- TTS自体が意図どおり読まない語が混ざると、判定器の較正が崩れる(真値が怪しい語は除いて評価した)
試してみたい方へ
ログイン不要ですぐ単語・フレーズを閲覧できます。発音記号は登録なしでも見られます。
→ https://study.nyangailab.com/?utm_source=qiita&utm_medium=social&utm_campaign=q03_tts_verify
質問・感想・「その検証、穴があるのでは」等のツッコミ、歓迎です。