世界を動かす技術を、日本語で。

Mistralの「Voxtral Mini 4B」のリアルタイムRust実装がブラウザで動作します

概要

Voxtral Mini 4B Realtime は、Rust製の ストリーミング音声認識モデル であり、 ネイティブ実行とブラウザ実行 の両方に対応。 Burn MLフレームワーク を利用し、 MistralのVoxtral Mini 4B Realtime を完全Rustで実装。 Q4 GGUF量子化モデル(2.5GB) は、 WASM+WebGPU 経由でブラウザタブ内で全てクライアントサイド実行可能。 CLIやブラウザデモ で簡単に試用可能。 モデルの構造や制約、ビルド方法、テスト方法 も詳細に説明。

Voxtral Mini 4B Realtime 概要

  • Rust製ストリーミング音声認識モデル、MistralのVoxtral Mini 4B Realtime実装
  • Burn MLフレームワーク による構築
  • Q4 GGUF量子化モデル(2.5GB) がWASM+WebGPUでブラウザ実行対応
  • ネイティブ(f32)とQ4 GGUF の2つの推論パス
    • f32(約9GB)はSafeTensors形式、Q4 GGUFは2.5GB
  • CLI・ブラウザデモ、HuggingFace Spacesでのホストデモ提供

クイックスタート(CLI)

  • モデル重み(約9GB) のダウンロード
    • uv run --with huggingface_hub \ hf download mistralai/Voxtral-Mini-4B-Realtime-2602 --local-dir models/voxtral
  • 音声ファイルの書き起こし(f32 SafeTensors)
    • cargo run --release --features "wgpu,cli,hub" --bin voxtral-transcribe -- \ --audio audio.wav --model models/voxtral
  • Q4量子化パス(2.5GB) の利用
    • cargo run --release --features "wgpu,cli,hub" --bin voxtral-transcribe -- \ --audio audio.wav --gguf models/voxtral-q4.gguf --tokenizer models/voxtral/tekken.json

ブラウザデモ

  • WASMパッケージのビルド
    • wasm-pack build --target web --no-default-features --features wasm
  • 自己署名証明書の生成(WebGPUはセキュアコンテキスト必須)
    • openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout /tmp/voxtral-key.pem -out /tmp/voxtral-cert.pem -days 7 -nodes -subj "/CN=localhost"
  • 開発サーバー起動
    • bun serve.mjs
  • https://localhost:8443 を開き、証明書を承認し、「Load from Server」でモデルシャードをダウンロード
  • マイク録音またはWAVファイルアップロード による書き起こし
  • HuggingFace Spaces でのホストデモも選択可能

アーキテクチャ

  • 入力 :16kHz モノラル音声
  • 前処理 :Melスペクトログラム [B,128,T]
  • エンコーダ :32層、1280次元、スライディングウィンドウ750
  • 畳み込み4倍ダウンサンプル
  • リシェイプ :[B, T/16, 5120]
  • アダプタ :[B, T/16, 3072]
  • デコーダ :自己回帰型、26層、3072次元、GQA 32Q/8KV
  • 出力 :トークンID → テキスト

推論パス

  • F32(ネイティブ)
    • SafeTensors(約9GB)
  • Q4 GGUF(ネイティブ+ブラウザ)
    • GGUF Q4_0(約2.5GB)

線形演算・埋め込み

  • 線形演算 :Burnテンソルmatmul、カスタムWGSLシェーダ(融合dequant+matmul)
  • 埋め込み :f32テンソル(1.5GiB)、Q4はGPU上216MB+CPU側ルックアップ

ブラウザ対応

  • F32 :非対応
  • Q4 GGUF :WASM+WebGPUで対応

Q4パディング問題とワークアラウンド

  • mistral-common は音声先頭に32トークン分の無音パディング(12.5Hz)を付与
  • mel/conv/reshape後、38デコーダープリフィックス中16のみが無音、残り22は実音声
  • Q4_0量子化モデル はプリフィックスに音声があると全パッドトークン出力の不具合
  • パディングを76トークンに増加 し、38トークン全て無音でカバー
  • 詳細 はsrc/audio/pad.rs参照

WASM制約と解決策

  • 2GBアロケーション制限 :ShardedCursorで複数Vec<u8>バッファを跨いで読み込み
  • 4GBアドレス空間制限 :2段階ロード(重みパース→リーダ破棄→最終化)
  • 1.5GiB埋め込みテーブル :Q4埋め込みはGPU+CPU側ルックアップ
  • 同期GPUリードバック不可 :全テンソル読み出しはinto_data_async().await利用
  • 256ワークグループ制限 :cubecl-wgpuをパッチ適用しカーネルワークグループ数を上限設定

ビルド方法

  • ネイティブ(wgpu+native-tokenizerがデフォルト)
    • cargo build --release
  • 全機能有効
    • cargo build --release --features "wgpu,cli,hub"
  • WASM
    • wasm-pack build --target web --no-default-features --features wasm

フィーチャーフラグ一覧

  • wgpu :Burn/CubeCL経由のGPUバックエンド(WebGPU, Vulkan, Metal)
  • native-tokenizer :Tekkenトークナイザ(C依存、WASM非対応)
  • wasm :ブラウザ用(wasm-bindgen、WebGPUデバイス初期化、JSバインディング)
  • cli :clap+indicatifによるCLIバイナリ
  • hub :HuggingFace Hubからのモデルダウンロード

テスト・Lint

  • ユニット+統合テスト(GPU必須)
    • cargo test --features "wgpu,cli,hub"
  • Lint
    • cargo clippy --features "wgpu,cli,hub" -- -D warnings
    • cargo clippy --no-default-features --features wasm --target wasm32-unknown-unknown -- -D warnings
  • E2Eブラウザテスト(Playwright+モデルシャード必須)
    • bunx playwright test tests/e2e_browser.spec.ts
  • GPU依存テスト はCIではスキップ、ローカルGPU環境で実行

モデル準備(Q4 GGUFシャーディング)

  • GGUFファイルを512MB以下に分割 (ブラウザのArrayBuffer制限回避)
    • split -b 512m models/voxtral-q4.gguf models/voxtral-q4-shards/shard-
  • 開発サーバーとE2Eテスト は自動でシャードを検出

ベンチマーク

  • 精度(WER)・推論速度ベンチマーク は近日公開予定

プロジェクト構成

  • src/audio/ :Melスペクトログラム、チャンク化、リサンプリング、パディング
  • src/models/ :f32モデル(エンコーダ、デコーダ、アダプタ、アテンション、RoPE、KVキャッシュ)
  • src/gguf/ :Q4 GGUF(リーダ、ローダ、モデル、テンソル、WGSLシェーダ、テスト)
  • src/web/ :WASMバインディング(VoxtralQ4、initWgpuDevice、非同期デコードループ)
  • src/tokenizer/ :Tekkenトークナイザラッパー(ネイティブのみ)
  • bin/transcribe :CLIバイナリ
  • web/ :ブラウザデモ(index.html、worker.js、voxtral-client.js)
  • tests/ :統合テスト+Playwright E2Eスペック
  • scripts/ :開発用スクリプト(リファレンス実装、重み検証、E2Eヘルパー)
  • patches/ :WebGPU用cubecl-wgpuワークグループサイズ修正パッチ

ライセンス

  • Apache-2.0

Hackerたちの意見

うーん、俺の環境では壊れてるみたい(Firefox、Asahi Linux、M1 Pro)。マイクに「こんにちは」って言ったら、1分くらい処理してから、こんなのが返ってきた:panorama panorama panorama panorama panorama panorama panorama panorama molest rist moundothe exh Invothe molest Yan artist Yan Yan Yan Yan Yanothe Yan Yan Yan Yan Yan Yan Yan

これはRustで書かれたプログラムへの批判なの?異端者を殺せ!

すごい作品だね!handy.computerでも動くといいな。それと、ストリーミングのサポート予定はあるの?

Handyを試してみたけど、今まで試した中で一番軽くて使いやすいUIだね!意図してなかったかもしれないけど、オススメしてくれてありがとう!それにしても、今は君の最初の意見に賛成で、Voxtralのサポートが本当に欲しいな…

これをtranscribe-rsに移植することを考えてる。最初のバージョンはおそらくストリーミング実装にはならないだろうね。

これを微調整したいけど、残念ながらhuggingfaceの実装はまだ出てないみたい。

これ、リアルタイムにはほど遠いね。M4 Maxで。

興味がある人は、@antirezがVoxtral Mini 4BのC実装をここに公開してるよ:https://github.com/antirez/voxtral.c 俺のフォークもここにある:https://github.com/HorizonXP/voxtral.c で、CUDA実装とか他の便利な機能を作ってるところ。今のところかなりうまく動いてるけど、Mistral AIのAPIエンドポイントの速度にはまだ追いついてない。

もう一つのMistral実装もあるよ:https://github.com/EricLBuehler/mistral.rs 違いはよくわからないけど、全体的に評価が良いみたい。

デモを試してみたけど、マイクをクリックして音声を録音してから、「停止して転写」をクリックしないと結果が見れないみたい。これをリアルタイムで、ユーザーが声に出してから1〜2秒以内に転写を表示するようにできるかな?https://huggingface.co/spaces/mistralai/Voxtral-Mini-Realtim... のHugging Faceのサーバーサイドデモはそれができてるけど、かなり大きい(約8.5GB)のサーバーサイドモデルをGPUで動かしてるみたい。

リアルタイムには速くないけど、もっと高度なUIとリングバッファを使えば、君が言ってるようにできるかもね。(例えば、FlutterでWhisperを使ってるし、Dart経由でllama.cppのGGUF推論もやってる)M4 Maxでは、これがリアルタイムにはほど遠い。Whisperは2022年以降のデバイスではほぼリアルタイムだよ。追加の推論コストは、消費者向けハードウェアでのWERの減少には見合わないし、少なくとも実装する時間を考えると価値がないと思う。

すごいね、ここが重要なところだよね:オンプレミスで動くオープンモデル。ユーザーやビジネスに好まれてる。Mistralがそれを解決したのは嬉しいな。

これらのモデルについては何も知らないけど、NvidiaのParakeetを試してみたらすごく良かった。フルモデルで9GBのモデルの場合、リアルタイムで本当に動かすためにはGPUメモリに常にロードしておく必要があるの?それとも、毎回重みをロードするのにどれくらいの遅延があるの?

個人的にはollamaサーバーを運営してる。モデルのロードは結構早いよ。トークン毎秒と最初のトークンまでの時間には違いがある。新しいモデルをロードする時や、特に大きなコンテキストを入れ替える時に遅延が出ることがある。大抵の場合、モデルはすでにロードされてるし、小さなコンテキストから始めて時間をかけて構築していくから、トークン毎秒が一番影響する。特に派手なことはあまりやってなくて、少しエージェント的なことをやるくらい。主にqwen-coder 30a3bかqwen2.5のコードインストラクト/ベース7bを使ってる。複数のエージェントを使うような複雑なエージェントの処理は、大きなコンテキストを入れ替えると本当に遅くなることがある。ik_llamaにはプロンプトキャッシングがあって、エージェントのコンテキストを入れ替える時にスピードアップに役立つけど、限界がある。要するに、毎回重みをロードするのはあまり問題じゃないけど、モデルやコンテキストを頻繁に切り替える必要があると、最近のエージェントの処理はそうなりがち。

(音声が検出されません)それとも…何も話さずにランダムなドイツ語の文を生成する。