Skip to content

Releases: oboroge0/hayamimi

v0.3.1 — 認識精度の修正と数字の読みやすさ改善 / Recognition fixes + CJK number normalization

Choose a tag to compare

@oboroge0 oboroge0 released this 01 Sep 15:05

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

v0.3.0 — 話者分離の本格化とスマホ対応 / Real diarization + mobile support

Choose a tag to compare

@oboroge0 oboroge0 released this 31 Aug 05:20

hayamimi 早耳 v0.3.0 — 話者分離の本格化とスマホ対応

複数人の会話で「誰の発言か」を自動で付ける機能を本格化し、スマホ(Flutter)でも動くようになりました。デモ動画の字幕と話者ラベルは、実際の会議音声(AMI Meeting Corpus、CC BY 4.0)に対する実出力です。

話者分離を本格化しました

これまでの --speakers は、発話ごとに声の特徴を比べて話者を切り替える簡易方式でした。READMEにも「フルの話者分離ではない」と明記していたとおり、複数人の会議では話者の取り違えが目立ちます。

v0.3.0では、清書(二段パス)のタイミングで発話グループ全体をあらためて話者分離モデルにかけ直し、その結果をセッション全体の話者ラベルに対応付ける方式にしました。速報の字幕はこれまでどおり即座に出て、少し遅れて出る清書側で話者の境界が正確になります。

実際の会議音声(AMIコーパス、4人会議×5本・計50分)で測定したところ、話者分離誤り率(DER)は従来方式の25.7%から13.9%に下がりました。清書ではグループ内の話者交代も復元されるため、短い相槌が別の話者として正しく分かれて表示されます。

追加で必要になるモデルは話者境界検出用の約7MB(pyannote segmentation-3.0、MIT)だけです。声の特徴抽出には従来と同じモデルを使い回すため、メモリへの影響はほとんどありません。速報字幕の応答時間への影響も平均50ms未満でした。

画面に表示される話者数が実際より多くなる問題には、一度しか現れないラベルを「S3?」のような仮表示にする対応を入れました。2回目の出現で確定表示になります。

スマホで動くようになりました

Flutter製のライブラリ hayamimi_core を追加しました。マイク→VAD→音声認識→清書のパイプラインを、他のアプリにそのまま組み込めます(スマートグラスのコンパニオンアプリ等を想定)。字幕イベントの形式はデスクトップ版・OBSオーバーレイと共通です。

iPhone 15の実機で速度を測定したところ、int8モデルの処理速度はPCの約4.8倍(RTF 0.013)でした。PCのx86では「int8がfp32より遅い」逆転がありましたが、ARMのint8カーネルが効くスマホでは int8 が最小かつ最速です。この実測を根拠に、推奨構成(ja単体 約72MB / 5言語切替 約396MB)をライブラリのREADMEに記載しました。

導入の手間を減らすため、モデルの自動ダウンロード機能(進捗表示・チェックサム検証・再実行時の差分取得つき)も追加しています。

修正した不具合

  • 起動時に画面が固まることがある問題を修正しました。モデル読み込み(最大396MB)が画面の描画を止めていたことが原因で、読み込みを裏側(バックグラウンドisolate)に移しています。
  • OSにマイクを取り上げられた時(バックグラウンド移行や他アプリの割り込み)に、無反応のままになるのではなくエラーとして通知するようにしました。
  • ボタンの二度押しなど、開始・接続・破棄の二重呼び出しで起きるメモリリークや例外を防ぐようにしました。
  • --speakers 使用時に、終了間際の清書が出力されずに失われることがある問題を修正しました。

精度改善の実験について(正直な報告)

話者分離の精度をさらに上げるため、閾値調整・割当ロジック変更・全体再クラスタなど8方向の実験を行いましたが、すべて採用基準に届きませんでした(現在の13.9%は現構成での実測上の最適値です)。どこが限界で、なぜかの記録は、失敗した実験も含めて docs/DIARIZATION_PLAN.md に残しています。残る最大の伸びしろは同時に複数人が話す「重なり発話」(約3ポイント分)で、回収できることは試作で確認済みです。今後の課題とします。

既知の制限

  • 重なり発話(同時に複数人が話す区間)はまだ扱えません。
  • スマホ版で、発話ごとの認識処理は画面と同じスレッドで動くため、発話のたびに0.1〜0.5秒ほど画面が引っかかることがあります(改善予定: #24)。

English summary

Speaker labels are upgraded from simple turn-taking to real diarization: the live path still assigns S1/S2/... instantly, and the refine pass re-diarizes each utterance group with pyannote segmentation-3.0 (~7MB, MIT) and maps the result back onto the session's labels. On 5 real AMI meetings (50 minutes total), DER dropped from 25.7% to 13.9%, with short backchannels now split correctly. One-off labels are shown provisionally as "S3?" until confirmed.

Mobile support lands as hayamimi_core, an embeddable Flutter library (mic → VAD → ASR → refine, wire-compatible subtitle events) with a model auto-download helper. Measured on a real iPhone 15: int8 runs at RTF 0.013, ~4.8x faster than the same files on a desktop x86 CPU — on ARM, int8 is both the smallest and the fastest choice.

Also fixed: a startup freeze (model loading now happens off the UI isolate), silent sessions after the OS revokes the mic, double-start/dispose leaks, and lost refine output at shutdown with --speakers.

Honest note: 8 further accuracy experiments (threshold sweeps, assignment changes, global re-clustering) all failed our adoption bar — the full record, negative results included, is in docs/DIARIZATION_PLAN.md. The remaining ~3pt headroom is overlapping speech, recoverable per our prototype; deferred to future work.

The demo video's subtitles and speaker labels are actual pipeline output on real meeting audio (AMI Meeting Corpus, CC BY 4.0).

🤖 Generated with Claude Code

hayamimi 早耳 v0.2.0 — 言語切替を作り直しました / language switching, redesigned

Choose a tag to compare

@oboroge0 oboroge0 released this 27 Aug 05:03

言語切替の仕組みを大きく見直しました。ぜひ実際の環境で試してみてください。

言語切替を改善しました

v0.1.xまでの自動言語切替には、主に2つの問題がありました。

  • 最初の発話を誤判定すると、その後もしばらく間違った言語が使われ続ける
  • 相槌などの短い発話を別の言語と誤認し、意図せず切り替わる

実際にマイクを使ってテストする中でも、この両方のケースを確認していました。

v0.2.0では、言語判定に2つの判定器を使用し、両方の結果が一致した場合にのみ言語を切り替える方式へ変更しました。

検証では、短い発話に対する単独の判定器の正解率が約61%だったのに対し、2つの判定結果が一致したケースでは約98%まで向上しました。測定方法や詳細な結果は docs/LID.md にまとめています。

この変更により、言語が明確な発話では素早く切り替えつつ、相槌のような曖昧な短い発話では不要な切替が起きにくくなっています。

また、速報として表示される途中字幕で言語判定を誤った場合でも、その後に生成される確定字幕では再判定され、修正されるようになっています。

3つのモードから選べます

起動オプション 動作
--mode single --lang ja 指定した言語のみを使用します。自動切替は行いません
--mode balanced 新しい判定方式です。v0.2.0からの標準モードです
--mode fast 判定結果に応じてすぐに切り替える、従来に近い動作です

安定性を重視する場合は balanced、単一言語で使用する場合は single、切替速度を優先する場合は fast を選べます。

実際の環境で試してみてください

開発環境では、5言語を混在させた約12分の連続音声でも動作を確認しており、従来の即時切替方式と比べて意図しない言語切替が7回から3回に減ることを測定しています(docs/SOAK.md)。

とはいえ、言語切替の使いやすさは、話し方、声、マイク、周囲のノイズなどによって変わります。実際の配信や会議でどのように動くかを知りたいと考えています。

普段どおり使ってみて、

  • 切り替わってほしい場面で切り替わらなかった
  • 意図していない場面で言語が切り替わった

といったケースがあれば、使用していた言語やその時の状況とあわせてIssueで報告してもらえると助かります。

実際の使用例が増えるほど、今後の調整や改善に活かしやすくなります。

その他の変更

翻訳先として指定できる言語を拡張しました。

--translate es のように、M2M-100が対応する約100言語を指定できます。スペイン語については品質検証も行っています。

この変更のきっかけを作ってくれた @wheesnoza さん、そしてhayamimi初の外部コントリビューションをありがとうございました。#7

OBS向け字幕オーバーレイも改善しました。

確定した字幕と、現在話している途中の字幕を別々のソースとして配置できます。

  • ?show=final で確定字幕のみ表示
  • ?show=partial で途中字幕のみ表示

そのほか、以下を含む複数の不具合を修正しています。

  • 広東語へ正しく切り替わらない問題
  • 長時間話すと字幕が消えることがある問題

破壊的変更はありません。モデルの再ダウンロードも不要です。

English summary

Language switching has been redesigned in v0.2.0.

In v0.1.x, an incorrect decision on the first utterance could cause the wrong language to remain active, while short interjections could sometimes trigger unintended switches.

The new balanced mode uses two language detectors and switches only when their results agree. In our evaluation, a single detector achieved about 61% accuracy on short utterances, while cases where both detectors agreed were correct about 98% of the time. Details are available in docs/LID.md. In a 12-minute five-language soak test, unintended switches dropped from 7 (previous instant-switching behavior) to 3 (docs/SOAK.md).

Three modes are now available.

  • single keeps the language fixed
  • balanced uses the new agreement-based system and is now the default
  • fast prioritizes immediate switching and behaves similarly to the previous system

Please try it during real streams, meetings, or conversations. If the language switches unexpectedly, or fails to switch when it should, opening an Issue with the languages involved and some context would be very helpful.

--translate now also accepts roughly 100 target languages supported by M2M-100. Spanish has been validated. Thanks to @wheesnoza for the suggestion and for hayamimi's first external contribution in #7.

The OBS subtitle overlay can now show confirmed and in-progress subtitles as separate sources using ?show=final and ?show=partial.

There are no breaking changes, and no model downloads are required.

🤖 Generated with Claude Code

hayamimi 早耳 v0.1.2 — user-reported fixes & network audio input

Choose a tag to compare

@oboroge0 oboroge0 released this 26 Aug 13:23

hayamimi 早耳 v0.1.2 — user-reported fixes & network audio input

Two days after the first release, users started filing real bug reports, and we started wiring hayamimi up to devices beyond the PC it runs on. v0.1.2 contains those fixes, a network audio input, and one change of default worth knowing about.

Heads-up: the default port changed

The --serve default port moved from 8765 to 8833 (8=ha, 8=ya, 33=mimi — it spells the project name). If your OBS browser source points at localhost:8765, update the URL, or launch with --serve 8765 to keep the old port.

Fixes from user reports

Both issues below were filed by @kentatorimoto with reproducible, root-caused reports that made the fixes straightforward — thank you.

  • --lang-switch-guard now works as documented (#2). The README promised that short new-language detections are treated as noise, but the implementation only used the guard to suppress a fallback path, so two consecutive short misdetections could still flip the subtitle language mid-session. A detection shorter than the guard now never advances the language switch, and the real switch knob is exposed as --lid-switch-confirm N (default 2).
  • --hotwords failures are loud now (#1). The Japanese tier's model ships vocabulary that cannot encode hotwords under the current configuration, so every hotword was silently dropped — the underlying library only murmurs on stderr and exits successfully. hayamimi now verifies each hotword against the model vocabulary at startup and prints an unmissable warning, with --replace suggested as the workaround. A real fix (a byte-level hotword encoder) remains future work.

New: network audio input

--input ws starts a WebSocket endpoint that accepts streamed audio from another device on your LAN — the first building block for the phone and Stack-chan clients this project is working toward. Ingested audio flows through the normal pipeline, so a remote microphone's transcript shows up on the existing dashboard and OBS overlay. The protocol is a deliberately tiny WebSocket subset that bare-metal firmware can speak, and scripts/ws_mic_client.py is a working reference client.

Tests grew from 23 to 52. No model changes; download_models.py output is unchanged.

日本語での要約

公開から2日で届いたユーザー報告2件(@kentatorimoto さん、再現手順と原因分析まで揃った丁寧な報告をありがとうございました)を修正しました。--lang-switch-guard はREADMEの説明どおり「ガード秒数未満の短い判定はノイズ扱い」で動くようになり、切替に必要な連続一致回数も --lid-switch-confirm として公開しています。日本語でホットワードが無言のまま無効になっていた問題には、起動時チェックと明確な警告を追加しました。新機能として、LAN上の別デバイスからマイク音声を送り込める --input ws を追加しています(スマホ・スタックチャン連携への布石で、受けた音声は既存のダッシュボードやOBSオーバーレイにそのまま流れます)。また --serve のデフォルトポートを8765から8833(は・や・みみ)に変更したため、OBSのブラウザソースURLの更新が必要です。

🤖 Generated with Claude Code

hayamimi 早耳 v0.1.1 — noise robustness, measured honestly

Choose a tag to compare

@oboroge0 oboroge0 released this 25 Aug 23:03

hayamimi 早耳 v0.1.1 — noise robustness, measured honestly

v0.1.0's accuracy numbers were all measured on clean speech. This release asks what happens when the audio isn't clean — noisy rooms, background chatter, and (by user request) singing — measures it, and fixes what could be fixed.

Noise robustness

We ran the full pipeline against white, pink, and babble noise at four signal-to-noise levels for all five main languages (docs/NOISE.md). The clearest finding: background chatter is by far the most destructive noise type, and it breaks language identification before it breaks transcription — one wrong guess routes the audio to the wrong language model and the subtitles collapse.

The fix targets that exact failure: switching languages now requires two consecutive agreeing detections, so a single noise-induced misdetection no longer flips the subtitle language. In the noise evaluation this improved language identification in 15 of 40 noisy conditions with zero regression on clean audio. The trade-off is that a foreign phrase only one segment long no longer triggers a switch; lid_switch_confirm=1 restores the old instant behavior.

We also tested the obvious alternative — putting a speech denoiser (GTCRN) in front of the recognizer — and rejected it based on the same measurements: it made accuracy worse in 49 of 60 noisy conditions and even degraded clean audio. The A/B data is in docs/NOISE.md, so the decision can be re-examined if better denoisers appear.

Singing

A user asked how well hayamimi transcribes singing, so we built an evaluation set and measured it (docs/SINGING.md). A cappella Japanese lands at a kana-level character error rate of 0.24, versus 0.15 for the same singer reading the same lyrics — degraded, but readable as subtitles. English and Korean songs fall to roughly 50% error, and when language identification guesses wrong, that clip is lost entirely. If you plan to sing, pinning the language with --lang ja avoids the worst case. Accompanied music is untested and will be harder.

Reproducibility

The noise and singing evaluation sets are both built from public corpora by scripts in the repository (make_noisyset.py, make_singset.py), and the scoring scripts (eval_noise.py, eval_singing.py) are included. Unit tests grew from 15 to 23. No model changes; download_models.py output is unchanged.

日本語での要約

v0.1.0の精度はクリーンな音声での測定でした。このリリースでは、ノイズ環境と歌声でどこまで劣化するかを実測し、直せる部分を直しています。背景の話し声が最も破壊的で、文字起こしより先に言語判定が崩れると分かったため、言語切替に連続2回の一致を要求するようにしました(ノイズ40条件中15条件で判定が改善、クリーン音声への影響はゼロ)。ノイズ除去モデルを前段に挟む案は、60条件中49条件でむしろ悪化したため、測定データを公開した上で不採用としています。歌声は日本語アカペラなら朗読の約1.6倍の誤り率で字幕として読める水準、英語・韓国語の歌は誤り率5割前後でした。評価セットの生成・採点スクリプトはすべて同梱しています。

🤖 Generated with Claude Code

hayamimi 早耳 v0.1.0

Choose a tag to compare

@oboroge0 oboroge0 released this 25 Aug 14:57

hayamimi 早耳 v0.1.0 — first public release

Real-time, multilingual speech-to-text on CPU only. No GPU, no cloud, under 2GB RAM.

🎬 Demo video attached below (hayamimi_demo.mp4) — real engine output on real CC-BY audio, replayed frame-accurately.

Why it exists

Running Whisper-class models in real time normally requires a GPU or a cloud API. hayamimi routes each utterance to a small specialist model per language instead, which is how it stays real-time on an ordinary 6-core CPU.

What you get

  • Japanese accuracy that beats much larger models: 5.8% character error rate on real broadcast audio — less than half of whisper-large-v3-turbo's 13.8% on the same clips — while running at 10-50x realtime.
  • Fast feedback: a finalized line lands about 100ms after you stop talking, with draft subtitles streaming while you speak.
  • 5 specialist language routes (Japanese / Chinese / Korean+Cantonese / English+24 European languages) plus a fallback model covering roughly 1600 languages.
  • Practical extras: two-pass refinement for a clean transcript, speaker labels, ja→en/zh/ko translation, hotwords and a user dictionary, Japanese punctuation restoration, Korean word spacing.
  • Streaming-ready output: --serve starts a browser dashboard and a transparent OBS overlay.

Every accuracy number above comes from evaluation on real speech (broadcast, interviews, lectures) — not synthetic audio. The measurement scripts ship in the repository, so the results are reproducible.

Install

python -m venv .venv && .venv/Scripts/pip install -r requirements.txt
.venv/Scripts/python scripts/download_models.py   # ~3.1GB (or --minimal, ~1.1GB ja/en)
.venv/Scripts/python scripts/realtime_transcribe.py --serve

License notes

The code is MIT. Model weights are downloaded from their original publishers and carry their own licenses (THIRD_PARTY_NOTICES.md has the full table). The optional --translate en model (FuguMT) is CC BY-SA 4.0. Demo-video audio comes from FLEURS and LibriSpeech (both CC-BY 4.0); attribution is in demo/CREDITS.md.

日本語での紹介

hayamimi(早耳)は、GPUもクラウドも使わずにCPUだけでリアルタイム多言語文字起こしをするツールです。言語ごとに小さな専用モデルへ振り分ける構成で、実放送の日本語音声で文字誤り率5.8%(同じ音声でwhisper-large-v3-turboは13.8%)を、6コアCPUの実時間の10〜50倍の速度で達成しています。話し終えてから約100msで確定字幕が出て、--serve を付ければOBS用の透過オーバーレイとダッシュボードがそのまま使えます。精度の数値はすべて実音声での測定で、測定スクリプトもリポジトリに同梱しています。

🤖 Generated with Claude Code