第3回 個人開発でできるリアルタイム翻訳 - Ollama・LM Studio・自宅GPUの限界
![]()
4言語の双方向翻訳に対応: 英語・日本語・スペイン語・中国語の任意の組み合わせでリアルタイム双方向翻訳が可能です。ソースコードはGitHubで公開しています。
第1回では音声翻訳における「沈黙」の問題を取り上げ、Deepgram・FastAPI・WebSocketで基盤を構築しました。第2回ではLLMストリーミングの実装詳細として、JSONフィールド順序の最適化とデュアルプロンプト戦略をコード付きで解説しました。第3回となる今回は、ローカルGPUでの検証を実際のセットアップコマンドやバックエンドコードとともに紹介し、モバイルLLMの実現可能性を具体的なベンチマークで検証し、他の言語ペアへの対応ガイドを示し、最後にこのアプローチの率直な振り返りを述べます。
なぜローカルLLMを試したのか
クラウドAPIは高速で便利ですが、継続的な利用にはコストが発生します。
| プロバイダー | 5時間あたりのコスト |
|---|---|
| Groq / Llama 4 Maverick | 約515円(~$3.43) |
| OpenAI GPT-4o-mini | 約261円(~$1.74) |
| Gemini 2.5 Flash Lite | 約175円(~$1.17) |
日常的に使うツールとして考えると、この費用は無視できません。自宅のGPUで完結するリアルタイム翻訳が実現できれば、ランニングコストをゼロにできます。
そこで、手元にあるRTX 3060(VRAM 6GB)でGoogleのオープンソースLLM「Gemma 3 4B」を動かし、リアルタイム翻訳に使えるか検証しました。
Ollamaのセットアップ
最初に試したのはOllamaです。ローカルでオープンソースLLMを動かす方法として、おそらく最も手軽なツールです。
インストールとモデルの準備
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# Pull Gemma 3 4B model
ollama pull gemma3:4b
# Verify it's running
ollama list
インストール後、Ollamaはバックグラウンドサービスとして動作し、ポート11434でAPIを公開します。重要な利点の一つは、OpenAI互換のエンドポイントを提供していることです。クラウドAPIで使っている AsyncOpenAI クライアントがそのまま接続でき、設定の変更は1行で済みます。
バックエンドの設定
# Ollama provides an OpenAI-compatible API endpoint
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://192.168.0.183:11434/v1")
gemma_client = AsyncOpenAI(base_url=OLLAMA_BASE_URL, api_key="ollama")
# Critical: Ollama with 6GB VRAM cannot handle parallel requests
# A lock prevents concurrent calls
gemma_lock = asyncio.Lock()
ストリーミング呼び出しの実装
Ollamaへのストリーミング呼び出しは、クラウドAPIへの呼び出しと同じパターンで動作します。以下は _call_gemma メソッドの簡略版です。
async def _call_gemma(self, text: str, is_final: bool):
"""Stream response from Gemma 3 via Ollama"""
start_time = time.time()
system_prompt = INTENT_SYSTEM_PROMPT_QUALITY if is_final else INTENT_SYSTEM_PROMPT_SPEED
stream = await gemma_client.chat.completions.create(
model="gemma3:4b",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
temperature=0.3,
max_tokens=500,
stream=True,
)
full_response = ""
async for chunk in stream:
if chunk.choices[0].delta.content:
full_response += chunk.choices[0].delta.content
await self._process_streaming_chunk(
full_response, text, sent_intent_partial, sent_translation_partial
)
検証結果
- 単発リクエスト: 翻訳品質は実用的なレベルでした。Gemma 3 4Bは英語から日本語への翻訳を十分にこなし、ストリーミング出力も問題なく動作しました。
- 並列リクエスト(リアルタイム翻訳で必要とされるもの): GPUメモリが枯渇し、Windowsごと強制シャットダウンされました。
根本的な問題は、Ollamaがモデル全体をVRAMにロードすることにあります。6GBでは、並列の推論コンテキストを保持する余裕がありません。リアルタイム翻訳では、話者がまだ話している間のパーシャル結果と、発話が確定した後のファイナル翻訳が重複して生成されます。この並行処理パターンは、6GBカード上の単一モデルインVRAMアーキテクチャとは根本的に相容れないものでした。
LM Studio 0.4.0 — CLIによるヘッドレスサーバー
次にLM Studioを試しました。バージョン0.4.0でCLIツールと llmster デーモンを通じたヘッドレスサーバー機能が導入されています。
CLIセットアップの全手順
# Install LM Studio (download from lmstudio.ai)
# LM Studio 0.4.0 introduced the headless daemon 'llmster'
# Start the daemon (runs without GUI)
lms daemon up
# Start the inference server
lms server start --port 1234 --bind 0.0.0.0 --cors
# Load a model (if not already loaded via GUI)
lms load gemma-3-4b-it
# Verify the server is running
curl http://localhost:1234/v1/models
lms daemon up コマンドは、LM Studioの推論エンジンをGUIアプリケーションとは完全に独立したバックグラウンドプロセスとして起動します。ヘッドレスマシンでの推論実行や自動化パイプラインへの組み込みに適しています。Ollamaと同様にOpenAI互換APIを提供します。
バックエンドの設定
# LM Studio also provides an OpenAI-compatible API
LMSTUDIO_BASE_URL = os.getenv("LMSTUDIO_BASE_URL", "http://192.168.0.183:1234/v1")
LMSTUDIO_MODEL = os.getenv("LMSTUDIO_MODEL", "gemma-3-4b-it")
lmstudio_client = AsyncOpenAI(base_url=LMSTUDIO_BASE_URL, api_key="lm-studio")
# Lock mechanism: with limited VRAM, we serialize requests
lmstudio_lock = asyncio.Lock()
並列リクエスト制御のロック機構
ロック戦略の実装が興味深い部分です。リアルタイム翻訳は2種類のリクエストを生成します — パーシャル(話している最中)とファイナル(発話確定後)。ロックはこの2種類を異なる方法で扱う必要があります。
async def _call_lmstudio(self, text: str, is_final: bool):
"""LM Studio API with request serialization"""
# For partial results: skip if another request is already running
# For final results: wait for the lock (must be processed)
if not is_final and lmstudio_lock.locked():
print(f"[LMStudio] Skipped (lock busy, partial): {text[:30]}...")
return
async with lmstudio_lock:
stream = await lmstudio_client.chat.completions.create(
model=LMSTUDIO_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
temperature=0.3,
max_tokens=500,
stream=True,
)
# ... same streaming chunk processing as other providers
このロック戦略の考え方は以下の通りです。パーシャル翻訳は推測的なものであり、話者が話し続けると上書きされます。GPUが既にビジー状態であれば、パーシャルリクエストをキューに溜めてメモリ圧迫を引き起こすよりも、ドロップするほうが合理的です。一方、ファイナル翻訳は確定済みの発話を表しており、必ず処理される必要があるため、ロックの解放を待ちます。このアプローチにより、完了した発話の正確性を優先しつつ、進行中の発話については適切にデグレードさせています。
検証結果
| 項目 | 結果 |
|---|---|
| 翻訳 | △ 動作するが3〜4秒の遅延 |
| サマリー生成 | × ロック競合でほぼ実行されず |
| リアルタイム性 | × 500ms以内の応答は不可能 |
| コスト | ◎ 完全無料 |
翻訳自体は動作しますが、1件あたり3〜4秒かかるため、話すスピードに追いつくことができません。
重要な知見:プロバイダー間のAPI互換性
OllamaとLM Studioの両方をテストして得られた重要な知見は、どちらもOpenAI互換APIを提供しているということです。同じ AsyncOpenAI クライアントとストリーミングコードが、クラウドAPI(OpenAI、Groq、Gemini)でもローカルサーバーでも同一に動作し、変更が必要なのは base_url のみです。つまり、クラウドとローカルの推論の切り替えは設定1行の変更であり、コードの書き換えではありません。この相互運用性は、さまざまなプロバイダーを実験する際に大きな実用的メリットとなります。
結論
VRAM 6GBのGPUではリアルタイム翻訳の要件を満たすことはできませんでした。VRAM 12GB以上(RTX 4070クラス以上)であれば可能性はありますが、現時点ではGroqやGeminiのクラウドAPIが現実的な選択肢です。
モバイルローカルLLMの調査
スマートフォンのSoC上で直接LLMを動かせないかも調査しました。サーバー依存を完全に排除し、システムを完全にポータブルにできる可能性を探るためです。
調査したランタイム
以下は実機テストではなく、各プロジェクトの公開情報やコミュニティのベンチマーク報告をもとに調査した結果です。
- MLC Chat(mlc.ai): Metal(iOS)やVulkan(Android)経由でモバイルGPU上で量子化LLMを実行。Llama、Gemma、Phiモデルファミリーに対応。
- Google AI Edge Gallery: Googleが提供するオンデバイスAI推論の公式フレームワーク。TensorFlow Liteデリゲートによるハードウェアアクセラレーションで、Android上でGemma 2B/7Bをサポート。
- SmolChat: SmolLMやPhi-3-miniなどの小型モデルに特化した軽量チャットインターフェース。最小限のメモリフットプリントとデプロイの容易さに重点を置いている。
公開されているベンチマーク数値
Snapdragon 8 Gen 2 (flagship 2023):
- Gemma 2B quantized (Q4): ~12-16 tokens/sec
- Phi-3-mini (3.8B): ~8-10 tokens/sec
Apple A17 Pro:
- Gemma 2B quantized: ~15-20 tokens/sec
- Phi-3-mini: ~10-14 tokens/sec
現行モバイルハードウェアが不十分な理由
500ms以下のレイテンシでリアルタイム翻訳を実現するには、完全なJSONレスポンス(約100〜200トークン)を500ms以内に生成する必要があります。これには最低200〜400 tokens/secのスループットが必要です。現行のモバイルSoCが達成する12〜20 tokens/secは、この用途に対しておよそ20倍遅いことになります。
ただし、モバイルSoCの演算性能はおよそ2年ごとに倍増してきた実績があります。このペースであれば、3〜5年以内にオンデバイス翻訳が実用レベルに達する可能性があります。特に、モデルの量子化技術やアーキテクチャ最適化の改善が続いていること、そして特定タスクに特化した1Bパラメータ未満の小型・高効率モデルというトレンドが、このタイムラインをさらに早める可能性もあります。
他の言語ペアへの対応方法
Update: 現在は英語・日本語・スペイン語・中国語の4言語による双方向翻訳に対応しています。以下のガイドは、言語対応の仕組みを理解するための参考資料として残しています。
本システムは当初、英語→日本語の翻訳用に構築されていましたが、他の言語ペアに対応するために必要な変更は3箇所のみです。ストリーミング音声認識、JSONフィールド順序を最適化したLLMストリーミング、WebSocketによる配信というコアアーキテクチャは、言語ペアに関係なく同じように機能します。
変更1:Deepgramの言語パラメータ
音声認識の入力言語は、Deepgramの LiveOptions で設定します。language パラメータを変更するだけで対応できます。
# Current: English input
options = LiveOptions(
model="nova-2",
language="en-US", # ← Change this
encoding="linear16",
sample_rate=16000,
channels=1,
interim_results=True,
utterance_end_ms=1000,
vad_events=True,
)
# Example: Spanish input
options = LiveOptions(
model="nova-2",
language="es", # Spanish
# ... rest stays the same
)
Deepgramは30以上の言語をサポートしています。対応言語コードの全リストは公式ドキュメントを参照してください。
変更2:LLMプロンプト — 翻訳先言語
翻訳プロンプトでは、ソース言語、ターゲット言語、そして intent_label に使用する言語を定義します。別の言語ペアに変更する場合は以下のようになります。
# Current prompt (translates English → Japanese):
INTENT_SYSTEM_PROMPT_SPEED = """あなたはリアルタイム同時通訳AIです。
英語の発話をリアルタイムで日本語に翻訳します。
...
"""
# For Spanish → English translation:
INTENT_SYSTEM_PROMPT_SPEED = """You are a real-time interpreter AI.
Translate Spanish speech into English in real time.
Processing order:
1. Determine dialogue_act
2. Extract intent_label (in English, max 10 words)
3. Identify key slots
4. Generate full_translation in English
Output JSON format (output in this order):
{
"dialogue_act": "QUESTION | PROPOSAL | ...",
"intent_label": "short English label",
"slots": {"when": "", "who": "", "where": "", "what": ""},
"full_translation": "Natural English translation",
"key_terms": ["important words"],
"confidence": 0.0-1.0,
"is_meaning_stable": true/false
}
"""
プロンプトで指定する必要があるのは3点です。(1)ソース言語、(2)ターゲット言語、(3)intent_label の言語。
変更3:フロントエンドのUIラベル
ユーザーに表示するUIテキストを、ターゲット言語に合わせて変更します。
// Current: Japanese labels
<h2>リアルタイム翻訳</h2>
<span>処理中...</span>
<span>翻訳中...</span>
// For English UI:
<h2>Real-time Translation</h2>
<span>processing...</span>
<span>Translating...</span>
必要な変更はこの3箇所のみです。DeepgramのストリーミングAPIがソース言語の音声認識を処理し、LLMの多言語能力がターゲット言語への翻訳を担います。ストリーミングアーキテクチャ、JSONフィールド順序の最適化、WebSocketによる配信パイプラインはすべてそのまま利用できます。
今後の開発構想
ローカルLLMの限界を確認した上で、以下の3つの方向性を検討しています。
1. 音声読み上げ(TTS)によるハンズフリー翻訳
翻訳結果を音声で出力し、Bluetoothイヤホン経由で聞く仕組みを実装しました。Web Speech APIの制約やモバイルブラウザ固有の問題など、実際に手を動かして分かった知見を第4回(補足)にまとめています。
2. 双方向翻訳 ✓
現在は英語→日本語の一方向ですが、逆方向にも対応することで双方向のリアルタイム会話が可能になります。 Update: 英語・日本語・スペイン語・中国語の4言語による双方向翻訳を実装しました。任意の組み合わせで翻訳が可能です。
- 自分が日本語で話す → 英語に翻訳 → 相手に届く
- 相手がスペイン語で話す → 中国語に翻訳 → 自分に届く
3. オープンソーススマートグラスとの連携
近年、オープンソースのスマートグラスプロジェクトがいくつか立ち上がっています。OpenGlass(約$20の部品で普通のメガネをAIスマートグラスに変換)、Mentra(カメラ・スピーカー・マイク搭載、オープンソースSDK提供)、Team Open Smart Glasses(完全オープンソース、ライブ翻訳対応)などです。これらのデバイスとIntent-First Translationを組み合わせることで、相手が話し始めてから0.5秒後に視界に翻訳字幕が表示される体験が技術的には実現可能です。
連載を振り返って
| 記事 | テーマ | 要点 |
|---|---|---|
| 第1回 | 課題と基盤構築 | 「沈黙」問題。Deepgram・FastAPI・WebSocketで基盤を構築 |
| 第2回 | LLMストリーミング | JSONフィールド順序、デュアルプロンプト、ストリーミング抽出 — コード付き |
| 第3回 | 自宅ハードウェアと多言語 | Ollama/LM Studio/モバイルLLMのハンズオン検証。多言語アーキテクチャ(現在4言語対応) |
| 第4回 | TTS & Bluetooth(補足) | Web Speech API、プラットフォーム差異、Bluetoothオーディオルーティング |
Intent-First Translationとは何か — そして何ができないか
このプロジェクトの位置づけについて、率直に述べておきたいと思います。Intent-First Translationは UXの最適化 であり、リアルタイム翻訳の根本的な解決策ではありません。
「翻訳が届く前に意図を先に見せる」というアプローチは、情報を段階的に届けることで体感の待ち時間を短縮するものです。しかし、翻訳そのものにかかる時間は変わりません。最終的には確定翻訳が初期の近似を上書きします。その意味で、現在の翻訳技術が持つ根本的な制約は解消されていません。
リアルタイム翻訳の本質的な進歩は、まったく異なるレベルからもたらされるものだと考えています。より高速なモデルアーキテクチャ、推論に特化したハードウェア、強力なモデルをローカルで動かせるエッジAI、そしておそらくまだ想像されていないパラダイム。これらは半導体技術やモデル研究の進歩を必要とするものであり、個人のエンジニアによるUXの工夫で代替できるものではありません。
それでも共有する価値があると考える理由
それでも、この種の段階的な試みには意味があると考えています。
このシリーズで紹介した技術 — ストリーミングJSONの最適化、情報の段階的配信、状態に応じたプロンプト切替 — は、エンジニアであれば今日から使える実践的なパターンです。現在利用可能なツールの制約のなかで機能し、ユーザー体験を確実に改善できます。
さらに重要なのは、公開されているAPIとオープンソース技術を使えば、エンジニアであれば誰でも自宅でリアルタイムに近い翻訳機を構築できるという事実です。リアルタイム翻訳を試す敷居は、かつてないほど低くなっています。
このシリーズが、次世代のリアルタイム翻訳技術に取り組むエンジニアや研究者にとって、小さな参考点の一つになれば幸いです。