Home About Contact
Local LLM , Gemma , Llama CPP

Gemma 4 を macOS で使う llama cpp で

Gemma 4 がすごいらしい。 Android 上で動かしたりもできるモデルらしいので、活用していきたいと目論む。

AI 質問しつつなんとなくできたはいいけど絶対忘れるので記録を残す。 ollama ではなく llama cpp を使う。 使用環境は Macbook Air M2 16GB /Tahoe 26.4 です。

llama-cli, llama-mtmd-cli, llama-server をソースからビルドする。

llama-mtmd-cli というのはマルチモーダル用らしい。これを使えば 画像からテキストを生成(image captioning とか)できるのかも。試してない。

llama cpp は こちら https://github.com/ggml-org/llama.cpp

レポジトリを clone:

git clone https://github.com/ggml-org/llama.cpp

ビルドします。(環境はできているとして)

cd llama.cpp
cmake -B build -DBUILD_SHARED_LIBS=OFF -DGGML_CUDA=OFF
cmake --build build
  --config Release -j --clean-first \
  --target llama-cli llama-mtmd-cli llama-server

ビルドできたら成果物は $HOME/.local/llama.cpp/ に配置することにします。

mkdir ~/.local/llama.cpp
cp ./build/bin/llama-* ~/.local/llama.cpp/

~/.local/llama.cpp にパスを通した上で llama-cli が使えることを確認:

$ which llama-cli
/Users/foo/.local/llama.cpp/llama-cli

次のように -hf (これは hugging face からモデルを取得するオプション)を使うと 実行と同時にモデルを入手できるのだが...

llama-cli -hf unsloth/gemma-4-E4B-it-GGUF:Q4_K_M ...

モデルがどこに保存されるか自分で指定したいので、環境変数をセットしておく。

mkdir ~/.local/llama.cpp/models
export LLAMA_CACHE="~/.local/llama.cpp/models"

その上で実行してみる:

llama-cli \
  -hf unsloth/gemma-4-E4B-it-GGUF:Q4_K_M \
  -p "tell me please how to code STDOUT Hello,World! with JS"

初回は -hf で指定したモデルをダウンロードするので時間がかかる。 ダウンロードしたファイルは LLAMA_CACHE 環境編集で指定したところにダウンロード(キャッシュ)される。

この環境ではここにキャッシュされた:

~/.local/llama.cpp/models/models--unsloth--gemma-4-E4B-it-GGUF/snapshots/960a8cd001a5ec7a679e2c5d93f9916238e76d10/gemma-4-E4B-it-Q4_K_M.gguf

二回目に起動する場合は -hf の代わりに -m オプションでモデルを直接指定する:

というか、試してないけど -hf オプションをそのまま使えば別に良い気がする。 ローカルに指定モデルのキャッシュがあればそれを使い、なければダウンロードしてくれる、という振る舞いに違いない。(試してはいない)

m=~/.local/llama.cpp/models/models--unsloth--gemma-4-E4B-it-GGUF/snapshots/960a8cd001a5ec7a679e2c5d93f9916238e76d10/gemma-4-E4B-it-Q4_K_M.gguf

llama-cli \
  -m $m \
  -p "tell me please how to code STDOUT Hello,World! with JS"

llama-cli の代わりに llama-server を使う

次のようにしてサーバを起動:

m=~/.local/llama.cpp/models/models--unsloth--gemma-4-E4B-it-GGUF/snapshots/960a8cd001a5ec7a679e2c5d93f9916238e76d10/gemma-4-E4B-it-Q4_K_M.gguf
llama-server \
  -m $m \
  --jinja \
  --port 8080 \
  -ngl 99 \
  -c 32768

サーバにプロンプトを投げる:

curl http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"gemma-4-e4b","messages":[{"role":"user","content":"tell me please how to code STDOUT Hello,World! with JS"}]}'

モデル 26B MoE を使う

Gemma 4 のモデルの説明

もし使用するマックのメモリが 32GB あれば gemma-4-E4B-it-GGUF の代わりに gemma-4-26B-A4B-it-GGUF を使うことができます。 その場合は -hf オプションを次のように指定します:

-hf unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_XL

実行すると(この環境では)モデルは次の場所にキャッシュされました。

.local/llama.cpp/models/models--unsloth--gemma-4-26B-A4B-it-GGUF/snapshots/8bacec5c8e829a25502cdfe3c3f5b6aabee3218c/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf

番外編) CUDA x RTX 3060 12GB (Ubuntu 24.04 Server)

この環境でもビルドして動かしてみました。

CUDA 12.8 がセットアップされていることが前提として:

git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DGGML_CUDA=ON \
  -DCMAKE_CUDA_ARCHITECTURES="86"
cmake --build build \
  --config Release -j$(nproc) --clean-first \
  --target llama-cli llama-mtmd-cli llama-server

先ほどと同じですが、生成した llama.cpp コマンドに所定の場所にコピーしてそこにパスを通します。

mkdir ~/.local/llama.cpp
cp ./build/bin/llama-* ~/.local/llama.cpp/

モデルを用意します。

unsloth/gemma-4-E4B-it-GGUF:Q8_0

試しに llama-cli を -hf 指定して実行します。

llama-cli -hf unsloth/gemma-4-E4B-it-GGUF:Q8_0 -ngl 99

これで llama-cli コマンドの動作確認とローカルにモデルをキャッシュできたので、 次のような llama-server 起動ファイル start.sh を用意:

#!/bin/bash
model=~/.local/llama.cpp/models/models--unsloth--gemma-4-E4B-it-GGUF/snapshots/960a8cd001a5ec7a679e2c5d93f9916238e76d10/gemma-4-E4B-it-Q8_0.gguf
$HOME/.local/llama.cpp/llama-server -m $model \
  -ngl 99 \
  --jinja \
  --host 127.0.0.1 \
  --port 8080

このサーバにプロンプトを投げて結果は result.json に保存:

curl http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"gemma-4-e4b","messages":[{"role":"user","content":"tell me STDOUT Hello,World! code with haskell, only code no description"}]}' > result.json

結果の result.json のうち関心のある部分だけを取り出す:

jq -r '.choices[0].message.content' result.json

結局こうなりました:

main :: IO ()
main = putStrLn "Hello,World!"

かきかけです。