この記事の対象読者
- ローカルLLMに興味があるが、Ollamaで動かないモデルに遭遇して途方に暮れている方
- NVIDIA Nemotron-Nano-9B-v2-Japanese を自分のPCで動かしてみたい方
- llama.cpp や Open WebUI を使ったローカルAI環境を構築したい方
- HuggingFace の safetensors モデルを GGUF に変換する手順を知りたい方
この記事で得られること
- Nemotron-Nano-9B-v2-Japaneseの全体像: Mamba-2 + Transformer ハイブリッドという革新的アーキテクチャの理解
- Ollamaの罠と回避策: 新アーキテクチャで発生するdivide-by-zeroクラッシュの原因と対処法
- llama.cpp直接利用の完全手順: プリビルドバイナリのダウンロードからGGUF変換、GUI環境構築まで
- コピペで使えるスクリプト集: 起動/停止スクリプト、環境診断スクリプト、ヘルスチェックコマンド
この記事で扱わないこと
- vLLM や TensorRT-LLM を使った推論サーバー構築
- Linux / macOS 環境でのセットアップ(本記事は Windows 11 前提)
- モデルのファインチューニングや追加学習
- ベンチマーク測定の詳細な手法
1. Nemotron-Nano-9B-v2-Japaneseとの出会い
「日本語が得意な小型LLMをローカルで動かしたい」 -- これは多くのAIエンジニアが抱える共通の願望だろう。
2026年2月17日、NVIDIAが Nemotron-Nano-9B-v2-Japanese をリリースした。日本のLLM評価プラットフォーム「Nejumi Leaderboard 4」で10B未満モデルカテゴリ1位を獲得したという。しかも同等サイズの Qwen3-8B を上回るスコアだ。
「これは試すしかない」と意気込んで Ollama に GGUF を食わせたところ、待っていたのは無慈悲な 500 Internal Server Error だった。
この記事は、そこから始まる「動かすまでの全記録」である。料理で言えば、レシピ通りに作ったはずがオーブンが爆発したので、焚き火で焼き上げたようなものだ。結果的にはちゃんと美味しくできたので、同じ目に遭った方はぜひ参考にしてほしい。
ここまでで、この記事の雰囲気が掴めたでしょうか。次は、本題に入る前に知っておくべき用語を整理しておきましょう。
2. 前提知識の確認
本題に入る前に、この記事で登場する用語を確認します。
2.1 GGUF とは
GGUF (GPT-Generated Unified Format) は、llama.cpp プロジェクトが策定したモデルフォーマットです。HuggingFace の safetensors 形式をそのまま使うのではなく、GGUF に変換することで llama.cpp や Ollama で高速に推論できるようになります。
「safetensors が生の食材なら、GGUF は下ごしらえ済みのカット野菜」と思ってください。
2.2 Mamba-2 (SSM) とは
Mamba-2 は、Transformer の代替として注目される State Space Model (SSM) アーキテクチャの一種です。Transformer の Self-Attention は入力長の二乗に比例して計算量が増えますが、SSM は線形にスケールします。
つまり長いコンテキストを効率的に処理できるのが最大の強みです。
2.3 llama-server とは
llama.cpp に含まれる OpenAI互換のAPIサーバー です。/v1/chat/completions エンドポイントを提供するため、ChatGPT API と同じインターフェースでローカルモデルにアクセスできます。
2.4 Open WebUI とは
ローカルLLM用の ChatGPT風GUIインターフェース です。Docker で簡単にデプロイでき、OpenAI互換APIに接続してブラウザからチャットできます。
これらの用語が押さえられたら、Nemotron-Nano-9B-v2-Japanese の背景を見ていきましょう。
3. Nemotron-Nano-9B-v2-Japanese が生まれた背景
3.1 NVIDIAのソブリンAI戦略
NVIDIAは「ソブリンAI(主権AI)」というコンセプトを推進しています。各国・各言語に最適化されたAIモデルを提供し、特定の地域のニーズに応えるという戦略です。
Nemotron-Nano-9B-v2-Japanese はその一環として、ベースモデルの Nemotron-Nano-9B-v2 に対して以下のカスタマイズを施したモデルです。
| カスタマイズ | 内容 |
|---|---|
| 継続事前学習 | 日本語オープンソースコーパスで追加学習 |
| 合成データ | NVIDIAのNemotron-Personasで日本文化に適した対話データを生成 |
| Tool Calling SFT | 日本語でのツール呼び出し能力を強化 |
3.2 なぜ「ハイブリッド」アーキテクチャなのか
従来のLLMは純粋な Transformer で構成されていましたが、Nemotron-Nano-9B-v2 はMamba-2 + Transformer のハイブリッド構成を採用しています。
| 項目 | 詳細 |
|---|---|
| 総レイヤー数 | 56層 |
| Mamba-2 レイヤー | 27層(大半を占める) |
| MLP レイヤー | 25層 |
| Attention レイヤー | わずか4層(14, 21, 30, 39層目のみ) |
56層中たった4層だけが Attention -- これが後述する Ollama の問題の伏線になります。
3.3 モデルの基本スペック
| 項目 | 詳細 |
|---|---|
| パラメータ数 | 8.89B |
| アーキテクチャ | nemotron_h (Mamba-2 + Transformer ハイブリッド) |
| コンテキスト長 | 1,048,576 トークン(約100万トークン) |
| トークナイザ | Tekken (BPE, 131,072 語彙) |
| 対応言語 | en, es, fr, de, it, ja |
| ライセンス | NVIDIA Open Model License(商用利用可) |
| HuggingFace | nvidia/NVIDIA-Nemotron-Nano-9B-v2-Japanese |
背景がわかったところで、私が実際に遭遇した問題とその解決策を見ていきましょう。
4. 基本概念と仕組み ── Ollama の落とし穴
4.1 最初のアプローチ: Ollama で動かそうとした
ローカルLLMを動かすとき、まず試すのは Ollama だろう。コミュニティが公開した GGUF を pull して実行するだけ -- のはずだった。
ollama pull mirage335/NVIDIA-Nemotron-Nano-9B-v2-virtuoso
ollama run mirage335/NVIDIA-Nemotron-Nano-9B-v2-virtuoso
結果:
Error: 500 Internal Server Error: llama runner process has terminated: exit status 2
クラッシュ。
4.2 エラーログの解析
Ollama のサーバーログ(%LOCALAPPDATA%\Ollama\server.log)を覗くと、こんなスタックトレースが記録されていた。
load_tensors: loading model tensors, this can take a while... (mmap = false)
Exception 0xc0000094 0x0 0x0 0x7ff705c5f227
0xc0000094 = Windows の STATUS_INTEGER_DIVIDE_BY_ZERO
ゼロ除算。CUDAバックエンドでモデルのテンソルをロードする際に発生している。
4.3 原因の特定
切り分けを行った結果がこちら。
| 確認項目 | 結果 |
|---|---|
| GPU 認識 | OK |
| CUDA ライブラリ | OK (cuda_v13, compute 12.0) |
| モデルメタデータ読み込み | OK (arch = nemotron_h) |
| テンソルローディング | NG (divide-by-zero) |
結論: Ollama 0.16.2 に内蔵された llama.cpp の CUDA バックエンドに、nemotron_h アーキテクチャのテンソルロード処理のバグがある。 SSMレイヤーのパラメータ(ssm_d_conv, ssm_d_inner, ssm_d_state 等)の処理でゼロ除算が発生している可能性が高い。
4.4 試したが効果がなかったこと
| 試行 | 結果 |
|---|---|
OLLAMA_NEW_ENGINE=true で再起動 |
同じエラー |
| 別の GGUF (bartowski Q8_0) | 同じエラー |
| CPU モードへのフォールバック | GPU 検出タイムアウトで不安定 |
私はここで3時間ほどハマりました。「Ollama は万能ではない」という教訓を身をもって学んだ瞬間です。
基本概念が理解できたところで、実際の解決策に進みましょう。
5. 実践:llama.cpp で動かしてみよう
Ollama の内蔵 llama.cpp にバグがあるなら、最新の llama.cpp を直接使えばいい。シンプルな発想だが、これが正解だった。
5.1 環境構築
必要なもの
| 項目 | 要件 |
|---|---|
| OS | Windows 11 |
| GPU | NVIDIA GPU(CUDA対応) |
| VRAM | 10GB以上(BF16なら18GB以上推奨) |
| ストレージ | 40GB以上の空き容量 |
| ソフトウェア | Python 3.9+, Git, Docker Desktop |
llama.cpp プリビルドバイナリのダウンロード
llama.cpp は GitHub Releases でプリビルドバイナリを配布しているため、ビルド不要で利用できる。
# ===== llama.cpp プリビルドバイナリのダウンロード =====
$version = "b8086" # 2026-02-17 リリース
# --- CUDA 12.x 環境の場合 ---
# バイナリ本体
Invoke-WebRequest `
-Uri "https://github.com/ggml-org/llama.cpp/releases/download/$version/llama-$version-bin-win-cuda-12.4-x64.zip" `
-OutFile "C:\AI\llama-cpp\llama-cuda12.zip"
# CUDA ランタイム DLL
Invoke-WebRequest `
-Uri "https://github.com/ggml-org/llama.cpp/releases/download/$version/cudart-llama-bin-win-cuda-12.4-x64.zip" `
-OutFile "C:\AI\llama-cpp\cudart-cuda12.zip"
# 展開
Expand-Archive "C:\AI\llama-cpp\llama-cuda12.zip" -DestinationPath "C:\AI\llama-cpp\bin" -Force
Expand-Archive "C:\AI\llama-cpp\cudart-cuda12.zip" -DestinationPath "C:\AI\llama-cpp\bin" -Force
Blackwell GPU (RTX 50シリーズ / RTX PRO 6000) をお使いの方へ
Compute Capability 12.0 には CUDA 13.1 ビルドが必要です。上記のcuda-12.4をcuda-13.1に読み替えてください。
5.2 環境別の設定ファイル
用途に応じて以下の3種類の設定を使い分けてください。
開発環境用(ローカルテスト向け)
# config-dev.yaml - 開発環境用(コピペしてそのまま使える)
model_path: "C:\\AI\\models\\nemotron-nano-9b-v2-japanese.bf16.gguf"
server:
host: "127.0.0.1" # ローカルのみ
port: 8081
parallel_slots: 2 # 開発時は2で十分
context_length: 4096 # 短めでメモリ節約
gpu_layers: 999 # 全レイヤーGPUオフロード
alias: "nemotron-nano-9b-v2-japanese"
logging:
level: "DEBUG"
verbose: true
本番環境用(常時起動サーバー向け)
# config-prod.yaml - 本番環境用
model_path: "C:\\AI\\models\\nemotron-nano-9b-v2-japanese.bf16.gguf"
server:
host: "0.0.0.0" # 外部アクセス許可
port: 8081
parallel_slots: 4 # 同時接続数を増やす
context_length: 8192 # 長めのコンテキスト
gpu_layers: 999
alias: "nemotron-nano-9b-v2-japanese"
logging:
level: "INFO"
verbose: false
テスト環境用(CI/CD・動作確認向け)
# config-test.yaml - テスト環境用
model_path: "C:\\AI\\models\\nemotron-nano-9b-v2-japanese.q8_0.gguf" # 量子化版で軽量
server:
host: "127.0.0.1"
port: 8082 # 本番とポートを分離
parallel_slots: 1
context_length: 2048 # 最小限
gpu_layers: 999
alias: "nemotron-nano-9b-v2-japanese-test"
logging:
level: "DEBUG"
verbose: true
補足: llama-server 自体は YAML 設定ファイルを直接読みませんが、上記をパラメータの管理・共有用として使い、起動スクリプトから参照する運用がおすすめです。
5.3 GGUF 変換 ── safetensors から GGUF へ
リリース直後はコミュニティ GGUF が存在しないことが多い。自分で変換する手順を知っておくと重宝する。
Step 1: 変換用の仮想環境を作成
# llama.cpp リポジトリをクローン(変換スクリプト取得用)
git clone --depth 1 https://github.com/ggml-org/llama.cpp.git C:\AI\llama-cpp\repo
# 変換専用の仮想環境を作成(グローバル環境を汚さない)
python -m venv C:\AI\llama-cpp\venv
# 依存関係インストール(CPU版 torch で十分。約114MBと軽量)
cmd /c "C:\AI\llama-cpp\venv\Scripts\pip.exe install numpy sentencepiece gguf transformers safetensors huggingface_hub torch --index-url https://download.pytorch.org/whl/cpu"
重要: --index-url https://download.pytorch.org/whl/cpu を必ず指定してください。 GPU版 torch をインストールすると、既存の CUDA 環境と競合する可能性があります。変換処理に GPU は不要です。
Step 2: モデルのダウンロード
# download_model.py
from huggingface_hub import snapshot_download
path = snapshot_download(
"nvidia/NVIDIA-Nemotron-Nano-9B-v2-Japanese",
local_dir="C:/AI/models/nemotron-nano-9b-v2-japanese",
resume_download=True,
)
print(f"Downloaded to: {path}")
# 実行
cmd /c "C:\AI\llama-cpp\venv\Scripts\python.exe download_model.py"
4つの safetensors シャード、合計約18GB。回線速度にもよるが3-10分程度で完了する。
Step 3: BF16 GGUF への変換
cmd /c "C:\AI\llama-cpp\venv\Scripts\python.exe C:\AI\llama-cpp\repo\convert_hf_to_gguf.py C:\AI\models\nemotron-nano-9b-v2-japanese --outtype bf16 --outfile C:\AI\models\nemotron-nano-9b-v2-japanese.bf16.gguf"
| 変換オプション | 説明 | 出力サイズ目安 | 推奨環境 |
|---|---|---|---|
--outtype bf16 |
BFloat16 (量子化なし) | ~17 GB | VRAM 24GB以上 |
--outtype f16 |
Float16 (量子化なし) | ~17 GB | VRAM 24GB以上 |
--outtype q8_0 |
8bit 量子化 | ~9 GB | VRAM 12GB以上 |
変換時間: 約 1.5 分(NVMe SSD 環境)。
(オプション) さらに量子化する場合
& "C:\AI\llama-cpp\bin\llama-quantize.exe" `
"C:\AI\models\nemotron-nano-9b-v2-japanese.bf16.gguf" `
"C:\AI\models\nemotron-nano-9b-v2-japanese.q8_0.gguf" `
Q8_0
5.4 llama-server の起動と動作確認
& "C:\AI\llama-cpp\bin\llama-server.exe" `
-m "C:\AI\models\nemotron-nano-9b-v2-japanese.bf16.gguf" `
--alias "nemotron-nano-9b-v2-japanese" `
-ngl 999 `
-c 8192 `
--port 8081 `
--host 0.0.0.0 `
-np 4
| オプション | 説明 |
|---|---|
-ngl 999 |
全レイヤーを GPU にオフロード |
-c 8192 |
コンテキスト長(トークン数) |
-np 4 |
並列スロット数 |
--alias |
OpenAI API で返すモデル名 |
起動ログに以下が表示されれば成功。
init: chat template, thinking = 1
thinking = 1 は思考モード(Chain-of-Thought)が有効であることを示す。llama-server は tokenizer_config.json のチャットテンプレートを自動検出するため、手動設定は不要だ。
5.5 Open WebUI で GUI チャット環境を構築
全体アーキテクチャ
ブラウザ (http://localhost:3000)
│
▼
Open WebUI (Docker, port 3000)
│ OpenAI 互換 API (/v1/chat/completions)
▼
llama-server (port 8081)
│
▼
NVIDIA GPU (CUDA)
Open WebUI の起動
docker run -d `
-p 3000:8080 `
--add-host=host.docker.internal:host-gateway `
-e OPENAI_API_BASE_URL=http://host.docker.internal:8081/v1 `
-e OPENAI_API_KEY=dummy `
-e OLLAMA_BASE_URL=http://host.docker.internal:11434 `
-v open-webui:/app/backend/data `
--name open-webui --restart always `
ghcr.io/open-webui/open-webui:main
| 環境変数 | 値 | 説明 |
|---|---|---|
OPENAI_API_BASE_URL |
http://host.docker.internal:8081/v1 |
llama-server への接続先 |
OPENAI_API_KEY |
dummy |
llama-server は認証不要だが空にはできない |
OLLAMA_BASE_URL |
http://host.docker.internal:11434 |
Ollama も並行利用する場合 |
host.docker.internal はDockerコンテナからホストマシンの localhost にアクセスするための特殊ホスト名です。 --add-host=host.docker.internal:host-gateway フラグは念のため付けておきましょう。
初期設定
-
http://localhost:3000にアクセス - 管理者アカウントを作成(最初に作成したアカウントが管理者になる)
- モデル一覧に
nemotron-nano-9b-v2-japaneseが自動表示される - モデルを選択してチャット開始
5.6 よくあるエラーと対処法
| エラー | 原因 | 対処法 |
|---|---|---|
llama runner process has terminated: exit status 2 |
Ollama の nemotron_h バグ | llama-server を直接使用する |
Exception 0xc0000094(サーバーログ) |
CUDA テンソルロード時のゼロ除算 | llama-server を直接使用する |
ModuleNotFoundError: No module named 'typer' |
huggingface_hub CLI の依存不足 | pip install typer |
ModuleNotFoundError: No module named 'torch' |
変換用 venv に torch 未インストール | CPU版 torch をインストール |
No matching distribution found for open-webui |
Python 3.10 非対応 | Docker を使用する |
You do not have permission to access this resource |
管理者アカウント重複 | Docker ボリューム削除して再作成 |
failed to connect to the docker API |
Docker Desktop 未起動 | Docker Desktop を起動する |
failure during GPU discovery(サーバーログ) |
GPU 検出タイムアウト(一時的) | Ollama を再起動する |
5.7 環境診断スクリプト
問題が発生した場合は、以下のスクリプトで環境を一括チェックできます。
#!/usr/bin/env python3
"""
Nemotron ローカル環境診断スクリプト
実行方法: python check_env.py
"""
import sys
import shutil
import subprocess
from pathlib import Path
def check_python_version():
"""Pythonバージョン確認"""
if sys.version_info < (3, 9):
return f"Python 3.9以上が必要です(現在: {sys.version})"
return None
def check_required_packages():
"""変換用パッケージの確認"""
issues = []
required = ["torch", "transformers", "safetensors", "gguf", "huggingface_hub"]
for pkg in required:
try:
__import__(pkg)
except ImportError:
issues.append(f"{pkg} がインストールされていません")
return issues
def check_gpu():
"""GPU / CUDA の確認"""
try:
result = subprocess.run(
["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader"],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
return None, result.stdout.strip()
return "nvidia-smi の実行に失敗しました", None
except FileNotFoundError:
return "nvidia-smi が見つかりません(NVIDIA ドライバ未インストール?)", None
except Exception as e:
return f"GPU 確認中にエラー: {e}", None
def check_docker():
"""Docker の確認"""
if not shutil.which("docker"):
return "Docker がインストールされていません"
try:
result = subprocess.run(
["docker", "info"], capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
return "Docker Desktop が起動していません"
except Exception:
return "Docker の確認に失敗しました"
return None
def check_llama_server(bin_path="C:\\AI\\llama-cpp\\bin\\llama-server.exe"):
"""llama-server バイナリの確認"""
if not Path(bin_path).exists():
return f"llama-server が見つかりません: {bin_path}"
return None
def check_model_file(model_path="C:\\AI\\models\\nemotron-nano-9b-v2-japanese.bf16.gguf"):
"""モデルファイルの確認"""
p = Path(model_path)
if not p.exists():
return f"モデルファイルが見つかりません: {model_path}"
size_gb = p.stat().st_size / (1024 ** 3)
if size_gb < 1:
return f"モデルファイルが小さすぎます: {size_gb:.2f} GB"
return None
def main():
"""メイン診断処理"""
print("=" * 60)
print(" Nemotron ローカル環境診断")
print("=" * 60)
all_ok = True
# Python
issue = check_python_version()
if issue:
print(f" [NG] {issue}")
all_ok = False
else:
print(f" [OK] Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
# GPU
issue, gpu_info = check_gpu()
if issue:
print(f" [NG] {issue}")
all_ok = False
else:
print(f" [OK] GPU: {gpu_info}")
# Docker
issue = check_docker()
if issue:
print(f" [NG] {issue}")
all_ok = False
else:
print(" [OK] Docker")
# llama-server
issue = check_llama_server()
if issue:
print(f" [NG] {issue}")
all_ok = False
else:
print(" [OK] llama-server")
# モデルファイル
issue = check_model_file()
if issue:
print(f" [NG] {issue}")
all_ok = False
else:
print(" [OK] モデルファイル")
# パッケージ(変換用 venv 内で実行した場合)
pkg_issues = check_required_packages()
if pkg_issues:
for pi in pkg_issues:
print(f" [NG] {pi}")
all_ok = False
else:
print(" [OK] 変換用パッケージ")
print("=" * 60)
if all_ok:
print(" すべてのチェックをパスしました!")
else:
print(" 上記の問題を解決してから再実行してください")
print("=" * 60)
if __name__ == "__main__":
main()
5.8 起動/停止スクリプト
起動スクリプト(start-nemotron-japanese.ps1)
# C:\AI\start-nemotron-japanese.ps1
$ErrorActionPreference = "Stop"
$modelPath = "C:\AI\models\nemotron-nano-9b-v2-japanese.bf16.gguf"
$llamaServer = "C:\AI\llama-cpp\bin\llama-server.exe"
if (!(Test-Path $modelPath)) {
Write-Host "Model not found: $modelPath" -ForegroundColor Red
exit 1
}
# llama-server 起動
$existing = netstat -ano 2>$null | Select-String ":8081.*LISTENING"
if ($existing) {
Write-Host "Port 8081 already in use. Skipping." -ForegroundColor Yellow
} else {
Write-Host "Starting llama-server..." -ForegroundColor Green
Start-Process -FilePath $llamaServer -ArgumentList @(
"-m", $modelPath, "--alias", "nemotron-nano-9b-v2-japanese",
"-ngl", "999", "-c", "8192", "--port", "8081",
"--host", "0.0.0.0", "-np", "4"
) -WindowStyle Minimized
Start-Sleep -Seconds 5
}
# Open WebUI (Docker) 起動
$container = docker ps --filter "name=open-webui" --format "{{.Names}}" 2>$null
if ($container -eq "open-webui") {
Write-Host "Open WebUI already running." -ForegroundColor Yellow
} else {
docker start open-webui 2>$null
if ($LASTEXITCODE -ne 0) {
docker run -d -p 3000:8080 `
--add-host=host.docker.internal:host-gateway `
-e OPENAI_API_BASE_URL=http://host.docker.internal:8081/v1 `
-e OPENAI_API_KEY=dummy `
-e OLLAMA_BASE_URL=http://host.docker.internal:11434 `
-v open-webui:/app/backend/data `
--name open-webui --restart always `
ghcr.io/open-webui/open-webui:main
}
}
Write-Host "`n Open WebUI: http://localhost:3000" -ForegroundColor Cyan
Write-Host " llama-server: http://localhost:8081" -ForegroundColor Cyan
停止スクリプト(stop-nemotron-japanese.ps1)
# C:\AI\stop-nemotron-japanese.ps1
netstat -ano 2>$null | Select-String ":8081.*LISTENING" | ForEach-Object {
$parts = ($_.ToString().Trim() -split '\s+')
$procId = $parts[-1]
if ($procId -and $procId -ne "0") {
Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
}
}
docker stop open-webui 2>$null
Write-Host "Stopped." -ForegroundColor Green
5.9 ヘルスチェック
# API 応答確認
python -c "import urllib.request, json; r = urllib.request.urlopen('http://127.0.0.1:8081/v1/models'); print(json.loads(r.read().decode())['data'][0]['id'])"
# チャット動作確認
python -c "
import urllib.request, json
req = urllib.request.Request('http://127.0.0.1:8081/v1/chat/completions',
data=json.dumps({'model':'nemotron-nano-9b-v2-japanese',
'messages':[{'role':'user','content':'日本の首都はどこですか?'}],
'max_tokens':50}).encode('utf-8'),
headers={'Content-Type':'application/json'})
resp = urllib.request.urlopen(req)
data = json.loads(resp.read().decode('utf-8'))
print(data['choices'][0]['message']['content'])
"
実装方法がわかったので、次は具体的なユースケースを見ていきます。
6. ユースケース別ガイド
6.1 ユースケース1: 個人開発のチャットボット
想定読者: 個人開発者、趣味でAIチャットを楽しみたい方
推奨構成: llama-server + Open WebUI(最小構成)
サンプルコード:
"""
最小構成でのチャットリクエスト
実行方法: python simple_chat.py
"""
import json
import urllib.request
def chat(message: str, model: str = "nemotron-nano-9b-v2-japanese") -> str:
"""llama-server にチャットリクエストを送信"""
payload = {
"model": model,
"messages": [{"role": "user", "content": message}],
"max_tokens": 512,
"temperature": 0.7,
}
req = urllib.request.Request(
"http://127.0.0.1:8081/v1/chat/completions",
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read().decode("utf-8"))
return data["choices"][0]["message"]["content"]
if __name__ == "__main__":
answer = chat("Pythonでフィボナッチ数列を生成する関数を書いてください")
print(answer)
6.2 ユースケース2: OpenAI SDKからの利用(既存コードの移行)
想定読者: OpenAI API を使ったアプリを持っていて、ローカルLLMに切り替えたい方
推奨構成: llama-server をバックエンドに、openai Python SDK でアクセス
サンプルコード:
"""
OpenAI SDK で llama-server に接続
実行方法: pip install openai && python openai_compat.py
"""
from openai import OpenAI
# base_url を llama-server に向けるだけ
client = OpenAI(
base_url="http://127.0.0.1:8081/v1",
api_key="dummy", # llama-server は認証不要
)
response = client.chat.completions.create(
model="nemotron-nano-9b-v2-japanese",
messages=[
{"role": "system", "content": "あなたは親切な日本語アシスタントです。"},
{"role": "user", "content": "機械学習と深層学習の違いを簡潔に説明してください。"},
],
max_tokens=512,
temperature=0.7,
)
print(response.choices[0].message.content)
ポイント: base_url を変えるだけで、既存の OpenAI API コードがそのまま動く。
6.3 ユースケース3: ストリーミング対応のWebアプリ
想定読者: FastAPI 等で自作チャットアプリを構築したい方
推奨構成: llama-server + FastAPI + SSE (Server-Sent Events)
サンプルコード:
"""
FastAPI + llama-server ストリーミングチャット
実行方法: pip install fastapi uvicorn openai && uvicorn stream_app:app --port 8000
"""
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from openai import OpenAI
app = FastAPI()
client = OpenAI(base_url="http://127.0.0.1:8081/v1", api_key="dummy")
def generate_stream(message: str):
"""ストリーミングレスポンスを生成"""
stream = client.chat.completions.create(
model="nemotron-nano-9b-v2-japanese",
messages=[{"role": "user", "content": message}],
max_tokens=1024,
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield f"data: {chunk.choices[0].delta.content}\n\n"
yield "data: [DONE]\n\n"
@app.get("/chat")
async def chat(message: str = "こんにちは"):
return StreamingResponse(
generate_stream(message),
media_type="text/event-stream",
)
ユースケースを把握できたところで、この先の学習パスを確認しましょう。
7. 学習ロードマップ
この記事を読んだ後、次のステップとして以下をおすすめします。
初級者向け(まずはここから)
- llama.cpp 公式 README を読んで、オプションの全体像を把握する
- Open WebUI 公式ドキュメント で追加機能(RAG、プラグイン等)を試す
- 量子化レベル(Q4_K_M, Q5_K_M, Q8_0)を変えて、品質と速度のトレードオフを体感する
中級者向け(実践に進む)
- 自分のプロジェクトに OpenAI SDK 経由で組み込む(ユースケース2参照)
- llama.cpp の OpenAI互換APIドキュメント で Function Calling を試す
- Docker Compose で llama-server + Open WebUI を一括管理する構成に移行する
上級者向け(さらに深く)
- Nemotron-H テクニカルレポート でハイブリッドアーキテクチャの内部実装を理解する
- NeMo Framework を使ったファインチューニングに挑戦する
- vLLM や TensorRT-LLM での推論パイプラインを構築して、スループットを最大化する
8. まとめ
この記事では、NVIDIA Nemotron-Nano-9B-v2-Japanese について以下を解説しました。
- Mamba-2 + Transformer ハイブリッドという革新的アーキテクチャ -- 56層中わずか4層だけが Attention という大胆な設計
- Ollama の nemotron_h 対応バグと回避策 -- llama.cpp のプリビルドバイナリを直接使うことで解決
- safetensors → GGUF 変換の完全手順 -- CPU版 PyTorch だけあれば数分で完了
- Open WebUI + llama-server による GUI チャット環境 -- Docker で手軽に構築可能
- 3つのユースケース別サンプルコード -- 個人利用から Web アプリまで
私の所感
Ollama は間違いなくローカルLLMのゲームチェンジャーだ。しかし、新しいアーキテクチャへの対応は llama.cpp 本体に遅れることがある。「Ollama で動かないなら llama.cpp を直接使え」 -- これは今後も通用する鉄則だと思う。
Nemotron-Nano-9B-v2-Japanese は、9Bパラメータという比較的小型なサイズながら、日本語の品質は驚くほど高い。Nejumi Leaderboard 4 での10B未満1位は伊達じゃない。ローカルで日本語LLMを動かしたい方には、現時点で最有力の選択肢の一つだろう。
Ollama のバグは将来修正されるはずだ。その時は Ollama に戻すのも容易だし、llama-server で得た知識は無駄にならない。「遠回りしたけど、結果的にはいい経験だった」 -- そう言える記事になっていれば幸いだ。