hayamimi 早耳 v0.3.1 — 認識精度の修正と数字の読みやすさ改善
外部ベンチマーク(FLEURS)での検証をきっかけに、認識パイプラインの実バグ修正と出力改善を行いました。あわせて、標準ベンチマークでの立ち位置を1枚のグラフに整理しています(下図)。
標準ベンチマークでの立ち位置(新規計測)
FLEURS test split(5言語×各100クリップ)を、同一クリップ・同一採点・同一CPUで whisper-large-v3-turbo と直接比較しました。精度はturboがja/en/koで上回ります(turboには正解言語を与える有利条件)。hayamimiの強みは、12〜21倍の処理速度、言語の自動判定(500クリップ中498正解)、そして発話中に字幕が出る逐次処理(Whisper系は発話終了までまとめて処理)です。再現手順は scripts/eval_fleurs_bench.py、詳細は docs/BENCHMARKS.md。
実際の放送音声では傾向が変わり、日本語は hayamimi が大きく上回ります(CER 3.8% vs 13.8%、docs/SCORECARD.md / docs/COMPARISON.md)。朗読音声と実環境音声で景色が違うこと自体も、両方の数値を公開して正直に記録しています。
長い音声で最初の文が欠ける問題を修正しました
複数の文を含む音声をまとめて認識させると、最後の文しか出力されないことがありました。原因は音声の切り出しではなく、認識モデルが「1回の認識=1つの発話」を強く仮定していることです。無音で区切ってから認識する方式をまず試しましたが、全クリップに適用すると別の悪化を招いたため(A/B実測で確認)、**「出力が不自然に短い時だけ区切って再認識し、迷ったら元の結果を残す」**方式にしました。通常の音声への影響はゼロ(130クリップでバイト単位一致)で、欠落クリップは CER 0.667→0.111 まで回復します。
息継ぎなしで文が続く音声(無音が0.35秒未満)は今も救えません。既知の制限としてREADMEに明記しています。
数字がアラビア数字で出るようになりました(ja/zh/yue)
「四万人」→「4万人」「八零二点一」→「802.1」のように、字幕として読みやすい表記に変換します。固有名詞や慣用表現(「一番」「四国」など)を巻き込まないよう、明確に数値と判断できるものだけを変換する保守的な設計です(FLEURS実測: zh -2.0pt / yue -1.1pt)。
日本語の清書に「二段照合」オプションを追加しました
--refine-ja-second-opinion を付けると、清書時に第二のモデル(parakeet-ja)でも認識し、両者がほぼ一致した時だけそちらを採用します。不一致は背景音声混入のシグナルとして従来出力を守ります。実放送50分の検証で CER 7.2%→4.0%。既定はオフ(メモリ実測+約250MB)。
組み込み向けの改善
- 置換辞書・数字変換の除外/強制を実行中に差し替えできるAPIを追加(Python setter+HTTPエンドポイント、Flutter側は
textTransformフック) - ライブ字幕が発話冒頭の単語を失わないこと(1秒プリロール)を実測で確認し、回帰テストで固定
- モデルファイル欠如時にプロセスごと落ちる経路を封鎖し、例外として扱えるようにしました。ライブ処理に停止トークンを追加し、組み込みアプリが「停止ボタン」を実装できるようにしました
- ネットワーク入力(
--ws-host)の既定を0.0.0.0から127.0.0.1に変更しました。LAN公開は明示指定になります(破壊的変更: LAN越しのスマホ連携を使っている場合は--ws-host 0.0.0.0を付けてください)
計測の正直な注記
- 社内スコアカードの zh は5.3%→6.6%に見えますが、悪化分は参照文が漢数字の箇所との表記ズレ(出力「1000多年」vs 参照「一千多年」)で、認識誤りではありません(
docs/SCORECARD.mdの注記)。 - 話者分離は文献裏付きの追加実験2本(話者数ヒント/ゲート付き重複検出)も実測しましたが、採用基準に届かず不採用としました。経緯と数値は
docs/DIARIZATION_PLAN.md§19-20 にあります。
English summary
Fixes a real recognition bug found via FLEURS benchmarking: multi-utterance audio fed to the offline path lost leading sentences (the model assumes one utterance per decode). The fix decodes normally and only retries with silence-based splitting when the output looks truncated — 130-clip byte-identity for normal audio, CER 0.667→0.111 on the worst clip. Adds conservative CJK inverse text normalization (四万→4万; measured zh -2.0pt / yue -1.1pt on FLEURS), runtime-swappable replacement/ITN dictionaries (plus a Flutter textTransform hook), an opt-in ja refine-time second opinion (adopt parakeet-ja only when both models agree; 7.2%→4.0% CER on a 50-minute broadcast set, +~250MB RSS), process-safety hardening (no more process-killing exits on missing models; a stop token for embedders), and a breaking change: --ws-host now defaults to 127.0.0.1 (pass 0.0.0.0 explicitly for LAN use).
New unified benchmark: FLEURS test, 5 languages x 100 clips, same clips/scoring/CPU vs whisper-large-v3-turbo (language given to turbo). turbo is more accurate on ja/en/ko under that condition; hayamimi runs 12-21x faster with automatic language identification (498/500) and true streaming subtitles. On real broadcast Japanese the picture reverses (CER 3.8% vs 13.8%) — both are published, with reproduction scripts.
🤖 Generated with Claude Code