Dance with Agents

文字起こしの精度を上げようとして、自分の思い込みが5つ潰れた話

固有名詞が、どうしても当たらない

管理人の手元には、41分ほどの音声と、それを人力で文字起こししたテキストがある。今回やったのは、自動の文字起こしを、その人力のテキストにどこまで近づけられるかという取り組みだった。人力のほうが正解で、そこへどれだけ寄せられるかを測っていく。

精度そのものは悪くない。ただ、固有名詞が当たらない。

「モーセ」が「孟前」や「孟瀬」になる。「皇室典範」が「皇室天般」になる。「推古天皇」が「水戸天皇」になる。読み上げれば正しいのに、漢字が違う。

この手の誤りを眺めているうちに、ひとつ気づいたことがある。間違っているのは「聞き取り」ではなく「漢字の選択」だけなのだ。音は合っている。ということは、モデルを大きくしても直らない。必要なのは、正しい表記を知っている辞書のほうだ。

そう考えて、エージェントと一緒に補正の仕組みを作りはじめた。ところが作っていく過程で、管理人(と、エージェント)の「良さそうに見える判断」が、実測でひとつずつ否定されていった。今回はその記録になる。

材料は、すでに手元にあった

辞書を手で書くのは骨が折れる。だが幸い、同じ題材を扱った資料をテキスト化したものが、数十万字ぶん手元にあった。ここには正しい表記がすべて含まれている。さらに口語訳聖書の全文と、以前作った天皇データベース(追号と読み仮名のセット)もある。

これらから自動で用語を抽出させたら、3,000語ほどの辞書ができた。うち固有名詞が1,500語あまり。手入力はゼロだ。過去に作ったものが、別のプロジェクトの材料になるというのは、自分でシステムを作っていると時々起きる。

辞書には表記だけでなく「読み」を必ず持たせた。日本語の誤変換は同音異義語なので、読みを鍵にすれば「孟瀬(モウセ)」と「モーセ」を突き合わせられる。

思い込み①:用語をWhisperに教えれば当たる

Whisperには initial_prompt という引数がある。あらかじめ文脈や語彙を与えておくと、それを踏まえて認識してくれる、というものだ。ここに固有名詞を並べれば当たりやすくなるはず——そう考えて実装した。

まず判明したのは、渡せる量が絶望的に少ないことだった。Whisperのプロンプト窓は223トークン。日本語の固有名詞なら35語ほどしか収まらない。3,000語の辞書とは桁が2つ違う。しかも生成したテキストが積み上がると窓から押し出されるので、41分の音声では冒頭の1〜2分にしか効かない。

それでも35語ぶんは精度に表れるだろうと思っていた。実測すると、こうなった。

モデル 用語プロンプトあり なし 差
turbo 92.8% 95.5% -2.7pt
large-v3 93.4% 95.3% -1.8pt

(数値は既存の文字起こしとの文字レベル一致率。3つの区間の平均)

両モデルで一貫して悪化した。 語の羅列をWhisperが「直前の文脈」と誤解して、出力が引きずられるのだと思われる。良かれと思って足した機能が、逆効果だった。

思い込み②:文脈を引き継がせれば漢字が安定する

Whisperには condition_on_previous_text という設定があり、直前に認識したテキストを次の窓のヒントとして渡す。日本語の漢字選択は文脈依存が強いのだから、これは有効なはずだ——という理屈で有効にした。

41分を通して回した結果がこれだった。

  • 同一文字の羅列(人人人人人…)が4箇所、886文字
  • 同じ節を繰り返すループが8行
  • 存在しない内容の捏造

そして一致率は 93.4% → 80.7% に落ちた。

原因ははっきりしている。一度ループが生じると、そのループ自体が次のプロンプトに入って自己強化する。 文脈の引き継ぎは、良い文脈だけでなく壊れた出力も引き継いでしまう。

面白いのは、この現象が75秒の短いクリップでも再現したことだ。ほぼ同じ内容の一文が、間を置かずに2回繰り返された。 長時間回さなくても観測できる。

なおハルシネーションそのものについては、ハルシネーション——AIは、嘘をついているわけではないで扱っている。あちらはLLMの話だが、音声認識モデルでも似た構造で起きる。

思い込み③:large-v3のほうがturboより固有名詞に強い

管理人は最初、エージェントから「turboではなくlarge-v3を使うべき。turboは固有名詞で落ちる」と助言を受けた。もっともらしい。実際そう書かれている記事も多い。

だが管理人が元々使っていたのは turbo で、その出力のほうが、エージェントがlarge-v3で作り直したものより明らかにきれいだった。そこで測ってみた。

構成(同一クリップ・同一設定) 一致率
turbo 95.5%
large-v3 95.3%

実質同点だった。 そして41分の音声を通したとき、turboは5分ほどで終わる。large-v3は3倍以上かかった。「turboは固有名詞に弱い」は、少なくともこの音源では成立しなかった。

助言が間違っていたというより、測らずに一般論を当てはめると外れるという話だと思う。

思い込み④:日本語特化モデルなら日本語に強い

kotoba-whisperという、日本語に特化した蒸留モデルがある。日本語データで追加学習されているのだから、この用途に向いているはずだ——と期待して比較対象に加えた。

結果は 64.3%。他のどの構成よりも大幅に悪かった。一文がまるごと欠落し、別の箇所では固有名詞が一般的な語に置き換わっていた。

日本語特化=自分の用途に良い、ではなかった。 何に特化しているのかは、自分の題材で測らないとわからない。

思い込み⑤:無音対策(VAD)で精度が上がるはずだ

音声を調べたら、41分のうち45%(18.9分)が無音だった。ゆっくりとした語りで、間が長い。Whisperは無音を渡されると何か喋っていると推測して捏造する性質があるので、無音を除去するVAD(音声区間検出)を挟めば幻覚が止まるはずだ、と考えた。

無音が密集した区間を選んで測り直した結果がこれだ。

構成 一致率 幻覚
turbo(VADなし) 91.7% 0
turbo+VAD 90.7% 0
turbo+VAD+幻覚抑制 90.7% 0

VADなしでも幻覚は出なかった。 VADは精度を1pt下げ、専用の幻覚抑制パラメータに至っては数値が完全に同一で、何の効果もなかった。

つまり最初の実行で幻覚が生じた真因は無音ではなく、思い込み②の文脈引き継ぎによる蓄積だった。症状から原因を推測すると、間違った対策に労力を使う。

結局、いちばん単純な構成が勝った

5つの実験を経て残ったのは、turbo・用語プロンプトなし・文脈引き継ぎなし・VADなしという、最も単純な構成だった。付け足そうとした改善が、ひとつ残らず有害か無意味だった。

この構成で41分を回すと、幻覚0・反復0の出力が5分ほどで得られた。

それでも辞書は必要だった——3つのモデルが3通りに間違える

ここが今回いちばん腑に落ちた点になる。ある翻訳家の名前の箇所で、3つのモデルがそれぞれ違う漢字に変換した。 名字は3つとも当たったが、下の名前が3通りに割れ、どれも正解ではなかった。

これは当然で、この名前は学習データに十分な量がない。モデルを替えても、設定を詰めても、原理的に当たらない。当てるには「この文脈ではこの表記が正しい」と知っている辞書が要る。

同じ理由で、誤りの側も日本語として実在する語のときは、音だけでは決まらない。どちらの表記が正しいかは文脈しか教えてくれないので、文脈を読める層——最終的にはLLM——に判断させるしかない。

だから補正は3段構えにした。誤りの側が日本語として存在しない語(皇室天般→皇室典範)は無条件に置換、読みが一致するもの(孟瀬→モーセ)は自動置換、誤りの側も実在する語はLLMに文脈判断させる。

いちばん学びが大きかったのは、エージェントを止めたとき

作業中、エージェントが読みの類似度による候補探索を実装したのだが、ノイズが多いという理由でその機能を削ろうとした。削除理由として挙げられた例のひとつが、ある分野の専門用語への変換だった。無関係な誤変換の代表例、という扱いだ。

管理人はここで引っかかった。その変換は、正しいかもしれない。 扱っている題材の文脈なら、登場してもおかしくない語だった。

調べさせたら、コーパスに実在した。関連する語も繰り返し登場していた。「無関係」ではなかった。

さらに、そこから芋づる式にエージェントの作った辞書に誤りが1件見つかった。先ほどの翻訳家の名前の箇所を、エージェントは一般知識から別の人物の名前だと推測し、無条件置換の辞書に入れていたのだ。コーパスを検索したら、正しい名前は16回も出現していた。推測する必要などなかった。

この2つは、管理人が「それ、本当に間違い?」と一度立ち止まらなければ、両方とも通っていた。エージェントの判断を検証できるのは、その領域を知っている人間だけという場面がある。今回の題材は管理人にとってなじみのある分野だったから気づけた。

そして最終的に、設計もそのぶん良くなった。類似度探索は削らずに残し、自動置換は絶対にせず、LLM判断に回すという位置づけに変えた。「候補を漏らさず挙げる係」と「決める係」を分けたわけだ。

「コーパスで裏を取れ」にも、限界があった

ここまでの教訓は「推測せずコーパスを引け」だった。ところがその直後、その教訓が通用しない例に足をすくわれた。

仕上げた自動の文字起こしを自分のGoogle Driveにアップロードしてもらい、中身を眺めていて気づいた。冒頭に登場する管理人自身の名前が、同音の別表記になっていた。 読みは同じ「ひでたけ」で、漢字が違う。今回ずっと相手にしてきた誤りと、まったく同じ型だ。

そして今回は、自動の側のどの手段でも救えなかった。

  • 数十万字のコーパス → 名字までしか登場しない。フルネームの記載がない
  • 聖書や天皇データベース → 当然、無関係
  • どのモデル・どの設定 → 音は合っているので原理的に区別できない

本人にしか確認手段がない語、という層が存在する。コーパスは強力な材料だが、そこに書かれていないものは得られない。しかも厄介なことに、こういう語ほど文書の冒頭に登場して、目立つ。

対処は単純で、手動辞書に1行足すだけだ。ただ、「足すべきだ」と気づけるのは、その文書の当事者だけという点は変わらない。辞書は自動生成できても、辞書に何が欠けているかの判断は自動化できない。

まとめ

  • 音は合っていて漢字だけ違う誤りは、モデルを替えても直らない。辞書の仕事になる
  • 良さそうに見える改善策(用語プロンプト、文脈引き継ぎ、VAD、日本語特化モデル)は、5つ試して5つとも実測で否定された
  • 比較するときは全文を回さない。代表区間を切り出せば、1構成あたり数十秒で済む
  • 評価指標は「全体の正確さ」ではなく「自分が困っていること」に合わせる。今回は固有名詞の当たり方
  • エージェントが「これはノイズだから削る」と言ったとき、その例が本当にノイズかは、題材を知っている自分にしか判断できないことがある
  • 材料(コーパス)で裏が取れる語と、当事者にしか分からない語がある。辞書は自動生成できても、何が欠けているかの判断は自動化できない

実測は面倒だ。だが今回は、面倒がった分だけ間違った方向に労力を使うところだった。

LLM / エージェントの記事

LLM / エージェントのページへ →