前の記事でvLLmでGemma4のMTPを動かしていたんだけど、LLM運用以外にもサーバを使いたい身としてはVRAMがあればあるだけ食べてしまうvLLMはちょっと辛かったんですな。
で、一旦はOllamaでオフィシャルに配布されているQwen3.6:27bのMTPイメージに切り替えていたんだけど、こっちは手軽だし性能面も悪く無いものの応対の日本語がちょっと微妙・・・
そんなわけで紆余曲折、単純に執筆時点ではOllama公式ではmtpなgemma4イメージが上がってない+OpenClaw等で恒常的に利用するので単独で駐在してた方が都合が良いということで、今時点ではllama.cpp serverでGemma4を動かすという方向で落ち着いている。
環境としてはUbuntu 24.04でRocm7.2.4を導入済み、できる限りサーバ的なものはDockerで運用する方針。
llama.cppは高頻度で更新されるが、ちょいちょい仕様変更があったりバグったりというのがあるので、雑にlatestタグに任せるのではなくちゃんとバージョンを明記して運用した方が良い。
(まさに直近でもb9737以降しばらくGemma4 MTPで上手く起動できないバグがあり、直近のb9935でようやっと動きそうだったのもあって、記録としてこの記事を書こうと思った)
以下のような.envでバージョン更新だったりモデルファイルだったりを指定。
# === Release Tags ===
TAG_STABLE=server-rocm-b9737
TAG_BLEEDING=server-rocm-b9935
# 現在アクティブにするタグをここでスイッチ
LLAMA_IMAGE_TAG=${TAG_BLEEDING}
# 同じUnslothのリポジトリを指定
LLAMA_MODEL=unsloth/gemma-4-31B-it-qat-GGUF
LLAMA_MODEL_ALIAS=gemma-4:31b
# ファイル名(または相対パス)を直接指定するために環境変数を分ける
LLAMA_MAIN_FILE=gemma-4-31B-it-qat-UD-Q4_K_XL.gguf
LLAMA_DRAFT_FILE=mtp-gemma-4-31B-it.gguf
# コンテキストサイズ
LLAMA_CTX_SIZE=131072
LLAMA_N_PARALLEL=1
# === ROCm Prefill 高速化チューニング ===
# 全体の論理バッチを大きくして並列演算を促進
LLAMA_BATCH_SIZE=4096
# GPUの演算効率(行列サイズ)に最適化
LLAMA_UBATCH_SIZE=512
# === CPUオーバーヘッド削減 ===
# GPU完全オフロード時はCPU側を絞った方がオーバーヘッドが減る
LLAMA_THREADS=4
LLAMA_THREADS_BATCH=8
# === Gemma 4 最適化サンプリング ===
LLAMA_TEMP=1.0
LLAMA_TOP_K=64
LLAMA_TOP_P=0.95
LLAMA_MIN_P=0.05
LLAMA_REPEAT_PENALTY=1.0
以下のようなcompose.ymlで運用。
services:
llama-cpp-gpu:
image: ghcr.io/ggml-org/llama.cpp:${LLAMA_IMAGE_TAG:-server-rocm}
ports:
- "10000:10000"
volumes:
- llama-cache:/app/models
environment:
- LLAMA_MODEL=${LLAMA_MODEL}
- LLAMA_N_GPU_LAYERS=${LLAMA_N_GPU_LAYERS:--1}
- LLAMA_CTX_SIZE=${LLAMA_CTX_SIZE:-32768}
- LLAMA_BATCH_SIZE=${LLAMA_BATCH_SIZE:-2048}
- LLAMA_UBATCH_SIZE=${LLAMA_UBATCH_SIZE:-1024}
- LLAMA_THREADS=${LLAMA_THREADS:-16}
- LLAMA_N_PARALLEL=${LLAMA_N_PARALLEL:-8}
- LLAMA_TEMP=${LLAMA_TEMP:-0.8}
- LLAMA_TOP_K=${LLAMA_TOP_K:-40}
- LLAMA_TOP_P=${LLAMA_TOP_P:-0.95}
- LLAMA_MIN_P=${LLAMA_MIN_P:-0.05}
- LLAMA_REPEAT_PENALTY=${LLAMA_REPEAT_PENALTY:-1.1}
- LLAMA_CACHE=models
command: >
-hf ${LLAMA_MODEL}
-m ${LLAMA_MAIN_FILE}
-hfd ${LLAMA_MODEL}
--model-draft ${LLAMA_DRAFT_FILE}
--spec-type draft-mtp
--spec-draft-n-max 4
--alias ${LLAMA_MODEL_ALIAS}
--host 0.0.0.0
--port 10000
--n-gpu-layers 99
--n-gpu-layers-draft 99
--ctx-size ${LLAMA_CTX_SIZE}
--fit off
--batch-size ${LLAMA_BATCH_SIZE}
--ubatch-size ${LLAMA_UBATCH_SIZE}
--threads ${LLAMA_THREADS}
--threads-batch ${LLAMA_THREADS_BATCH}
--parallel ${LLAMA_N_PARALLEL}
--temp ${LLAMA_TEMP}
--top-k ${LLAMA_TOP_K}
--top-p ${LLAMA_TOP_P}
--min-p ${LLAMA_MIN_P}
--repeat-penalty ${LLAMA_REPEAT_PENALTY}
--cont-batching
--mlock
--jinja
--flash-attn on
-ctk q8_0
-ctv q8_0
--reasoning-format auto
restart: unless-stopped
devices:
- /dev/kfd:/dev/kfd
- /dev/dri:/dev/dri
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
llama-cache:
まさに直近の更新によってMTPが高速化していて、(自前スクリプトでの雑測定だけど)vLLMで15 Token/sec弱だったのが、llama.cpp serverでは20→ちょっと設定見直して 22 Token/sec前後まで上がってくれた。
MoEモデルの水準からするとそれでも遅くは感じられるが、OpenClawで実際に運用すると安定して自己メンテナンスできるのはこの位のサイズのDenseモデルからだなという体感があるので、現時点での速度と能力の実用バランスを考えるとこれで運用するのが良い塩梅だと思う。
追記: ROCm7.14がリリースされたので・・・
公式イメージはこの記事の記述時点ではベースがROCm7.2.1なのだが、せっかくサーバの方を7.14にアプグレしたので対応するイメージを自前で作ってみる。
Dockerfile: 公式のイメージの記述を踏襲しつつ、ROCm7.14に変更。
ARG UBUNTU_VERSION=24.04
ARG ROCM_VERSION=7.14.0
ARG AMDGPU_VERSION=7.14
# 1. UIのビルドステージ
FROM docker.io/node:24 AS web
WORKDIR /app
RUN apt-get update && apt-get install -y git && git clone --depth 1 https://github.com/ggml-org/llama.cpp.git .
WORKDIR /app/tools/ui
RUN npm ci && npm run build
# 2. 本体ビルドステージ
FROM docker.io/rocm/dev-ubuntu-${UBUNTU_VERSION}:${ROCM_VERSION}-full AS build
ARG ROCM_DOCKER_ARCH='gfx908;gfx90a;gfx942;gfx1030;gfx1100;gfx1101;gfx1102;gfx1151;gfx1150;gfx1200;gfx1201'
ENV AMDGPU_TARGETS=${ROCM_DOCKER_ARCH}
RUN apt-get update && apt-get install -y build-essential cmake git libssl-dev curl libgomp1
WORKDIR /app
RUN git clone --depth 1 https://github.com/ggml-org/llama.cpp.git .
COPY --from=web /app/tools/ui/dist tools/ui/dist
RUN HIPCXX="$(hipconfig -l)/clang" HIP_PATH="$(hipconfig -R)" \
cmake -S . -B build \
-DGGML_HIP=ON \
-DGGML_HIP_ROCWMMA_FATTN=ON \
-DAMDGPU_TARGETS="$ROCM_DOCKER_ARCH" \
-DGGML_BACKEND_DL=ON \
-DGGML_CPU_ALL_VARIANTS=ON \
-DCMAKE_BUILD_TYPE=Release \
-DLLAMA_BUILD_TESTS=OFF \
&& cmake --build build --config Release -j$(nproc)
RUN mkdir -p /app/lib && find build -name "*.so*" -exec cp -P {} /app/lib \;
RUN mkdir -p /app/full && cp build/bin/* /app/full
# 3. 実行用ベースステージ
FROM docker.io/rocm/dev-ubuntu-${UBUNTU_VERSION}:${ROCM_VERSION}-full AS base
RUN apt-get update && apt-get install -y libgomp1 curl ffmpeg \
&& apt-get autoremove -y && apt-get clean -y \
&& rm -rf /tmp/* /var/tmp/* \
&& find /var/cache/apt/archives /var/lib/apt/lists -not -name lock -type f -delete \
&& find /var/cache -type f -delete
COPY --from=build /app/lib/ /app
# 4. サーバー実行ステージ
FROM base AS server
ENV LLAMA_ARG_HOST=0.0.0.0
# 🌟 Issue #25807 対策:ROCm 7.14 で変更されたライブラリパスを通す
ENV LD_LIBRARY_PATH=/opt/rocm/lib:/opt/rocm/core-7.14/lib:/app:${LD_LIBRARY_PATH}
COPY --from=build /app/full/llama-server /app/llama-server
WORKDIR /app
HEALTHCHECK CMD [ "curl", "-f", "http://localhost:8080/health" ]
ENTRYPOINT [ "/app/llama-server" ]
compose.local.yml: イメージ部分だけオーバーライドする設定を作成。
services:
llama-cpp-gpu:
build:
context: .
dockerfile: Dockerfile
target: server
args:
ROCM_VERSION: "7.14.0"
AMDGPU_VERSION: "7.14"
ROCM_DOCKER_ARCH: 'gfx1151'
# 元ファイルに設定された外部イメージ(ghcr.io)を無視し、ローカルビルドした独自タグに差し替えます
image: llama-cpp-server:rocm7.14-local
以上で起動。
$ docker compose -f compose.yml -f compose.local.yml up --build -d
ひとまず今のところは問題なく動いてそう。
ベンチマーク的には、ほんのちょっと恩恵あったか?という感じ。まあ作るだけ作ったけど、あっても微差なんで普通にrocm-server-*として提供されているのを大人しく利用するのが良さそう。
$ for i in $(seq 1 10); do ./bench.sh ;done Tokens/s: 21.836889040832848 Tokens/s: 23.360779183154488 Tokens/s: 22.426950598457406 Tokens/s: 24.485984078146974 Tokens/s: 36.570018013590904 Tokens/s: 24.22252425616665 Tokens/s: 22.600159632681795 Tokens/s: 24.018720292803533 Tokens/s: 25.29314483156075 Tokens/s: 22.28074483950202
MTPの性質なのかたまに外れ値的に早い時が存在するので、それこそ平均ではなく中央値でみるのが良いのかもしれない。