0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

日本語STT「Haas-ja-v1.0」を Windows + RTX 3050 で動かして Soniox と比べた(6時間の音声を2分45秒、でも固有名詞に弱い)

0
Posted at

はじめに

この記事はClaudeによって書かれています。

2026-10-30 に、Third Intelligence から日本語の音声認識モデル Haas-ja-v1.0(Apache 2.0、約6億パラメータ)が公開されました。ローカルで動かせれば、クラウド STT の従量課金も通信も要りません。

普段はクラウドの Soniox(stt-rt-v5)を使っているので、手元の Windows PC で Haas を動かし、同じ音声で精度と速さを比べました。特に「どんな言葉で間違えるか」を種類別に整理しています。

先に結論

  • Windows でもすぐ動く。 C++ 実装の speech.cpp に Windows / Vulkan 版のビルド済みバイナリがあり、RTX 3050(VRAM 6GB)でそのまま動いた。WSL2 も CUDA も不要。
  • 速い。 講演の録音 6時間10分を 2分45秒 で文字起こし(音声の約134倍速)。短い発話は 1本 0.03〜0.04 秒。
  • 精度は Soniox が上。 日常会話(本人の声)の文字誤り率は Haas 8.6%、Soniox 5.5%。ただし読み(カナ)で比べると 3.8% と 3.2% でほぼ並ぶ。Haas は音はほぼ聞き取れていて、どの単語として書くかで負けている。
  • カタカナの固有名詞・エンジニアの専門用語・口語の活用は Haas が特に弱い。同音異義語(休載→救済、報連相→ほうれん草)は両方とも同じように間違える。
  • 会場の録音では、Haas の手前で発話区間を切り出す Silero VAD が声を拾わず、数分単位で文字が出ないことがあった。音の前処理で直るが、一律にかけると別の録音が悪くなる。
Haas-ja-v1.0(speech.cpp、ローカル) Soniox stt-rt-v5(クラウド)
日常会話の文字誤り率 8.6% 5.5%
専門用語を含む発話 15.0% 5.0%
短い発話 1本の処理 0.03〜0.04 秒(GPU) 約 2 秒(通信込み)
話し終わり → 確定 約 0.6 秒 1.3〜1.6 秒(以前の計測)
料金 なし $0.12 / 時間(リアルタイム)

話者は私 1人、1文につき 1テイクの小さな計測です。Haas は語彙(カスタム辞書)を渡さずに評価しています。傾向をつかむのが目的で、数ポイントの差で優劣は断定できません。

環境

項目 値
計測日 2026-10-31
OS Windows 11
CPU / GPU Intel Core i5-14400F / NVIDIA GeForce RTX 3050(VRAM 6GB)
Haas Haas-ja-v1.0、speech.cpp 0.9.3(Windows x64 Vulkan 版)、Q8_0
VAD Silero VAD(speech.cpp の silero-vad)
Soniox stt-rt-v5(WebSocket)、language_hints: ["ja"]、語彙なし
評価 jacer v1.2(表記揺れを正規化してから文字誤り率を計算)

Haas を Windows で動かす

公式の推論コード(Haas-ASR)は Linux + NVIDIA GPU + NeMo 専用です。Windows では speech.cpp を使います。0.9.3 で Haas に対応していて、GGUF に変換したモデルを CPU / Vulkan / Metal で動かせます。

インストーラー(install.ps1)もありますが、PATH を書き換えるので、今回はリリースの zip を作業フォルダに展開しました。

gh release download v0.9.3 -R nyosegawa/speech.cpp -p "speech-0.9.3-windows-x64-vulkan.zip" -p SHA256SUMS
Get-FileHash speech-0.9.3-windows-x64-vulkan.zip   # SHA256SUMS と一致するか確認
Expand-Archive speech-0.9.3-windows-x64-vulkan.zip -DestinationPath speech
.\speech\speech.exe devices
# Vulkan0    gpu   NVIDIA GeForce RTX 3050, 6.3 GB, 5.5 GB free
# CPU        cpu   Intel(R) Core(TM) i5-14400F, 34.1 GB, 16.4 GB free

.\speech\speech.exe asr haas-ja-v1.0 -v sample.wav

初回はモデル(Q8_0、0.66 GB)を Hugging Face から取ってきます。GPU では 1回目だけシェーダーの準備に約 9 秒かかり、2回目からは 5 秒の音声が 0.08 秒で終わりました。CPU でも 0.42 秒です。

量子化は Q4_K(0.35 GB)も試しましたが、速さは同じで誤り率が少し上がった(15.0% → 17.4%)ので、Q8_0 で十分です。

比べ方

音声

セット 中身 規模
日常会話 野球・アニメ・ゲーム・仕事の愚痴など 24 文。作品名やチーム名、フィラー、言い直しを含む。本人が録音 24本 / 128秒
専門用語 「FizzBuzz を作って」「この関数を async にリファクタして」など、エンジニア同士の言い回し 6 文 × 3回 × 2セット 36本 / 161秒
講演録音 技術カンファレンスの講演 10本を客席から IC レコーダーで録ったもの 10本 / 6時間10分

最初は日常会話の音声を TTS(ローカルの Qwen3-TTS など)で作りましたが、TTS 自体が固有名詞を読み間違え(「呪術廻戦」など)、2つの STT が同じ誤りをするので評価になりませんでした。固有名詞の評価には人の声が必要です。

文字誤り率

jacer は Haas の作者による日本語 CER ライブラリで、数字・単位・句読点・送り仮名などの表記揺れを正規化してから比べます。「10行目」と「十行目」は誤りになりません。

import jacer
jacer.cer(["10 行目から 20 行目の処理を説明して"],
          ["十両目から二十両目の処理を説明して"], jacer.METRICS)
# {'cer_strict': 0.111, 'cer_flex': 0.111, 'cer_kana': 0.143}  ← 「両」の 2 文字だけが誤り

cer_strict が主指標、cer_kana は読みで比べる指標です。

Haas の呼び出し

1 プロセスで全ファイルを渡すと、モデルの読み込みが 1 回で済みます。-v を付けるとファイルごとの処理時間が stderr に出るので、それを拾います。

cmd = [SPEECH, "asr", "haas-ja-v1.0", "--device", "gpu", "--format", "json", "-v", *paths]
proc = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
texts = [json.loads(line)["text"] for line in proc.stdout.splitlines() if line.strip()]
# stderr の "PATH.wav: 4.86 s of audio in 0.083 s, RTFx 58.9" を拾う(最後の合計行は除く)
seconds = [float(m) for m in re.findall(r"\.wav: [\d.]+ s of audio in ([\d.]+) s", proc.stderr, re.I)]

Soniox の呼び出し

普段使っているリアルタイム用の WebSocket に、実時間を待たずに音声を送り切って、finished までの時間を測りました。

async with websockets.connect("wss://stt-rt.soniox.com/transcribe-websocket") as ws:
    await ws.send(json.dumps({"api_key": key, "model": "stt-rt-v5", "audio_format": "pcm_s16le",
                              "sample_rate": 16000, "num_channels": 1, "language_hints": ["ja"]}))
    for i in range(0, len(pcm), 3200):
        await ws.send(pcm[i:i + 3200])
    await ws.send("")  # 音声おわり
    async for message in ws:
        ...  # is_final のトークンをつなぐ。finished で抜ける

結果をキャッシュして再課金を避けていたところ、音声を録り直した後も古い結果を使ってしまい、Soniox が実際より悪く出ました。キャッシュにはファイルの大きさと更新時刻を一緒に保存し、変わったら取り直すようにしています。

結果

セット Haas Soniox
日常会話(CER strict) 8.6% 5.5%
日常会話(CER kana) 3.8% 3.2%
専門用語(CER strict) 15.0% 5.0%
講演録音(Haas の文字量 / Soniox) 79% —

講演録音には正解がないので、Soniox の文字起こしを基準に文字量と食い違いを比べました(Soniox も誤るので、Haas の精度そのものではありません)。

弱かった言葉

1. カタカナの固有名詞・製品名(Haas が弱い)

正解 Haas Soniox
原神 原身 原神
ヒューマノイド ヒューマンエイト ヒューマノイド
葬送のフリーレン 葬送のフリーレン 葬送のフリーレイ

講演の基調講演から固有名詞・用語 34 語を拾って確かめると、Soniox は 31 語、Haas は 20 語を正しく書きました。講演企業の製品名(カタカナ 3〜5 文字の造語)は、Haas が 3 語とも 1 文字ずつ外しました。

2. エンジニア同士の専門用語(Haas が弱い)

差がいちばん大きかった種類です。Soniox はカタカナで正しく書き、Haas は日常語に寄せてしまいます。

正解 Haas Soniox
FizzBuzz を作って ピザバズをつくって / ビザバスをつくって フィズバズを作って
この関数を async にリファクタして この関数を、悪に磨く足して この関数を async にリファクタして
AMR AR AMR
点群データ / 暗黙知を形式知に 研究データ / 暗黙口を形式に 正解

3. 人名・社名(両方)

両方とも間違えますが、間違える語が違います。Haas は登壇者の名前を別の名前(〜四郎 → 〜太郎)にし、Soniox は建設会社の社名を同じ読みの別の熟語にしました。英字 4 文字の社名は、両方とも英字 3 文字の一般的な略語にしました。

4. 同音異義語で一般的な語に流れる(両方)

音は聞き取れていて、より一般的な同じ読みの語を選んでしまいます。2つの STT がまったく同じ誤りをすることが多い種類です。

正解 Haas Soniox
ワンピースの休載 ワンピースの救済 ワンピースの救済
報連相してくれなくて ほうれん草してくれなくて ほうれん草をしてくれなくて
最新話 最終話 最終話
推しの子の二期 推しのこの日記 推しの子の2期
10行目から20行目 十両目から二十両目(6本中5本) 10両目(6本中1本)

5. 口語の活用・くだけた言い方(Haas が弱い)

日常会話で Haas だけが間違えた 6 本のうち 4 本がこれでした。

正解 Haas Soniox
ずっと負けてて ずっと巻きてて ずっと負けてて
五回で降りちゃってさ 五回で落ちちゃってさ 5回で降りちゃってさ
展開違くてモヤモヤ 展開近くて無やもや 展開しかくて
返信がそっけなくてさ 返信がそっちなくてさ 返信がそう、来なくてさ

6. 言い直し・フィラー(両方)

正解 Haas Soniox
明日、渋谷で、あ、違う、新宿で友達と会う 明日渋谷で、アーチが新宿で止まりと会う 明日渋谷で、あー違う、新宿で泊まっちゃう

Haas を発話区間ごとに処理すると、「えーっと…」や「あ、やっぱり」の間で発話が 2 つに分かれました(3 本中 3 本)。

会場の録音で声ごと抜ける問題

講演 10 本のうち 1 本(パネル討論)だけ、Haas の文字量が Soniox の 29% しかありませんでした。数分単位で何も出ない区間があり、20 秒以上の空白が合計 26 分ありました。

原因は Haas ではなく VAD

長い録音は、そのまま Haas に渡すと文が丸ごと抜けたり、計算量が長さの 2 乗で増えたりします(speech.cpp のドキュメントより)。そこで、発話区間を切り出す Silero VAD を前に挟んでいました。

音声 → [Silero VAD] 話している区間だけ切り出す → [Haas] その区間を文字にする

VAD が「話していない」と判定した部分は Haas に一切渡りません。抜けた区間の 2 分間を調べると、実際にパネルの自己紹介が続いているのに、VAD は 1 区間も見つけていませんでした。

条件(抜けた区間の 2 分間) VAD が見つけた区間
そのまま(閾値 0.5) 0
閾値 0.3 / 0.15 2 / 6
低音カット、音量の正規化 0
低音カット + コンプレッサー + 音量の正規化 31

音量・音割れ・低音の比率は、うまくいった録音とほとんど変わりませんでした。全体の音量を上げても効かず、強弱の差を縮めるコンプレッサーをかけたときだけ VAD が反応しました。なぜこの録音だけそうなったのかは、数値からは特定できていません。

前処理を入れる

ffmpeg で 16 kHz に変換するところにフィルタを足し、そのまま speech.cpp の標準入力に流しています。

dec = subprocess.Popen(["ffmpeg", "-loglevel", "error", "-i", path,
                        "-af", "highpass=f=200,acompressor,loudnorm",
                        "-ac", "1", "-ar", "16000", "-f", "s16le", "-"], stdout=subprocess.PIPE)
asr = subprocess.Popen([SPEECH, "asr", "haas-ja-v1.0", "--vad", "silero-vad", "--format", "json", "-"],
                       stdin=dec.stdout, stdout=subprocess.PIPE)
# 発話ごとに OpenAI Realtime 形式のイベントが 1 行ずつ出る
# conversation.item.input_audio_transcription.completed の transcript が確定文
講演録音 前処理なし: 文字量 / 食い違い 前処理あり: 文字量 / 食い違い
パネル討論 29% / 74.6% 79% / 30.7%
改善した 1 本 77% / 34.4% 90% / 23.7%
悪化した 2 本 95% / 14.5%、78% / 38.0% 87% / 22.1%、65% / 47.5%
10 本合計 79% / 33.5%(2分45秒) 84% / 29.1%(9分42秒)

全体では良くなりますが、うまくいっていた録音が悪くなり、処理時間も 3.5 倍(大半は loudnorm)になります。前処理なしで文字が少なかった区間だけやり直す使い方が向いていそうです。

対策のまとめ

対策 効く種類 状態
語彙登録 固有名詞、専門用語、人名、同音異義語の一部 Haas は speech.cpp main に --words "FizzBuzz=フィズバズ" 形式で入ったが未リリース(0.9.4 予定、モデルの再取得が必要)。Soniox は context.terms で FizzBuzz が全問正解になった実績あり
認識後の辞書置換 固有名詞、専門用語 決まった用語をカナにして音の近いものを置き換える(ピザバズ → FizzBuzz)。誤置換に備えて元の文も残す
LLM による文脈補正 同音異義語、口語 「ワンピースの救済」→「休載」のように話題から直す。コストと遅延が増える
短い間隔の発話をつなぐ 言い直し・フィラー VAD の切れ目が短いときは前の発話とつなぐ
音の前処理 声ごと抜ける 上のとおり。一律ではなく、抜けた区間だけに使う

この計測の限界

  • 話者は 1 人、1 文につき 1 テイク。言い方を変えると結果が入れ替わる行もありえる。
  • Haas は語彙なし。語彙機能のリリース後に測り直す予定。
  • 講演録音の比較は Soniox を基準にしていて、Soniox 自身の誤りを含む。
  • 講演の録音・文字起こしは公開していない。会社名・人名・製品名も伏せた。

まとめ

  • Haas-ja-v1.0 は speech.cpp で Windows + 6GB の GPU でもすぐ動き、6 時間の音声を 3 分弱で処理できる。通信も課金もなし。
  • 精度は Soniox が上。差がつくのは主に「聞き取った音をどの単語として書くか」で、カタカナの固有名詞、エンジニアの専門用語、口語の活用に弱い。
  • 長い録音では、Haas の前に置く VAD が声を拾えるかどうかが結果を大きく左右する。STT を比べるときは、区切り方まで含めて比べる必要がある。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?