← 参照コンテンツに戻る MOKUMOKU GUIDE

ローカルLLM導入入門 — GGUF変換とOllama運用

実践技術教科書 · 国産LLMを手元で動かす

国産LLMを手元のMac/PCで動かすための入門。HuggingFaceのモデルをGGUF形式に変換し、Ollamaで推論するまでの原理と手順をまとめています。 (元教材からローカルLLM関連の章だけを抜き出して再構成したものです)

5. LLMのGGUF変換とローカル推論の原理

GGUFフォーマットとは

GGUF(GPT-Generated Unified Format)は、llama.cppプロジェクトが開発したモデル保存形式である。HuggingFaceのSafetensors形式と比較して、以下の特徴を持つ:

量子化の影響

量子化レベル ビット幅 8Bモデルの目安サイズ 品質への影響
F16 16bit 約16GB なし(ベースライン)
Q8_0 8bit 約9GB ほぼなし
Q4_K_M 4bit(混合) 約5GB わずかに低下
Q4_0 4bit 約4.5GB 明確に低下

Q4_K_Mは「重要なレイヤーは高精度、それ以外は低精度」の混合量子化で、サイズと品質のバランスが良い。

HuggingFace → GGUF → Ollama の変換パイプライン

HuggingFace (Safetensors)
    ↓ convert_hf_to_gguf.py
GGUF (F16 or 量子化済み)
    ↓ Modelfile作成
Ollama create
    ↓
ollama run <model-name>

vocab_size問題

LLMのトークナイザーが持つ語彙数(vocab_size)はモデルによって大きく異なる。

モデル vocab_size トークナイザー種別
Llama 2 32,000 BPE
Llama 3 128,000 BPE
Qwen3 151,936 BPE
LLM-jp-4 196,608 Unigram byte-fallback

Ollamaの内部実装(llama.cppベース)は、BPEトークナイザーを前提として設計されており、vocab_sizeが128Kを超えるモデルや、Unigram方式のトークナイザーはロードに失敗する場合がある。

これは「GGUF変換は成功するがOllama起動時にロードエラー」という形で顕在化し、デバッグが困難な落とし穴である。


8. LLM・AI基盤用語

用語 意味
GGUF llama.cpp用のモデル保存形式。単一ファイルにメタデータ・重みを格納
Safetensors HuggingFaceのモデル保存形式。安全で高速なシリアライズ
量子化 (Quantization) モデル重みのビット幅を削減し、サイズと推論速度を改善する技術
Q4_K_M 4bit混合量子化。重要レイヤーは高精度、それ以外は低精度
Ollama ローカルLLMの管理・実行ツール。Modelfileベースでモデル登録
Transformers HuggingFaceのLLMフレームワーク。Python APIでモデルをロード・推論
vocab_size トークナイザーの語彙数。モデルがロード可能かの制約条件になりうる
Unigram (byte-fallback) SentencePieceベースのサブワード分割方式。BPEと異なりmergesを持たない
BPE (Byte Pair Encoding) 最も一般的なサブワード分割方式。mergesリストで分割ルールを定義
SFT Supervised Fine-Tuning。教師あり微調整
DPO Direct Preference Optimization。人間の選好に基づくアライメント手法
MoE Mixture of Experts。推論時に一部のエキスパートのみを活性化する効率的アーキテクチャ
device_map="auto" PyTorch/Transformersでモデルを自動的にGPU/CPU/ディスクに分散配置する設定
chat_template モデル固有の対話フォーマット。Jinja2テンプレートで定義される

11. ローカルLLM導入パイプライン

┌─── HuggingFace ───┐
│                       │
│ モデルカード確認       │
│ ├ vocab_size          │
│ ├ tokenizer type      │
│ └ architecture        │
│         ↓             │
│ snapshot_download      │  ← 大容量モデルは
│ or hf_hub_download     │     個別シャードDLが確実
└───────┬───────┘
          ↓
┌─── GGUF変換 ───────┐
│                           │
│ convert_hf_to_gguf.py     │
│ ├ tokenizer未認識 → パッチ│  ← 新モデルでは頻出
│ ├ --outtype q8_0/q4_k_m   │
│ └ 警告: merges/vocab      │
└───────┬───────┘
          ↓
┌─── Ollama登録 ─────┐      ┌─── フォールバック ──┐
│                         │      │                        │
│ Modelfile作成           │      │ vocab_size > 128K      │
│ ollama create           │      │ or Unigramトークナイザー│
│ ollama run              │      │         ↓              │
│    ↓                    │      │ Transformersで直接推論  │
│ 成功? ─── Yes ──→ 完了 │      │ model.generate()       │
│    │                    │      │                        │
│   No(500 Error)       │      │ ← ここに来たらOllama  │
│    └────────────→│   対応を待つしかない    │
│                         │      └────────────┘
└─────────────┘

14. 日本語LLMの系譜

2023.10  NII LLM-jp-3 公開 — 日本語特化オープンモデルの先駆
         1.8B / 3.7B / 13B / 172B(MoE)
         │
2024     日本語LLMの百花繚乱
         rinna, CyberAgent, Stability AI, LINE 等が参入
         │
2025     GPT-4o / Claude 3.5 が日本語でも高性能を発揮
         ローカルモデルとの性能差が課題に
         │
2026.4   NII LLM-jp-4 公開
         8B(Dense)/ 32B-A3B(MoE)
         GPT-4oをGPT-5.4ジャッジで上回る日本語性能
         │
         ↓ 特徴
         ├ 10.5兆トークン事前学習(日本語は約3.6%)
         ├ 1.2兆トークン中間学習(合成データ含む)
         ├ SFT + DPO(RLなし)
         ├ reasoning_effort 3段階の思考モード
         ├ vocab_size 196,608(Unigram byte-fallback)
         └ OSAID準拠の透明性

LLM-jp-4の技術的な肝は「中間学習」にある。事前学習→いきなりSFTではなく、合成データを含む1.2兆トークンで日本語の指示理解力を底上げするステージを挟んでいる。


17. HuggingFaceモデルをGGUF変換してOllamaに登録する手順

前提条件

Step 1: 依存関係の確認

# 既存venvの検索(新規作成より速い)
find ~/ -maxdepth 4 -name "pyvenv.cfg" -exec grep -l "torch" {} \; 2>/dev/null

# 不足パッケージのインストール
<venv>/bin/pip install gguf sentencepiece accelerate

Step 2: llama.cppの取得

cd /tmp
git clone --depth 1 https://github.com/ggerganov/llama.cpp.git llama_cpp_convert

Step 3: モデルのダウンロード

from huggingface_hub import hf_hub_download
import os

repo = '<org>/<model-name>'
local_dir = '/tmp/<model-name>'

# Step 3a: メタデータ取得
for fname in ['config.json', 'tokenizer.json', 'tokenizer_config.json',
              'model.safetensors.index.json']:
    hf_hub_download(repo, fname, local_dir=local_dir, local_dir_use_symlinks=False)

# Step 3b: 重みファイルのダウンロード(シャード名はindex.jsonから確認)
import json
with open(f'{local_dir}/model.safetensors.index.json') as f:
    shards = set(json.load(f)['weight_map'].values())
for shard in sorted(shards):
    if not os.path.exists(f'{local_dir}/{shard}'):
        hf_hub_download(repo, shard, local_dir=local_dir, local_dir_use_symlinks=False)

Step 4: GGUF変換

<venv>/bin/python3 /tmp/llama_cpp_convert/convert_hf_to_gguf.py \
  /tmp/<model-name> \
  --outfile /tmp/<model-name>-q8_0.gguf \
  --outtype q8_0

トークナイザー未認識エラーへの対処

新しいモデルでは BPE pre-tokenizer was not recognized エラーが発生することがある。

# convert_hf_to_gguf.py の該当行を編集
# 変更前:
raise NotImplementedError("BPE pre-tokenizer was not recognized...")
# 変更後:
res = "default"  # fallback for unrecognized tokenizers

Step 5: Ollama登録

# Modelfile作成
cat > /tmp/Modelfile << 'EOF'
FROM /tmp/<model-name>-q8_0.gguf
PARAMETER temperature 0.7
PARAMETER num_ctx 4096
PARAMETER top_p 0.95
PARAMETER stop "<|endoftext|>"
PARAMETER stop "<|im_end|>"

TEMPLATE """{{- if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{- range .Messages }}{{- if eq .Role "user" }}<|im_start|>user
{{ .Content }}<|im_end|>
{{ end }}{{- if eq .Role "assistant" }}<|im_start|>assistant
{{ .Content }}<|im_end|>
{{ end }}{{- end }}<|im_start|>assistant
"""

SYSTEM "あなたは日本語に堪能なAIアシスタントです。"
EOF

# 登録
ollama create <model-name> -f /tmp/Modelfile

# テスト
ollama run <model-name> "こんにちは"

18. vocab_size巨大モデルのフォールバック推論手順

Ollamaでロードに失敗した場合のTransformersフォールバック手順。

Step 1: 直接ロード

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model_path = "/tmp/<model-name>"
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    device_map="auto",
    dtype=torch.float16,       # 16GB RAMの場合
    trust_remote_code=True
)

Step 2: 推論実行

chat = [{"role": "user", "content": "質問テキスト"}]

# chat_templateの戻り値型に注意
text = tokenizer.apply_chat_template(chat, add_generation_prompt=True, tokenize=False)
inputs = tokenizer(text, return_tensors="pt").to(model.device)

with torch.no_grad():
    output = model.generate(**inputs, max_new_tokens=200, do_sample=True, top_p=0.95, temperature=0.7)

response = tokenizer.decode(output[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
print(response)

注意点


本書は実際の技術検証セッションの記録を基に、固有情報を除去して汎用的な技術教材として再構成したものです。

← 参照コンテンツ一覧に戻る