13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

日本語性能世界一の音声認識モデルを作った

13
Last updated at Posted at 2026-04-16

どうも、ラッパーです。

バイブコーディングの流行もあって、音声入力の需要は高まっていると思います。個人的にもChatGPTの音声入力を使ったり、Claude Codeの/voiceも使っています。で、アプリで単体でも使えるようにローカルで動くモデルも一通り試してみたんですが、日本語の品質がどれも微妙なんですよね。Aqua Voiceもまあまあ良いんですが、有料のサブスクだし、日本語用のAPIを提供していないので専用アプリでしか使えなかったり、いろいろ不便なんですよね。

ないなら作るか、ということでQwen3-ASR-1.7BというモデルをベースにLoRAでファインチューニングしました。できたのがlilfuguです。

何が変わったのか

同じ音声を認識させた結果を並べます。

既存のローカルモデル:

えっとネクストジェイスのプロジェクトでユースエフェクトが三回ぐらい無限ループしててドッカーのコンテナも二回落ちたでバーセレーのデプロイも失敗しててプリズマのマイグレーションでエラーが出てるあとテストカバレッジが四十五パーセントぐらいしかないから八十パーセントまで上げたいのとテールウィンドCSSのコンポーネントも修正したい。

lilfugu:

えっと、Next.jsのプロジェクトでuseEffectが3回ぐらい無限ループしてて、dockerのコンテナも2回落ちた。で、Vercelのデプロイも失敗してて、Prismaのマイグレーションでエラーが出てる。あと、テストカバレッジが45%ぐらいしかないから80%まで上げたいのと、Tailwind CSSのコンポーネントも修正したい。

改善されているポイント:

  • 技術用語: ネクストジェイス → Next.js、ユースエフェクト → useEffect、バーセレー → Vercel
  • 数字: 三回 → 3回、四十五パーセント → 45%、八十パーセント → 80%
  • 句読点: 句点・読点が自然に入って文が区切られている
  • 可読性: そのままSlackに貼ったりAIに投げたりできるテキストになっている

技術用語だけの話じゃないんですよね。句読点・数字・可読性を含めた日本語性能全体が改善されています。開発者に限らず、日本語で音声入力をしている人なら恩恵があると思います。

試してみる

ブラウザで試せるデモを用意しているので、まず自分の声で試してみてください。

聞き取りやすいようにゆっくり丁寧に喋る必要はないです。普段通りの速度で、思ったことをバーっと喋ってみてもらった方がlilfuguの性能が分かると思います。

世界一です

まともに日本語の音声認識品質を測れるベンチマークがそもそも存在しないので、正確な比較は難しいんですよね。既存のベンチマークは正規化の問題で句読点や表記の品質差が数値に反映されない構造になっていて、上の出力ぐらい差があってもほぼ同スコアになってしまう。

なのでまともに日本語を評価できるベンチマーク(ADLIB)も別途作りました。その辺の話はこちらの記事に書いています。

既存のローカルモデルは一通り試した上で、これが一番良いので世界一ということにしておきます。(もっといいものがあれば挑戦者募集中です。)

使い方

TypeWhisper + mlx-audio(おすすめ)

TypeWhisperはローカルで動く音声入力アプリです。ホットキーで起動して喋るだけでどのアプリにもテキストが入力されます。ここにlilfuguを繋げます。

1. mlx-audio serverをインストール

brew tap guoqiao/tap
brew install guoqiao/tap/mlx-audio-server
brew services start mlx-audio-server

これでサーバーがバックグラウンドで常駐します。ログイン時に自動起動するので、毎回コマンドを打つ必要はないです。

[2026-04 時点] 既知のクラッシュ問題: MLX の Metal バックエンドにスレッド安全性の問題があり(ml-explore/mlx#2067ml-explore/mlx#2133)、同時/近接リクエスト時にサーバーが SIGSEGV で落ちることがあります。mlx-audio 側の修正 PR(Blaizzy/mlx-audio#594)はマージ前です。使っていてクラッシュが気になったら下の暫定ワークアラウンドを適用してください。

暫定ワークアラウンド(/v1/audio/transcriptions 系を保護)

brew services start mlx-audio-server 実行済みの前提です。plist の ProgramArguments を丸ごと置き換えるので、独自の引数を追加している場合は事前に中身を確認してください。

mkdir -p ~/.local/bin
cat > ~/.local/bin/mlx-audio-server-safe.py <<'PY'
"""Workaround for MLX Metal race. See ml-explore/mlx#2067."""
from __future__ import annotations
import functools, sys, threading
from mlx_audio.server import ModelProvider, main

_L = threading.RLock()


def _is_iter(o):
    return (hasattr(o, "__iter__") and hasattr(o, "__next__")
            and not isinstance(o, (str, bytes, list, tuple, dict)))


def _wrap(fn):
    @functools.wraps(fn)
    def locked(*a, **k):
        with _L:
            r = fn(*a, **k)
            if not _is_iter(r):
                return r
        def _it():
            with _L:
                yield from r
        return _it()
    return locked


_orig = ModelProvider.load_model


@functools.wraps(_orig)
def _load(self, name):
    with _L:
        m = _orig(self, name)
        if not getattr(m, "_locked", False):
            m.generate = _wrap(m.generate)
            m._locked = True
    return m


ModelProvider.load_model = _load

if __name__ == "__main__":
    sys.exit(main())
PY

PLIST=~/Library/LaunchAgents/me.guoqiao.mlx-audio-server.plist
cp "$PLIST" "$PLIST.bak"

/usr/bin/python3 <<'PY'
import plistlib, pathlib
p = pathlib.Path.home() / "Library/LaunchAgents/me.guoqiao.mlx-audio-server.plist"
d = plistlib.loads(p.read_bytes())
d["ProgramArguments"] = [
    "/opt/homebrew/opt/mlx-audio-server/libexec/bin/python",
    str(pathlib.Path.home() / ".local/bin/mlx-audio-server-safe.py"),
    "--host", "0.0.0.0", "--port", "8899",
    "--log-dir", "/opt/homebrew/var/log/mlx-audio-server",
]
p.write_bytes(plistlib.dumps(d))
PY

launchctl bootout gui/$(id -u) "$PLIST" 2>/dev/null
launchctl bootstrap gui/$(id -u) "$PLIST"

戻すとき

PLIST=~/Library/LaunchAgents/me.guoqiao.mlx-audio-server.plist
mv "$PLIST.bak" "$PLIST"
rm -f ~/.local/bin/mlx-audio-server-safe.py
launchctl bootout gui/$(id -u) "$PLIST" 2>/dev/null
launchctl bootstrap gui/$(id -u) "$PLIST"

2. TypeWhisperの設定

TypeWhisperの Settings → Integrations から「OpenAI Compatible」をインストールします。

typewhisper-integrations.png

設定アイコンから以下を設定します。

  • Server URL: http://localhost:8899
  • Transcription Model: holotherapper/lilfugu-8bit と入力

typewhisper-settings.png

初回はモデル名を手入力します。初回の音声入力時にモデルが自動でダウンロードされるので、最初だけ少し時間がかかります。

設定後、エンジンとしてOpenAI Compatibleを選択すれば、ホットキーを押して喋るだけでlilfuguで認識されたテキストがカーソル位置に入力されます。

コードから使う(Apple Silicon)

pip install -U mlx-audio
python3 -c "
from mlx_audio.stt import load
model = load('holotherapper/lilfugu-8bit')
result = model.generate('audio.wav', language='Japanese')
print(result.text)
"

8bit版(2.8GB)で、品質の劣化はすこーーーしはあるかもですが、ほぼ確認していません。初回実行時にモデルが自動でダウンロードされます。

バリアント

通常版(lilfugu):

リポジトリ サイズ 用途
lilfugu-8bit 2.8 GB Apple Silicon。おすすめ
lilfugu 4.1 GB MLX bfloat16
lilfugu-transformers 4.1 GB CUDA / Linux
lilfugu-transformers-8bit ~2.8 GB CUDA / Linux(8bit)
lilfugu-onnx ~5.9 GB ONNX。CPU (Linux / Win / macOS)
lilfugu-lora ~49 MB LoRAアダプター単体

攻め版(lilfugu-experimental):

用語変換がより積極的なバリアントです。技術的な会話がメインの人にはこっちが合うかもしれません。ただし汎用テキストでハルシネーションが起きやすいので、バランス重視なら通常版で。

リポジトリ サイズ 用途
experimental-8bit 2.8 GB Apple Silicon
experimental 4.1 GB MLX bfloat16
experimental-transformers 4.1 GB CUDA / Linux
experimental-transformers-8bit ~2.8 GB CUDA / Linux(8bit)
experimental-lora ~49 MB LoRAアダプター単体

CPUでも動きます(ONNX版)

MLX/CUDAなしでも動くようONNX版も用意しました。Linux / Windows の普通のPCでも動きます(推奨RAM 24GB以上)。

今後

現在さらに改良したバージョンも作っています。学習データの用語カバレッジを広げたり、ハルシネーション耐性を上げたり。アップデートしたらHuggingFaceのリポジトリに上げるので、気になる人はフォローしておいてもらえると。

おわりに

日本語の音声入力に不満がある人全般におすすめできると思います。ぜひ試してみてください。

技術的な詳細は別途記事を書く予定です。ベンチマークの話はこちらにまとめています。

13
6
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
13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?