はじめに
ローカルで LLM を動かしたいと思い、llama.cpp を Apple Silicon Mac にネイティブビルドして Metal GPU をフル活用する構成を試した。
今回は Qwen2.5 シリーズ(1.5B / 14B / 32B / 72B)を同一プロンプトで実行し、速度と品質を比較する。
環境
| 項目 | 内容 |
|---|---|
| OS | macOS 26.5 (Tahoe) |
| チップ | Apple Silicon (arm64) |
| RAM | 64 GB(統合メモリ) |
| llama.cpp | build 9222 |
| コンパイラ | AppleClang 21.0.0 |
| GPU バックエンド | Metal (ggml_metal) |
セットアップ
1. llama.cpp をクローンして Metal 有効でビルド
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
cmake -B build \
-DGGML_METAL=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j$(sysctl -n hw.logicalcpu)
-DGGML_METAL=ON で Metal GPU オフロードが有効になる。ビルド後に llama-cli と llama-server の 2 つのバイナリが生成される。
2. モデルのダウンロード
モデルは Hugging Face の bartowski リポジトリから GGUF 形式で取得した。
hf download \
bartowski/Qwen2.5-14B-Instruct-GGUF \
Qwen2.5-14B-Instruct-Q4_K_M.gguf \
--local-dir ~/llama.cpp/models
3. 推論実行コマンド
~/llama.cpp/build/bin/llama-cli \
-m ~/llama.cpp/models/<MODEL>.gguf \
-ngl 99 \
-n 256 \
--temp 0.7 \
-p "<|im_start|>user\nプロンプト<|im_end|>\n<|im_start|>assistant\n"
-ngl 99 で全レイヤーを Metal GPU にオフロードしている。
使用モデル一覧
| モデル | 量子化 | ファイルサイズ | 推定使用 RAM |
|---|---|---|---|
| Qwen2.5-1.5B-Instruct | Q8_0 | 1.5 GB | 約 2 GB |
| Qwen2.5-14B-Instruct | Q4_K_M | 9 GB | 約 11 GB |
| Qwen2.5-32B-Instruct | Q4_K_M | 18 GB | 約 22 GB |
| Qwen2.5-72B-Instruct | Q4_K_M | 44 GB | 約 46 GB |
量子化の選定基準:
- 1.5B は Q8_0(精度優先・サイズが小さいため余裕あり)
- 14B 以上は Q4_K_M(64 GB RAM 内に収める制約のため)
ベンチマーク結果
Generation 速度(t/s)
| モデル | 要約 | コード生成 | 推論 |
|---|---|---|---|
| 1.5B Q8_0 | 139.3 | 135.9 | 134.5 |
| 14B Q4_K_M | 30.5 | 30.0 | 30.3 |
| 32B Q4_K_M | 14.3 | 13.9 | 14.0 |
| 72B Q4_K_M | 3.9 | 3.1 | 3.9 |
速度倍率(1.5B 基準)
| モデル | 倍率 |
|---|---|
| 1.5B | 1x(基準) |
| 14B | 約 4.5x 遅い |
| 32B | 約 10x 遅い |
| 72B | 約 36x 遅い |
品質比較
使用プロンプト
タスク 1: 日本語要約
次の文章を3行で要約してください。「人工知能は1950年代に誕生し...(略)」
タスク 2: コード生成
Python でフィボナッチ数列を返すジェネレータ関数を書いてください。
タスク 3: 論理推論
A は B より年上、B は C より年上、C は D より年上です。4人を年齢順に並べてください。
品質結果
| モデル | 要約(言語) | コード生成 | 推論 |
|---|---|---|---|
| 1.5B | 中国語で回答(失敗) | 正答 | 誤答 |
| 14B | 日本語(正常) | 正答 | 正答 |
| 32B | 日本語(推定) | 正答 | 正答 |
| 72B | 日本語(推定) | 正答 | 正答 |
32B / 72B の出力は RTF 保存時の文字エンコーディング不一致により完全には判読できなかったが、判読可能な箇所から推論タスクの正答(
A > B > C > D)とコードブロックの正答を確認できた。
1.5B の品質問題について
1.5B は軽量なため言語制御が不安定で、日本語プロンプトに対して中国語で回答するケースが発生した。また論理推論タスクでは A→C→B→D という誤った順序を出力した。コード生成のような構造的なタスクは問題なく動作するが、自然言語タスクでの利用はリスクがある。
OpenAI 互換 API サーバーとして使う
llama.cpp には llama-server が付属しており、OpenAI 互換の /v1/chat/completions エンドポイントを提供する。
~/llama.cpp/build/bin/llama-server \
-m ~/llama.cpp/models/Qwen2.5-14B-Instruct-Q4_K_M.gguf \
-ngl 99 \
--host 127.0.0.1 \
--port 8080 \
--ctx-size 4096
# 動作確認
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen",
"messages": [{"role": "user", "content": "日本語で自己紹介してください"}]
}'
1.5B モデルでの llama-server 実行時、Generation 速度は 141.28 t/s だった。CLI 実行時の 1.5B 速度(135.9 t/s)とほぼ同等で、llama-server 経由でもオーバーヘッドはほぼ無いことを確認できた。
まとめ
| モデル | 速度 | 品質 | 推奨用途 |
|---|---|---|---|
| 1.5B | 非常に速い(135 t/s) | 低い | 英語・構造化タスクのみ |
| 14B | 快適(30 t/s) | 良好 | 日常会話・コード生成 |
| 32B | やや遅い(14 t/s) | 高い | 品質重視タスク |
| 72B | 遅い(3.9 t/s) | 最高 | 精度最優先タスク |
14B が速度と品質のバランスで最も実用的という結論になった。
64 GB RAM があれば 72B も動作するが、3.9 t/s は会話用途には厳しい。品質を求めるなら 32B が現実的な選択肢になる。
参考
この記事はclaudeで作成